mirror of
https://github.com/qemu/qemu.git
synced 2024-12-02 08:13:34 +08:00
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.
|
||||
|
||||
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
|
||||
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 \
|
||||
-usb \
|
||||
-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
|
||||
|
||||
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:
|
||||
|
||||
-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:
|
||||
|
||||
-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
|
||||
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
|
||||
command. If you want to unplug devices too you should specify some
|
||||
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
|
||||
|
||||
... 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
|
||||
and also assign them to the correct bus can be done this way:
|
||||
|
||||
qemu -M pc ${otheroptions} \
|
||||
-usb \
|
||||
-device usb-ehci,id=ehci \
|
||||
-device usb-host,bus=usb.0,hostbus=3,hostport=1 \
|
||||
qemu -M pc ${otheroptions} \
|
||||
-usb \
|
||||
-device usb-ehci,id=ehci \
|
||||
-device usb-host,bus=usb-bus.0,hostbus=3,hostport=1 \
|
||||
-device usb-host,bus=ehci.0,hostbus=1,hostport=1
|
||||
|
||||
enjoy,
|
||||
|
1
hw/usb.h
1
hw/usb.h
@ -337,6 +337,7 @@ typedef struct USBPortOps {
|
||||
struct USBPort {
|
||||
USBDevice *dev;
|
||||
int speedmask;
|
||||
int hubcount;
|
||||
char path[16];
|
||||
USBPortOps *ops;
|
||||
void *opaque;
|
||||
|
@ -341,8 +341,10 @@ void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
|
||||
if (upstream) {
|
||||
snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
|
||||
upstream->path, portnr);
|
||||
downstream->hubcount = upstream->hubcount + 1;
|
||||
} else {
|
||||
snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
|
||||
downstream->hubcount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,7 +236,7 @@ static const USBDescDevice desc_device_tablet2 = {
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_TABLET,
|
||||
.bmAttributes = 0xa0,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 50,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_tablet2,
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "trace.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/usb/desc.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#define NUM_PORTS 8
|
||||
|
||||
@ -32,6 +33,7 @@ typedef struct USBHubPort {
|
||||
USBPort port;
|
||||
uint16_t wPortStatus;
|
||||
uint16_t wPortChange;
|
||||
uint16_t wPortChange_reported;
|
||||
} USBHubPort;
|
||||
|
||||
typedef struct USBHubState {
|
||||
@ -466,8 +468,11 @@ static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
||||
status = 0;
|
||||
for(i = 0; i < NUM_PORTS; i++) {
|
||||
port = &s->ports[i];
|
||||
if (port->wPortChange)
|
||||
if (port->wPortChange &&
|
||||
port->wPortChange_reported != port->wPortChange) {
|
||||
status |= (1 << (i + 1));
|
||||
}
|
||||
port->wPortChange_reported = port->wPortChange;
|
||||
}
|
||||
if (status != 0) {
|
||||
for(i = 0; i < n; i++) {
|
||||
@ -514,6 +519,11 @@ static int usb_hub_initfn(USBDevice *dev)
|
||||
USBHubPort *port;
|
||||
int i;
|
||||
|
||||
if (dev->port->hubcount == 5) {
|
||||
error_report("usb hub chain too deep");
|
||||
return -1;
|
||||
}
|
||||
|
||||
usb_desc_create_serial(dev);
|
||||
usb_desc_init(dev);
|
||||
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
|
||||
|
@ -452,7 +452,6 @@ struct XHCIState {
|
||||
MemoryRegion mem_oper;
|
||||
MemoryRegion mem_runtime;
|
||||
MemoryRegion mem_doorbell;
|
||||
const char *name;
|
||||
unsigned int devaddr;
|
||||
|
||||
/* properties */
|
||||
@ -1172,8 +1171,6 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
||||
uint32_t ctx[5];
|
||||
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));
|
||||
ctx[0] &= ~EP_STATE_MASK;
|
||||
ctx[0] |= state;
|
||||
@ -2568,7 +2565,7 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "xhci: unimplemented command %d\n", type);
|
||||
trace_usb_xhci_unimplemented("command", type);
|
||||
event.ccode = CC_TRB_ERROR;
|
||||
break;
|
||||
}
|
||||
@ -2767,7 +2764,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
|
||||
ret = 0x00000000; /* reserved */
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg);
|
||||
trace_usb_xhci_unimplemented("cap read", reg);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@ -2790,8 +2787,7 @@ static uint64_t xhci_port_read(void *ptr, hwaddr reg, unsigned size)
|
||||
break;
|
||||
case 0x0c: /* reserved */
|
||||
default:
|
||||
fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n",
|
||||
port->portnr, (uint32_t)reg);
|
||||
trace_usb_xhci_unimplemented("port read", reg);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@ -2831,8 +2827,7 @@ static void xhci_port_write(void *ptr, hwaddr reg,
|
||||
case 0x04: /* PORTPMSC */
|
||||
case 0x08: /* PORTLI */
|
||||
default:
|
||||
fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n",
|
||||
port->portnr, (uint32_t)reg);
|
||||
trace_usb_xhci_unimplemented("port write", reg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2870,7 +2865,7 @@ static uint64_t xhci_oper_read(void *ptr, hwaddr reg, unsigned size)
|
||||
ret = xhci->config;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg);
|
||||
trace_usb_xhci_unimplemented("oper read", reg);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
@ -2935,7 +2930,7 @@ static void xhci_oper_write(void *ptr, hwaddr reg,
|
||||
xhci->config = val & 0xff;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n",
|
||||
(int)reg);
|
||||
trace_usb_xhci_unimplemented("runtime read", reg);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@ -2996,7 +2990,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||
trace_usb_xhci_runtime_write(reg, val);
|
||||
|
||||
if (reg < 0x20) {
|
||||
fprintf(stderr, "%s: reg 0x%x unimplemented\n", __func__, (int)reg);
|
||||
trace_usb_xhci_unimplemented("runtime write", reg);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3038,8 +3032,7 @@ static void xhci_runtime_write(void *ptr, hwaddr reg,
|
||||
xhci_events_update(xhci, v);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n",
|
||||
(int)reg);
|
||||
trace_usb_xhci_unimplemented("oper write", reg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3290,6 +3283,9 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
|
||||
if (xhci->numintrs > MAXINTRS) {
|
||||
xhci->numintrs = MAXINTRS;
|
||||
}
|
||||
while (xhci->numintrs & (xhci->numintrs - 1)) { /* ! power of 2 */
|
||||
xhci->numintrs++;
|
||||
}
|
||||
if (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 */
|
||||
const uint8_t *read_buf;
|
||||
int read_buf_size;
|
||||
/* Active chardev-watch-tag */
|
||||
guint watch;
|
||||
/* For async handling of close */
|
||||
QEMUBH *chardev_close_bh;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
int r;
|
||||
|
||||
if (!dev->cs->be_open) {
|
||||
return 0;
|
||||
@ -267,7 +281,17 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
|
||||
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);
|
||||
dev->parser = NULL;
|
||||
}
|
||||
if (dev->watch) {
|
||||
g_source_remove(dev->watch);
|
||||
dev->watch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbredir_create_parser(USBRedirDevice *dev)
|
||||
@ -1317,6 +1345,9 @@ static void usbredir_handle_destroy(USBDevice *udev)
|
||||
if (dev->parser) {
|
||||
usbredirparser_destroy(dev->parser);
|
||||
}
|
||||
if (dev->watch) {
|
||||
g_source_remove(dev->watch);
|
||||
}
|
||||
|
||||
free(dev->filter_rules);
|
||||
}
|
||||
@ -1973,6 +2004,10 @@ static int usbredir_post_load(void *priv, int version_id)
|
||||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
|
||||
if (dev->parser == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (dev->device_info.speed) {
|
||||
case usb_redir_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_success(void *xfer, uint32_t bytes) "%p: len %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
|
||||
usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
|
||||
|
Loading…
Reference in New Issue
Block a user