Merge remote-tracking branch 'kraxel/usb.79' into staging
# By Gerd Hoffmann (7) and Hans de Goede (3) # Via Gerd Hoffmann * kraxel/usb.79: usb-tablet: Don't claim wakeup capability for USB-2 version usb: update docs for bus name change usb-hub: report status changes only once usb-hub: limit chain length xhci: zap unused name field xhci: remove unimplemented printfs xhci: remove leftover debug printf xhci: fix numintrs sanity checks usb-redir: Add flow control support usb-redir: Fix crash on migration with no client connected
This commit is contained in:
commit
2a7a239ff0
|
@ -11,7 +11,7 @@ one USB 2.0 bus driven by the EHCI controller. Devices must be
|
||||||
attached to the correct controller manually.
|
attached to the correct controller manually.
|
||||||
|
|
||||||
The '-usb' switch will make qemu create the UHCI controller as part of
|
The '-usb' switch will make qemu create the UHCI controller as part of
|
||||||
the PIIX3 chipset. The USB 1.1 bus will carry the name "usb.0".
|
the PIIX3 chipset. The USB 1.1 bus will carry the name "usb-bus.0".
|
||||||
|
|
||||||
You can use the standard -device switch to add a EHCI controller to
|
You can use the standard -device switch to add a EHCI controller to
|
||||||
your virtual machine. It is strongly recommended to specify an ID for
|
your virtual machine. It is strongly recommended to specify an ID for
|
||||||
|
@ -27,7 +27,7 @@ a complete example:
|
||||||
-drive if=none,id=usbstick,file=/path/to/image \
|
-drive if=none,id=usbstick,file=/path/to/image \
|
||||||
-usb \
|
-usb \
|
||||||
-device usb-ehci,id=ehci \
|
-device usb-ehci,id=ehci \
|
||||||
-device usb-tablet,bus=usb.0 \
|
-device usb-tablet,bus=usb-bus.0 \
|
||||||
-device usb-storage,bus=ehci.0,drive=usbstick
|
-device usb-storage,bus=ehci.0,drive=usbstick
|
||||||
|
|
||||||
This attaches a usb tablet to the UHCI adapter and a usb mass storage
|
This attaches a usb tablet to the UHCI adapter and a usb mass storage
|
||||||
|
@ -88,22 +88,22 @@ ports (1-4), the emulated (1.1) USB hub has eight ports.
|
||||||
|
|
||||||
Plugging a tablet into UHCI port 1 works like this:
|
Plugging a tablet into UHCI port 1 works like this:
|
||||||
|
|
||||||
-device usb-tablet,bus=usb.0,port=1
|
-device usb-tablet,bus=usb-bus.0,port=1
|
||||||
|
|
||||||
Plugging a hub into UHCI port 2 works like this:
|
Plugging a hub into UHCI port 2 works like this:
|
||||||
|
|
||||||
-device usb-hub,bus=usb.0,port=2
|
-device usb-hub,bus=usb-bus.0,port=2
|
||||||
|
|
||||||
Plugging a virtual usb stick into port 4 of the hub just plugged works
|
Plugging a virtual usb stick into port 4 of the hub just plugged works
|
||||||
this way:
|
this way:
|
||||||
|
|
||||||
-device usb-storage,bus=usb.0,port=2.4,drive=...
|
-device usb-storage,bus=usb-bus.0,port=2.4,drive=...
|
||||||
|
|
||||||
You can do basically the same in the monitor using the device_add
|
You can do basically the same in the monitor using the device_add
|
||||||
command. If you want to unplug devices too you should specify some
|
command. If you want to unplug devices too you should specify some
|
||||||
unique id which you can use to refer to the device ...
|
unique id which you can use to refer to the device ...
|
||||||
|
|
||||||
(qemu) device_add usb-tablet,bus=usb.0,port=1,id=my-tablet
|
(qemu) device_add usb-tablet,bus=usb-bus.0,port=1,id=my-tablet
|
||||||
(qemu) device_del my-tablet
|
(qemu) device_del my-tablet
|
||||||
|
|
||||||
... when unplugging it with device_del.
|
... when unplugging it with device_del.
|
||||||
|
@ -148,10 +148,10 @@ using for testing is bus 1 + port 1 for 2.0 devices and bus 3 + port 1
|
||||||
for 1.1 devices. Passing through any device plugged into that port
|
for 1.1 devices. Passing through any device plugged into that port
|
||||||
and also assign them to the correct bus can be done this way:
|
and also assign them to the correct bus can be done this way:
|
||||||
|
|
||||||
qemu -M pc ${otheroptions} \
|
qemu -M pc ${otheroptions} \
|
||||||
-usb \
|
-usb \
|
||||||
-device usb-ehci,id=ehci \
|
-device usb-ehci,id=ehci \
|
||||||
-device usb-host,bus=usb.0,hostbus=3,hostport=1 \
|
-device usb-host,bus=usb-bus.0,hostbus=3,hostport=1 \
|
||||||
-device usb-host,bus=ehci.0,hostbus=1,hostport=1
|
-device usb-host,bus=ehci.0,hostbus=1,hostport=1
|
||||||
|
|
||||||
enjoy,
|
enjoy,
|
||||||
|
|
1
hw/usb.h
1
hw/usb.h
|
@ -337,6 +337,7 @@ typedef struct USBPortOps {
|
||||||
struct USBPort {
|
struct USBPort {
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
int speedmask;
|
int speedmask;
|
||||||
|
int hubcount;
|
||||||
char path[16];
|
char path[16];
|
||||||
USBPortOps *ops;
|
USBPortOps *ops;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
|
|
@ -341,8 +341,10 @@ void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
|
||||||
if (upstream) {
|
if (upstream) {
|
||||||
snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
|
snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
|
||||||
upstream->path, portnr);
|
upstream->path, portnr);
|
||||||
|
downstream->hubcount = upstream->hubcount + 1;
|
||||||
} else {
|
} else {
|
||||||
snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
|
snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
|
||||||
|
downstream->hubcount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,7 @@ static const USBDescDevice desc_device_tablet2 = {
|
||||||
.bNumInterfaces = 1,
|
.bNumInterfaces = 1,
|
||||||
.bConfigurationValue = 1,
|
.bConfigurationValue = 1,
|
||||||
.iConfiguration = STR_CONFIG_TABLET,
|
.iConfiguration = STR_CONFIG_TABLET,
|
||||||
.bmAttributes = 0xa0,
|
.bmAttributes = 0x80,
|
||||||
.bMaxPower = 50,
|
.bMaxPower = 50,
|
||||||
.nif = 1,
|
.nif = 1,
|
||||||
.ifs = &desc_iface_tablet2,
|
.ifs = &desc_iface_tablet2,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "hw/usb.h"
|
#include "hw/usb.h"
|
||||||
#include "hw/usb/desc.h"
|
#include "hw/usb/desc.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
|
||||||
#define NUM_PORTS 8
|
#define NUM_PORTS 8
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ typedef struct USBHubPort {
|
||||||
USBPort port;
|
USBPort port;
|
||||||
uint16_t wPortStatus;
|
uint16_t wPortStatus;
|
||||||
uint16_t wPortChange;
|
uint16_t wPortChange;
|
||||||
|
uint16_t wPortChange_reported;
|
||||||
} USBHubPort;
|
} USBHubPort;
|
||||||
|
|
||||||
typedef struct USBHubState {
|
typedef struct USBHubState {
|
||||||
|
@ -466,8 +468,11 @@ static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
||||||
status = 0;
|
status = 0;
|
||||||
for(i = 0; i < NUM_PORTS; i++) {
|
for(i = 0; i < NUM_PORTS; i++) {
|
||||||
port = &s->ports[i];
|
port = &s->ports[i];
|
||||||
if (port->wPortChange)
|
if (port->wPortChange &&
|
||||||
|
port->wPortChange_reported != port->wPortChange) {
|
||||||
status |= (1 << (i + 1));
|
status |= (1 << (i + 1));
|
||||||
|
}
|
||||||
|
port->wPortChange_reported = port->wPortChange;
|
||||||
}
|
}
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
for(i = 0; i < n; i++) {
|
for(i = 0; i < n; i++) {
|
||||||
|
@ -514,6 +519,11 @@ static int usb_hub_initfn(USBDevice *dev)
|
||||||
USBHubPort *port;
|
USBHubPort *port;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (dev->port->hubcount == 5) {
|
||||||
|
error_report("usb hub chain too deep");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
usb_desc_create_serial(dev);
|
usb_desc_create_serial(dev);
|
||||||
usb_desc_init(dev);
|
usb_desc_init(dev);
|
||||||
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||||
|
|
|
@ -452,7 +452,6 @@ struct XHCIState {
|
||||||
MemoryRegion mem_oper;
|
MemoryRegion mem_oper;
|
||||||
MemoryRegion mem_runtime;
|
MemoryRegion mem_runtime;
|
||||||
MemoryRegion mem_doorbell;
|
MemoryRegion mem_doorbell;
|
||||||
const char *name;
|
|
||||||
unsigned int devaddr;
|
unsigned int devaddr;
|
||||||
|
|
||||||
/* properties */
|
/* properties */
|
||||||
|
@ -1172,8 +1171,6 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||||
uint32_t ctx[5];
|
uint32_t ctx[5];
|
||||||
uint32_t ctx2[2];
|
uint32_t ctx2[2];
|
||||||
|
|
||||||
fprintf(stderr, "%s: epid %d, state %d\n",
|
|
||||||
__func__, epctx->epid, state);
|
|
||||||
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
||||||
ctx[0] &= ~EP_STATE_MASK;
|
ctx[0] &= ~EP_STATE_MASK;
|
||||||
ctx[0] |= state;
|
ctx[0] |= state;
|
||||||
|
@ -2568,7 +2565,7 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci: unimplemented command %d\n", type);
|
trace_usb_xhci_unimplemented("command", type);
|
||||||
event.ccode = CC_TRB_ERROR;
|
event.ccode = CC_TRB_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2767,7 +2764,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||||
ret = 0x00000000; /* reserved */
|
ret = 0x00000000; /* reserved */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg);
|
trace_usb_xhci_unimplemented("cap read", reg);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2790,8 +2787,7 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size)
|
||||||
break;
|
break;
|
||||||
case 0x0c: /* reserved */
|
case 0x0c: /* reserved */
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
|
trace_usb_xhci_unimplemented("port read", reg);
|
||||||
port->portnr, (uint32_t)reg);
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2831,8 +2827,7 @@ static void xhci_port_write(void *ptr, hwaddr reg,
|
||||||
case 0x04: /* PORTPMSC */
|
case 0x04: /* PORTPMSC */
|
||||||
case 0x08: /* PORTLI */
|
case 0x08: /* PORTLI */
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
|
trace_usb_xhci_unimplemented("port write", reg);
|
||||||
port->portnr, (uint32_t)reg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2870,7 +2865,7 @@ static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size)
|
||||||
ret = xhci->config;
|
ret = xhci->config;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg);
|
trace_usb_xhci_unimplemented("oper read", reg);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2935,7 +2930,7 @@ static void xhci_oper_write(void *ptr, hwaddr reg,
|
||||||
xhci->config = val & 0xff;
|
xhci->config = val & 0xff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
|
trace_usb_xhci_unimplemented("oper write", reg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2951,8 +2946,7 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg,
|
||||||
ret = xhci_mfindex_get(xhci) & 0x3fff;
|
ret = xhci_mfindex_get(xhci) & 0x3fff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n",
|
trace_usb_xhci_unimplemented("runtime read", reg);
|
||||||
(int)reg);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -2996,7 +2990,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||||
trace_usb_xhci_runtime_write(reg, val);
|
trace_usb_xhci_runtime_write(reg, val);
|
||||||
|
|
||||||
if (reg < 0x20) {
|
if (reg < 0x20) {
|
||||||
fprintf(stderr, "%s: reg 0x%x unimplemented\n", __func__, (int)reg);
|
trace_usb_xhci_unimplemented("runtime write", reg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3038,8 +3032,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||||
xhci_events_update(xhci, v);
|
xhci_events_update(xhci, v);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n",
|
trace_usb_xhci_unimplemented("oper write", reg);
|
||||||
(int)reg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3290,6 +3283,9 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
||||||
if (xhci->numintrs > MAXINTRS) {
|
if (xhci->numintrs > MAXINTRS) {
|
||||||
xhci->numintrs = MAXINTRS;
|
xhci->numintrs = MAXINTRS;
|
||||||
}
|
}
|
||||||
|
while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */
|
||||||
|
xhci->numintrs++;
|
||||||
|
}
|
||||||
if (xhci->numintrs < 1) {
|
if (xhci->numintrs < 1) {
|
||||||
xhci->numintrs = 1;
|
xhci->numintrs = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,8 @@ struct USBRedirDevice {
|
||||||
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
|
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
|
||||||
const uint8_t *read_buf;
|
const uint8_t *read_buf;
|
||||||
int read_buf_size;
|
int read_buf_size;
|
||||||
|
/* Active chardev-watch-tag */
|
||||||
|
guint watch;
|
||||||
/* For async handling of close */
|
/* For async handling of close */
|
||||||
QEMUBH *chardev_close_bh;
|
QEMUBH *chardev_close_bh;
|
||||||
/* To delay the usb attach in case of quick chardev close + open */
|
/* To delay the usb attach in case of quick chardev close + open */
|
||||||
|
@ -254,9 +256,21 @@ static int usbredir_read(void *priv, uint8_t *data, int count)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
USBRedirDevice *dev = opaque;
|
||||||
|
|
||||||
|
dev->watch = 0;
|
||||||
|
usbredirparser_do_write(dev->parser);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static int usbredir_write(void *priv, uint8_t *data, int count)
|
static int usbredir_write(void *priv, uint8_t *data, int count)
|
||||||
{
|
{
|
||||||
USBRedirDevice *dev = priv;
|
USBRedirDevice *dev = priv;
|
||||||
|
int r;
|
||||||
|
|
||||||
if (!dev->cs->be_open) {
|
if (!dev->cs->be_open) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -267,7 +281,17 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return qemu_chr_fe_write(dev->cs, data, count);
|
r = qemu_chr_fe_write(dev->cs, data, count);
|
||||||
|
if (r < count) {
|
||||||
|
if (!dev->watch) {
|
||||||
|
dev->watch = qemu_chr_fe_add_watch(dev->cs, G_IO_OUT,
|
||||||
|
usbredir_write_unblocked, dev);
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1085,6 +1109,10 @@ static void usbredir_chardev_close_bh(void *opaque)
|
||||||
usbredirparser_destroy(dev->parser);
|
usbredirparser_destroy(dev->parser);
|
||||||
dev->parser = NULL;
|
dev->parser = NULL;
|
||||||
}
|
}
|
||||||
|
if (dev->watch) {
|
||||||
|
g_source_remove(dev->watch);
|
||||||
|
dev->watch = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usbredir_create_parser(USBRedirDevice *dev)
|
static void usbredir_create_parser(USBRedirDevice *dev)
|
||||||
|
@ -1317,6 +1345,9 @@ static void usbredir_handle_destroy(USBDevice *udev)
|
||||||
if (dev->parser) {
|
if (dev->parser) {
|
||||||
usbredirparser_destroy(dev->parser);
|
usbredirparser_destroy(dev->parser);
|
||||||
}
|
}
|
||||||
|
if (dev->watch) {
|
||||||
|
g_source_remove(dev->watch);
|
||||||
|
}
|
||||||
|
|
||||||
free(dev->filter_rules);
|
free(dev->filter_rules);
|
||||||
}
|
}
|
||||||
|
@ -1973,6 +2004,10 @@ static int usbredir_post_load(void *priv, int version_id)
|
||||||
{
|
{
|
||||||
USBRedirDevice *dev = priv;
|
USBRedirDevice *dev = priv;
|
||||||
|
|
||||||
|
if (dev->parser == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
switch (dev->device_info.speed) {
|
switch (dev->device_info.speed) {
|
||||||
case usb_redir_speed_low:
|
case usb_redir_speed_low:
|
||||||
dev->dev.speed = USB_SPEED_LOW;
|
dev->dev.speed = USB_SPEED_LOW;
|
||||||
|
|
|
@ -380,6 +380,7 @@ usb_xhci_xfer_nak(void *xfer) "%p"
|
||||||
usb_xhci_xfer_retry(void *xfer) "%p"
|
usb_xhci_xfer_retry(void *xfer) "%p"
|
||||||
usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d"
|
usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d"
|
||||||
usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d"
|
usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d"
|
||||||
|
usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)"
|
||||||
|
|
||||||
# hw/usb/desc.c
|
# hw/usb/desc.c
|
||||||
usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
|
usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
|
||||||
|
|
Loading…
Reference in New Issue