mirror of https://github.com/xemu-project/xemu.git
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:
commit
e0a1e32dbc
|
@ -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
hw/usb.h
9
hw/usb.h
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
174
hw/usb/desc.c
174
hw/usb/desc.c
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -217,7 +217,7 @@ static const USBDescIface desc_iface[] = {
|
|||
};
|
||||
|
||||
static const USBDescDevice desc_device = {
|
||||
.bcdUSB = 0x0200,
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
|
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
1436
hw/usb/hcd-xhci.c
1436
hw/usb/hcd-xhci.c
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* USB redirector usb-guest
|
||||
*
|
||||
* Copyright (c) 2011 Red Hat, Inc.
|
||||
* Copyright (c) 2011-2012 Red Hat, Inc.
|
||||
*
|
||||
* Red Hat Authors:
|
||||
* Hans de Goede <hdegoede@redhat.com>
|
||||
|
@ -43,7 +43,7 @@
|
|||
#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
|
||||
#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
|
||||
|
||||
typedef struct AsyncURB AsyncURB;
|
||||
typedef struct Cancelled Cancelled;
|
||||
typedef struct USBRedirDevice USBRedirDevice;
|
||||
|
||||
/* Struct to hold buffered packets (iso or int input packets) */
|
||||
|
@ -79,15 +79,14 @@ struct USBRedirDevice {
|
|||
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
|
||||
const uint8_t *read_buf;
|
||||
int read_buf_size;
|
||||
/* For async handling of open/close */
|
||||
QEMUBH *open_close_bh;
|
||||
/* For async handling of close */
|
||||
QEMUBH *chardev_close_bh;
|
||||
/* To delay the usb attach in case of quick chardev close + open */
|
||||
QEMUTimer *attach_timer;
|
||||
int64_t next_attach_time;
|
||||
struct usbredirparser *parser;
|
||||
struct endp_data endpoint[MAX_ENDPOINTS];
|
||||
uint32_t packet_id;
|
||||
QTAILQ_HEAD(, AsyncURB) asyncq;
|
||||
QTAILQ_HEAD(, Cancelled) cancelled;
|
||||
/* Data for device filtering */
|
||||
struct usb_redir_device_connect_header device_info;
|
||||
struct usb_redir_interface_info_header interface_info;
|
||||
|
@ -95,17 +94,9 @@ struct USBRedirDevice {
|
|||
int filter_rules_count;
|
||||
};
|
||||
|
||||
struct AsyncURB {
|
||||
USBRedirDevice *dev;
|
||||
USBPacket *packet;
|
||||
uint32_t packet_id;
|
||||
int get;
|
||||
union {
|
||||
struct usb_redir_control_packet_header control_packet;
|
||||
struct usb_redir_bulk_packet_header bulk_packet;
|
||||
struct usb_redir_interrupt_packet_header interrupt_packet;
|
||||
};
|
||||
QTAILQ_ENTRY(AsyncURB)next;
|
||||
struct Cancelled {
|
||||
uint64_t id;
|
||||
QTAILQ_ENTRY(Cancelled)next;
|
||||
};
|
||||
|
||||
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
|
||||
|
@ -116,27 +107,27 @@ static void usbredir_interface_info(void *priv,
|
|||
struct usb_redir_interface_info_header *interface_info);
|
||||
static void usbredir_ep_info(void *priv,
|
||||
struct usb_redir_ep_info_header *ep_info);
|
||||
static void usbredir_configuration_status(void *priv, uint32_t id,
|
||||
static void usbredir_configuration_status(void *priv, uint64_t id,
|
||||
struct usb_redir_configuration_status_header *configuration_status);
|
||||
static void usbredir_alt_setting_status(void *priv, uint32_t id,
|
||||
static void usbredir_alt_setting_status(void *priv, uint64_t id,
|
||||
struct usb_redir_alt_setting_status_header *alt_setting_status);
|
||||
static void usbredir_iso_stream_status(void *priv, uint32_t id,
|
||||
static void usbredir_iso_stream_status(void *priv, uint64_t id,
|
||||
struct usb_redir_iso_stream_status_header *iso_stream_status);
|
||||
static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
|
||||
static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
|
||||
struct usb_redir_interrupt_receiving_status_header
|
||||
*interrupt_receiving_status);
|
||||
static void usbredir_bulk_streams_status(void *priv, uint32_t id,
|
||||
static void usbredir_bulk_streams_status(void *priv, uint64_t id,
|
||||
struct usb_redir_bulk_streams_status_header *bulk_streams_status);
|
||||
static void usbredir_control_packet(void *priv, uint32_t id,
|
||||
static void usbredir_control_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_control_packet_header *control_packet,
|
||||
uint8_t *data, int data_len);
|
||||
static void usbredir_bulk_packet(void *priv, uint32_t id,
|
||||
static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_bulk_packet_header *bulk_packet,
|
||||
uint8_t *data, int data_len);
|
||||
static void usbredir_iso_packet(void *priv, uint32_t id,
|
||||
static void usbredir_iso_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_iso_packet_header *iso_packet,
|
||||
uint8_t *data, int data_len);
|
||||
static void usbredir_interrupt_packet(void *priv, uint32_t id,
|
||||
static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_interrupt_packet_header *interrupt_header,
|
||||
uint8_t *data, int data_len);
|
||||
|
||||
|
@ -245,58 +236,58 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
|
|||
}
|
||||
|
||||
/*
|
||||
* Async and buffered packets helpers
|
||||
* Cancelled and buffered packets helpers
|
||||
*/
|
||||
|
||||
static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
|
||||
{
|
||||
AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
|
||||
aurb->dev = dev;
|
||||
aurb->packet = p;
|
||||
aurb->packet_id = dev->packet_id;
|
||||
QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
|
||||
dev->packet_id++;
|
||||
|
||||
return aurb;
|
||||
}
|
||||
|
||||
static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
|
||||
{
|
||||
QTAILQ_REMOVE(&dev->asyncq, aurb, next);
|
||||
g_free(aurb);
|
||||
}
|
||||
|
||||
static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
|
||||
{
|
||||
AsyncURB *aurb;
|
||||
|
||||
QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
|
||||
if (aurb->packet_id == packet_id) {
|
||||
return aurb;
|
||||
}
|
||||
}
|
||||
DPRINTF("could not find async urb for packet_id %u\n", packet_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
|
||||
{
|
||||
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
|
||||
AsyncURB *aurb;
|
||||
Cancelled *c;
|
||||
|
||||
QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
|
||||
if (p != aurb->packet) {
|
||||
continue;
|
||||
}
|
||||
DPRINTF("cancel packet id %"PRIu64"\n", p->id);
|
||||
|
||||
DPRINTF("async cancel id %u\n", aurb->packet_id);
|
||||
usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
c = g_malloc0(sizeof(Cancelled));
|
||||
c->id = p->id;
|
||||
QTAILQ_INSERT_TAIL(&dev->cancelled, c, next);
|
||||
|
||||
/* Mark it as dead */
|
||||
aurb->packet = NULL;
|
||||
break;
|
||||
usbredirparser_send_cancel_data_packet(dev->parser, p->id);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
}
|
||||
|
||||
static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
|
||||
{
|
||||
Cancelled *c;
|
||||
|
||||
if (!dev->dev.attached) {
|
||||
return 1; /* Treat everything as cancelled after a disconnect */
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(c, &dev->cancelled, next) {
|
||||
if (c->id == id) {
|
||||
QTAILQ_REMOVE(&dev->cancelled, c, next);
|
||||
g_free(c);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
|
||||
uint8_t ep, uint64_t id)
|
||||
{
|
||||
USBPacket *p;
|
||||
|
||||
if (usbredir_is_cancelled(dev, id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = usb_ep_find_packet_by_id(&dev->dev,
|
||||
(ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT,
|
||||
ep & 0x0f, id);
|
||||
if (p == NULL) {
|
||||
ERROR("could not find packet with id %"PRIu64"\n", id);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void bufp_alloc(USBRedirDevice *dev,
|
||||
|
@ -492,25 +483,22 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
|
|||
static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
uint8_t ep)
|
||||
{
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
struct usb_redir_bulk_packet_header bulk_packet;
|
||||
|
||||
DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
|
||||
p->iov.size, aurb->packet_id);
|
||||
DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
|
||||
|
||||
bulk_packet.endpoint = ep;
|
||||
bulk_packet.length = p->iov.size;
|
||||
bulk_packet.stream_id = 0;
|
||||
aurb->bulk_packet = bulk_packet;
|
||||
|
||||
if (ep & USB_DIR_IN) {
|
||||
usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
|
||||
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
||||
&bulk_packet, NULL, 0);
|
||||
} else {
|
||||
uint8_t buf[p->iov.size];
|
||||
usb_packet_copy(p, buf, p->iov.size);
|
||||
usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
|
||||
usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
|
||||
usbredirparser_send_bulk_packet(dev->parser, p->id,
|
||||
&bulk_packet, buf, p->iov.size);
|
||||
}
|
||||
usbredirparser_do_write(dev->parser);
|
||||
|
@ -573,20 +561,18 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
|
|||
return len;
|
||||
} else {
|
||||
/* Output interrupt endpoint, normal async operation */
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
struct usb_redir_interrupt_packet_header interrupt_packet;
|
||||
uint8_t buf[p->iov.size];
|
||||
|
||||
DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
|
||||
aurb->packet_id);
|
||||
DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep,
|
||||
p->iov.size, p->id);
|
||||
|
||||
interrupt_packet.endpoint = ep;
|
||||
interrupt_packet.length = p->iov.size;
|
||||
aurb->interrupt_packet = interrupt_packet;
|
||||
|
||||
usb_packet_copy(p, buf, p->iov.size);
|
||||
usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
|
||||
usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
|
||||
usbredirparser_send_interrupt_packet(dev->parser, p->id,
|
||||
&interrupt_packet, buf, p->iov.size);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
|
@ -640,10 +626,9 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
|
|||
int config)
|
||||
{
|
||||
struct usb_redir_set_configuration_header set_config;
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
int i;
|
||||
|
||||
DPRINTF("set config %d id %u\n", config, aurb->packet_id);
|
||||
DPRINTF("set config %d id %"PRIu64"\n", config, p->id);
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
switch (dev->endpoint[i].type) {
|
||||
|
@ -660,20 +645,16 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
|
|||
}
|
||||
|
||||
set_config.configuration = config;
|
||||
usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
|
||||
&set_config);
|
||||
usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
|
||||
{
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
DPRINTF("get config id %"PRIu64"\n", p->id);
|
||||
|
||||
DPRINTF("get config id %u\n", aurb->packet_id);
|
||||
|
||||
aurb->get = 1;
|
||||
usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
|
||||
usbredirparser_send_get_configuration(dev->parser, p->id);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
@ -682,11 +663,9 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
|
|||
int interface, int alt)
|
||||
{
|
||||
struct usb_redir_set_alt_setting_header set_alt;
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
int i;
|
||||
|
||||
DPRINTF("set interface %d alt %d id %u\n", interface, alt,
|
||||
aurb->packet_id);
|
||||
DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id);
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
if (dev->endpoint[i].interface == interface) {
|
||||
|
@ -706,8 +685,7 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
|
|||
|
||||
set_alt.interface = interface;
|
||||
set_alt.alt = alt;
|
||||
usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
|
||||
&set_alt);
|
||||
usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
@ -716,14 +694,11 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
|
|||
int interface)
|
||||
{
|
||||
struct usb_redir_get_alt_setting_header get_alt;
|
||||
AsyncURB *aurb = async_alloc(dev, p);
|
||||
|
||||
DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
|
||||
DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id);
|
||||
|
||||
get_alt.interface = interface;
|
||||
aurb->get = 1;
|
||||
usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
|
||||
&get_alt);
|
||||
usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
|
@ -733,7 +708,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
|||
{
|
||||
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
|
||||
struct usb_redir_control_packet_header control_packet;
|
||||
AsyncURB *aurb;
|
||||
|
||||
/* Special cases for certain standard device requests */
|
||||
switch (request) {
|
||||
|
@ -751,13 +725,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
|||
return usbredir_get_interface(dev, p, index);
|
||||
}
|
||||
|
||||
/* "Normal" ctrl requests */
|
||||
aurb = async_alloc(dev, p);
|
||||
|
||||
/* Note request is (bRequestType << 8) | bRequest */
|
||||
DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
|
||||
request >> 8, request & 0xff, value, index, length,
|
||||
aurb->packet_id);
|
||||
/* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */
|
||||
DPRINTF(
|
||||
"ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n",
|
||||
request >> 8, request & 0xff, value, index, length, p->id);
|
||||
|
||||
control_packet.request = request & 0xFF;
|
||||
control_packet.requesttype = request >> 8;
|
||||
|
@ -765,14 +736,13 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
|||
control_packet.value = value;
|
||||
control_packet.index = index;
|
||||
control_packet.length = length;
|
||||
aurb->control_packet = control_packet;
|
||||
|
||||
if (control_packet.requesttype & USB_DIR_IN) {
|
||||
usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
|
||||
usbredirparser_send_control_packet(dev->parser, p->id,
|
||||
&control_packet, NULL, 0);
|
||||
} else {
|
||||
usbredir_log_data(dev, "ctrl data out:", data, length);
|
||||
usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
|
||||
usbredirparser_send_control_packet(dev->parser, p->id,
|
||||
&control_packet, data, length);
|
||||
}
|
||||
usbredirparser_do_write(dev->parser);
|
||||
|
@ -784,18 +754,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
|||
* from within the USBDevice data / control packet callbacks and doing a
|
||||
* usb_detach from within these callbacks is not a good idea.
|
||||
*
|
||||
* So we use a bh handler to take care of close events. We also handle
|
||||
* open events from this callback to make sure that a close directly followed
|
||||
* by an open gets handled in the right order.
|
||||
* So we use a bh handler to take care of close events.
|
||||
*/
|
||||
static void usbredir_open_close_bh(void *opaque)
|
||||
static void usbredir_chardev_close_bh(void *opaque)
|
||||
{
|
||||
USBRedirDevice *dev = opaque;
|
||||
uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
|
||||
char version[32];
|
||||
|
||||
strcpy(version, "qemu usb-redir guest ");
|
||||
pstrcat(version, sizeof(version), qemu_get_version());
|
||||
|
||||
usbredir_device_disconnect(dev);
|
||||
|
||||
|
@ -803,34 +766,56 @@ static void usbredir_open_close_bh(void *opaque)
|
|||
usbredirparser_destroy(dev->parser);
|
||||
dev->parser = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->cs->opened) {
|
||||
dev->parser = qemu_oom_check(usbredirparser_create());
|
||||
dev->parser->priv = dev;
|
||||
dev->parser->log_func = usbredir_log;
|
||||
dev->parser->read_func = usbredir_read;
|
||||
dev->parser->write_func = usbredir_write;
|
||||
dev->parser->hello_func = usbredir_hello;
|
||||
dev->parser->device_connect_func = usbredir_device_connect;
|
||||
dev->parser->device_disconnect_func = usbredir_device_disconnect;
|
||||
dev->parser->interface_info_func = usbredir_interface_info;
|
||||
dev->parser->ep_info_func = usbredir_ep_info;
|
||||
dev->parser->configuration_status_func = usbredir_configuration_status;
|
||||
dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
|
||||
dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
|
||||
dev->parser->interrupt_receiving_status_func =
|
||||
usbredir_interrupt_receiving_status;
|
||||
dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
|
||||
dev->parser->control_packet_func = usbredir_control_packet;
|
||||
dev->parser->bulk_packet_func = usbredir_bulk_packet;
|
||||
dev->parser->iso_packet_func = usbredir_iso_packet;
|
||||
dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
|
||||
dev->read_buf = NULL;
|
||||
dev->read_buf_size = 0;
|
||||
static void usbredir_chardev_open(USBRedirDevice *dev)
|
||||
{
|
||||
uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
|
||||
char version[32];
|
||||
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
|
||||
usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
|
||||
/* Make sure any pending closes are handled (no-op if none pending) */
|
||||
usbredir_chardev_close_bh(dev);
|
||||
qemu_bh_cancel(dev->chardev_close_bh);
|
||||
|
||||
strcpy(version, "qemu usb-redir guest ");
|
||||
pstrcat(version, sizeof(version), qemu_get_version());
|
||||
|
||||
dev->parser = qemu_oom_check(usbredirparser_create());
|
||||
dev->parser->priv = dev;
|
||||
dev->parser->log_func = usbredir_log;
|
||||
dev->parser->read_func = usbredir_read;
|
||||
dev->parser->write_func = usbredir_write;
|
||||
dev->parser->hello_func = usbredir_hello;
|
||||
dev->parser->device_connect_func = usbredir_device_connect;
|
||||
dev->parser->device_disconnect_func = usbredir_device_disconnect;
|
||||
dev->parser->interface_info_func = usbredir_interface_info;
|
||||
dev->parser->ep_info_func = usbredir_ep_info;
|
||||
dev->parser->configuration_status_func = usbredir_configuration_status;
|
||||
dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
|
||||
dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
|
||||
dev->parser->interrupt_receiving_status_func =
|
||||
usbredir_interrupt_receiving_status;
|
||||
dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
|
||||
dev->parser->control_packet_func = usbredir_control_packet;
|
||||
dev->parser->bulk_packet_func = usbredir_bulk_packet;
|
||||
dev->parser->iso_packet_func = usbredir_iso_packet;
|
||||
dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
|
||||
dev->read_buf = NULL;
|
||||
dev->read_buf_size = 0;
|
||||
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
|
||||
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
|
||||
usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
}
|
||||
|
||||
static void usbredir_reject_device(USBRedirDevice *dev)
|
||||
{
|
||||
usbredir_device_disconnect(dev);
|
||||
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
|
||||
usbredirparser_send_filter_reject(dev->parser);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
}
|
||||
}
|
||||
|
@ -839,12 +824,19 @@ static void usbredir_do_attach(void *opaque)
|
|||
{
|
||||
USBRedirDevice *dev = opaque;
|
||||
|
||||
/* In order to work properly with XHCI controllers we need these caps */
|
||||
if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !(
|
||||
usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_ep_info_max_packet_size) &&
|
||||
usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_64bits_ids))) {
|
||||
ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n");
|
||||
usbredir_reject_device(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usb_device_attach(&dev->dev) != 0) {
|
||||
usbredir_device_disconnect(dev);
|
||||
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
|
||||
usbredirparser_send_filter_reject(dev->parser);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
}
|
||||
usbredir_reject_device(dev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -856,13 +848,13 @@ static int usbredir_chardev_can_read(void *opaque)
|
|||
{
|
||||
USBRedirDevice *dev = opaque;
|
||||
|
||||
if (dev->parser) {
|
||||
/* usbredir_parser_do_read will consume *all* data we give it */
|
||||
return 1024 * 1024;
|
||||
} else {
|
||||
/* usbredir_open_close_bh hasn't handled the open event yet */
|
||||
if (!dev->parser) {
|
||||
WARNING("chardev_can_read called on non open chardev!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* usbredir_parser_do_read will consume *all* data we give it */
|
||||
return 1024 * 1024;
|
||||
}
|
||||
|
||||
static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
|
||||
|
@ -886,8 +878,10 @@ static void usbredir_chardev_event(void *opaque, int event)
|
|||
|
||||
switch (event) {
|
||||
case CHR_EVENT_OPENED:
|
||||
usbredir_chardev_open(dev);
|
||||
break;
|
||||
case CHR_EVENT_CLOSED:
|
||||
qemu_bh_schedule(dev->open_close_bh);
|
||||
qemu_bh_schedule(dev->chardev_close_bh);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -917,10 +911,10 @@ static int usbredir_initfn(USBDevice *udev)
|
|||
}
|
||||
}
|
||||
|
||||
dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
|
||||
dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
|
||||
dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
|
||||
|
||||
QTAILQ_INIT(&dev->asyncq);
|
||||
QTAILQ_INIT(&dev->cancelled);
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
QTAILQ_INIT(&dev->endpoint[i].bufpq);
|
||||
}
|
||||
|
@ -939,11 +933,12 @@ static int usbredir_initfn(USBDevice *udev)
|
|||
|
||||
static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
|
||||
{
|
||||
AsyncURB *aurb, *next_aurb;
|
||||
Cancelled *c, *next_c;
|
||||
int i;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
|
||||
async_free(dev, aurb);
|
||||
QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) {
|
||||
QTAILQ_REMOVE(&dev->cancelled, c, next);
|
||||
g_free(c);
|
||||
}
|
||||
for (i = 0; i < MAX_ENDPOINTS; i++) {
|
||||
usbredir_free_bufpq(dev, I2EP(i));
|
||||
|
@ -957,7 +952,7 @@ static void usbredir_handle_destroy(USBDevice *udev)
|
|||
qemu_chr_fe_close(dev->cs);
|
||||
qemu_chr_delete(dev->cs);
|
||||
/* Note must be done after qemu_chr_close, as that causes a close event */
|
||||
qemu_bh_delete(dev->open_close_bh);
|
||||
qemu_bh_delete(dev->chardev_close_bh);
|
||||
|
||||
qemu_del_timer(dev->attach_timer);
|
||||
qemu_free_timer(dev->attach_timer);
|
||||
|
@ -1007,11 +1002,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
|
|||
return 0;
|
||||
|
||||
error:
|
||||
usbredir_device_disconnect(dev);
|
||||
if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
|
||||
usbredirparser_send_filter_reject(dev->parser);
|
||||
usbredirparser_do_write(dev->parser);
|
||||
}
|
||||
usbredir_reject_device(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1028,11 +1019,14 @@ static int usbredir_handle_status(USBRedirDevice *dev,
|
|||
case usb_redir_stall:
|
||||
return USB_RET_STALL;
|
||||
case usb_redir_cancelled:
|
||||
WARNING("returning cancelled packet to HC?\n");
|
||||
return USB_RET_NAK;
|
||||
/*
|
||||
* When the usbredir-host unredirects a device, it will report a status
|
||||
* of cancelled for all pending packets, followed by a disconnect msg.
|
||||
*/
|
||||
return USB_RET_IOERROR;
|
||||
case usb_redir_inval:
|
||||
WARNING("got invalid param error from usb-host?\n");
|
||||
return USB_RET_NAK;
|
||||
return USB_RET_IOERROR;
|
||||
case usb_redir_babble:
|
||||
return USB_RET_BABBLE;
|
||||
case usb_redir_ioerror:
|
||||
|
@ -1199,70 +1193,67 @@ static void usbredir_ep_info(void *priv,
|
|||
i & 0x0f);
|
||||
usb_ep->type = dev->endpoint[i].type;
|
||||
usb_ep->ifnum = dev->endpoint[i].interface;
|
||||
if (usbredirparser_peer_has_cap(dev->parser,
|
||||
usb_redir_cap_ep_info_max_packet_size)) {
|
||||
usb_ep->max_packet_size = ep_info->max_packet_size[i];
|
||||
}
|
||||
if (ep_info->type[i] == usb_redir_type_bulk) {
|
||||
usb_ep->pipeline = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_configuration_status(void *priv, uint32_t id,
|
||||
static void usbredir_configuration_status(void *priv, uint64_t id,
|
||||
struct usb_redir_configuration_status_header *config_status)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
AsyncURB *aurb;
|
||||
USBPacket *p;
|
||||
int len = 0;
|
||||
|
||||
DPRINTF("set config status %d config %d id %u\n", config_status->status,
|
||||
config_status->configuration, id);
|
||||
DPRINTF("set config status %d config %d id %"PRIu64"\n",
|
||||
config_status->status, config_status->configuration, id);
|
||||
|
||||
aurb = async_find(dev, id);
|
||||
if (!aurb) {
|
||||
return;
|
||||
}
|
||||
if (aurb->packet) {
|
||||
if (aurb->get) {
|
||||
p = usbredir_find_packet_by_id(dev, 0, id);
|
||||
if (p) {
|
||||
if (dev->dev.setup_buf[0] & USB_DIR_IN) {
|
||||
dev->dev.data_buf[0] = config_status->configuration;
|
||||
len = 1;
|
||||
}
|
||||
aurb->packet->result =
|
||||
usbredir_handle_status(dev, config_status->status, len);
|
||||
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
|
||||
p->result = usbredir_handle_status(dev, config_status->status, len);
|
||||
usb_generic_async_ctrl_complete(&dev->dev, p);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
}
|
||||
|
||||
static void usbredir_alt_setting_status(void *priv, uint32_t id,
|
||||
static void usbredir_alt_setting_status(void *priv, uint64_t id,
|
||||
struct usb_redir_alt_setting_status_header *alt_setting_status)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
AsyncURB *aurb;
|
||||
USBPacket *p;
|
||||
int len = 0;
|
||||
|
||||
DPRINTF("alt status %d intf %d alt %d id: %u\n",
|
||||
alt_setting_status->status,
|
||||
alt_setting_status->interface,
|
||||
DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
|
||||
alt_setting_status->status, alt_setting_status->interface,
|
||||
alt_setting_status->alt, id);
|
||||
|
||||
aurb = async_find(dev, id);
|
||||
if (!aurb) {
|
||||
return;
|
||||
}
|
||||
if (aurb->packet) {
|
||||
if (aurb->get) {
|
||||
p = usbredir_find_packet_by_id(dev, 0, id);
|
||||
if (p) {
|
||||
if (dev->dev.setup_buf[0] & USB_DIR_IN) {
|
||||
dev->dev.data_buf[0] = alt_setting_status->alt;
|
||||
len = 1;
|
||||
}
|
||||
aurb->packet->result =
|
||||
p->result =
|
||||
usbredir_handle_status(dev, alt_setting_status->status, len);
|
||||
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
|
||||
usb_generic_async_ctrl_complete(&dev->dev, p);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
}
|
||||
|
||||
static void usbredir_iso_stream_status(void *priv, uint32_t id,
|
||||
static void usbredir_iso_stream_status(void *priv, uint64_t id,
|
||||
struct usb_redir_iso_stream_status_header *iso_stream_status)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
uint8_t ep = iso_stream_status->endpoint;
|
||||
|
||||
DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
|
||||
DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status,
|
||||
ep, id);
|
||||
|
||||
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
|
||||
|
@ -1276,14 +1267,14 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id,
|
|||
}
|
||||
}
|
||||
|
||||
static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
|
||||
static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
|
||||
struct usb_redir_interrupt_receiving_status_header
|
||||
*interrupt_receiving_status)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
uint8_t ep = interrupt_receiving_status->endpoint;
|
||||
|
||||
DPRINTF("interrupt recv status %d ep %02X id %u\n",
|
||||
DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n",
|
||||
interrupt_receiving_status->status, ep, id);
|
||||
|
||||
if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
|
||||
|
@ -1298,37 +1289,24 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
|
|||
}
|
||||
}
|
||||
|
||||
static void usbredir_bulk_streams_status(void *priv, uint32_t id,
|
||||
static void usbredir_bulk_streams_status(void *priv, uint64_t id,
|
||||
struct usb_redir_bulk_streams_status_header *bulk_streams_status)
|
||||
{
|
||||
}
|
||||
|
||||
static void usbredir_control_packet(void *priv, uint32_t id,
|
||||
static void usbredir_control_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_control_packet_header *control_packet,
|
||||
uint8_t *data, int data_len)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
USBPacket *p;
|
||||
int len = control_packet->length;
|
||||
AsyncURB *aurb;
|
||||
|
||||
DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
|
||||
DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status,
|
||||
len, id);
|
||||
|
||||
aurb = async_find(dev, id);
|
||||
if (!aurb) {
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
aurb->control_packet.status = control_packet->status;
|
||||
aurb->control_packet.length = control_packet->length;
|
||||
if (memcmp(&aurb->control_packet, control_packet,
|
||||
sizeof(*control_packet))) {
|
||||
ERROR("return control packet mismatch, please report this!\n");
|
||||
len = USB_RET_NAK;
|
||||
}
|
||||
|
||||
if (aurb->packet) {
|
||||
p = usbredir_find_packet_by_id(dev, 0, id);
|
||||
if (p) {
|
||||
len = usbredir_handle_status(dev, control_packet->status, len);
|
||||
if (len > 0) {
|
||||
usbredir_log_data(dev, "ctrl data in:", data, data_len);
|
||||
|
@ -1340,65 +1318,52 @@ static void usbredir_control_packet(void *priv, uint32_t id,
|
|||
len = USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
aurb->packet->result = len;
|
||||
usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
|
||||
p->result = len;
|
||||
usb_generic_async_ctrl_complete(&dev->dev, p);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void usbredir_bulk_packet(void *priv, uint32_t id,
|
||||
static void usbredir_bulk_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_bulk_packet_header *bulk_packet,
|
||||
uint8_t *data, int data_len)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
uint8_t ep = bulk_packet->endpoint;
|
||||
int len = bulk_packet->length;
|
||||
AsyncURB *aurb;
|
||||
USBPacket *p;
|
||||
|
||||
DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
|
||||
ep, len, id);
|
||||
DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
|
||||
bulk_packet->status, ep, len, id);
|
||||
|
||||
aurb = async_find(dev, id);
|
||||
if (!aurb) {
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
|
||||
aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
|
||||
ERROR("return bulk packet mismatch, please report this!\n");
|
||||
len = USB_RET_NAK;
|
||||
}
|
||||
|
||||
if (aurb->packet) {
|
||||
p = usbredir_find_packet_by_id(dev, ep, id);
|
||||
if (p) {
|
||||
len = usbredir_handle_status(dev, bulk_packet->status, len);
|
||||
if (len > 0) {
|
||||
usbredir_log_data(dev, "bulk data in:", data, data_len);
|
||||
if (data_len <= aurb->packet->iov.size) {
|
||||
usb_packet_copy(aurb->packet, data, data_len);
|
||||
if (data_len <= p->iov.size) {
|
||||
usb_packet_copy(p, data, data_len);
|
||||
} else {
|
||||
ERROR("bulk buffer too small (%d > %zd)\n", data_len,
|
||||
aurb->packet->iov.size);
|
||||
len = USB_RET_STALL;
|
||||
ERROR("bulk got more data then requested (%d > %zd)\n",
|
||||
data_len, p->iov.size);
|
||||
len = USB_RET_BABBLE;
|
||||
}
|
||||
}
|
||||
aurb->packet->result = len;
|
||||
usb_packet_complete(&dev->dev, aurb->packet);
|
||||
p->result = len;
|
||||
usb_packet_complete(&dev->dev, p);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void usbredir_iso_packet(void *priv, uint32_t id,
|
||||
static void usbredir_iso_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_iso_packet_header *iso_packet,
|
||||
uint8_t *data, int data_len)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
uint8_t ep = iso_packet->endpoint;
|
||||
|
||||
DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
|
||||
data_len, id);
|
||||
DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n",
|
||||
iso_packet->status, ep, data_len, id);
|
||||
|
||||
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
|
||||
ERROR("received iso packet for non iso endpoint %02X\n", ep);
|
||||
|
@ -1416,14 +1381,14 @@ static void usbredir_iso_packet(void *priv, uint32_t id,
|
|||
bufp_alloc(dev, data, data_len, iso_packet->status, ep);
|
||||
}
|
||||
|
||||
static void usbredir_interrupt_packet(void *priv, uint32_t id,
|
||||
static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
||||
struct usb_redir_interrupt_packet_header *interrupt_packet,
|
||||
uint8_t *data, int data_len)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
uint8_t ep = interrupt_packet->endpoint;
|
||||
|
||||
DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
|
||||
DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n",
|
||||
interrupt_packet->status, ep, data_len, id);
|
||||
|
||||
if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
|
||||
|
@ -1444,22 +1409,12 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
|
|||
} else {
|
||||
int len = interrupt_packet->length;
|
||||
|
||||
AsyncURB *aurb = async_find(dev, id);
|
||||
if (!aurb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
|
||||
ERROR("return int packet mismatch, please report this!\n");
|
||||
len = USB_RET_NAK;
|
||||
}
|
||||
|
||||
if (aurb->packet) {
|
||||
aurb->packet->result = usbredir_handle_status(dev,
|
||||
USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
|
||||
if (p) {
|
||||
p->result = usbredir_handle_status(dev,
|
||||
interrupt_packet->status, len);
|
||||
usb_packet_complete(&dev->dev, aurb->packet);
|
||||
usb_packet_complete(&dev->dev, p);
|
||||
}
|
||||
async_free(dev, aurb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
trace-events
12
trace-events
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue