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

* 'usb.65' of git://git.kraxel.org/qemu:
  uhci: Don't queue up packets after one with the SPD flag set
  usb-redir: Revert usb-redir part of commit 93bfef4c
  usb-redir: Add chardev open / close debug logging
  usb-redir: Add support for migration
  usb-redir: Store max_packet_size in endp_data
  usb-redir: Add an already_in_flight packet-id queue
  usb-redir: Change cancelled packet code into a generic packet-id queue
  ehci: Walk async schedule before and after migration
  ehci: Don't set seen to 0 when removing unseen queue-heads
  configure: usbredir fixes
  ehci: Don't process too much frames in 1 timer tick (v2)
  ehci: Fix interrupts stopping when Interrupt Threshold Control is 8
  ehci: switch to new-style memory ops
  usb-host: allow emulated (non-async) control requests without USBPacket
This commit is contained in:
Aurelien Jarno 2012-09-21 19:53:26 +02:00
commit cfb75cb980
6 changed files with 629 additions and 142 deletions

2
configure vendored
View File

@ -2795,7 +2795,7 @@ if test "$usb_redir" != "no" ; then
usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags"
LIBS="$LIBS $usb_redir_libs" libs_softmmu="$libs_softmmu $usb_redir_libs"
else else
if test "$usb_redir" = "yes"; then if test "$usb_redir" = "yes"; then
feature_not_found "usb-redir" feature_not_found "usb-redir"

View File

@ -34,6 +34,7 @@
#include "monitor.h" #include "monitor.h"
#include "trace.h" #include "trace.h"
#include "dma.h" #include "dma.h"
#include "sysemu.h"
#define EHCI_DEBUG 0 #define EHCI_DEBUG 0
@ -139,6 +140,7 @@
#define NB_PORTS 6 // Number of downstream ports #define NB_PORTS 6 // Number of downstream ports
#define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction
#define MAX_QH 100 // Max allowable queue heads in a chain #define MAX_QH 100 // Max allowable queue heads in a chain
#define MIN_FR_PER_TICK 3 // Min frames to process when catching up
/* Internal periodic / asynchronous schedule state machine states /* Internal periodic / asynchronous schedule state machine states
*/ */
@ -389,6 +391,9 @@ struct EHCIState {
USBBus bus; USBBus bus;
qemu_irq irq; qemu_irq irq;
MemoryRegion mem; MemoryRegion mem;
MemoryRegion mem_caps;
MemoryRegion mem_opreg;
MemoryRegion mem_ports;
int companion_count; int companion_count;
/* properties */ /* properties */
@ -398,10 +403,10 @@ struct EHCIState {
* EHCI spec version 1.0 Section 2.3 * EHCI spec version 1.0 Section 2.3
* Host Controller Operational Registers * Host Controller Operational Registers
*/ */
uint8_t caps[OPREGBASE];
union { union {
uint8_t mmio[MMIO_SIZE]; uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)];
struct { struct {
uint8_t cap[OPREGBASE];
uint32_t usbcmd; uint32_t usbcmd;
uint32_t usbsts; uint32_t usbsts;
uint32_t usbintr; uint32_t usbintr;
@ -411,9 +416,9 @@ struct EHCIState {
uint32_t asynclistaddr; uint32_t asynclistaddr;
uint32_t notused[9]; uint32_t notused[9];
uint32_t configflag; uint32_t configflag;
uint32_t portsc[NB_PORTS];
}; };
}; };
uint32_t portsc[NB_PORTS];
/* /*
* Internal states, shadow registers, etc * Internal states, shadow registers, etc
@ -471,22 +476,12 @@ static const char *ehci_state_names[] = {
}; };
static const char *ehci_mmio_names[] = { static const char *ehci_mmio_names[] = {
[CAPLENGTH] = "CAPLENGTH",
[HCIVERSION] = "HCIVERSION",
[HCSPARAMS] = "HCSPARAMS",
[HCCPARAMS] = "HCCPARAMS",
[USBCMD] = "USBCMD", [USBCMD] = "USBCMD",
[USBSTS] = "USBSTS", [USBSTS] = "USBSTS",
[USBINTR] = "USBINTR", [USBINTR] = "USBINTR",
[FRINDEX] = "FRINDEX", [FRINDEX] = "FRINDEX",
[PERIODICLISTBASE] = "P-LIST BASE", [PERIODICLISTBASE] = "P-LIST BASE",
[ASYNCLISTADDR] = "A-LIST ADDR", [ASYNCLISTADDR] = "A-LIST ADDR",
[PORTSC_BEGIN] = "PORTSC #0",
[PORTSC_BEGIN + 4] = "PORTSC #1",
[PORTSC_BEGIN + 8] = "PORTSC #2",
[PORTSC_BEGIN + 12] = "PORTSC #3",
[PORTSC_BEGIN + 16] = "PORTSC #4",
[PORTSC_BEGIN + 20] = "PORTSC #5",
[CONFIGFLAG] = "CONFIGFLAG", [CONFIGFLAG] = "CONFIGFLAG",
}; };
@ -509,7 +504,8 @@ static const char *state2str(uint32_t state)
static const char *addr2str(target_phys_addr_t addr) static const char *addr2str(target_phys_addr_t addr)
{ {
return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names),
addr + OPREGBASE);
} }
static void ehci_trace_usbsts(uint32_t mask, int state) static void ehci_trace_usbsts(uint32_t mask, int state)
@ -853,10 +849,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
return NULL; return NULL;
} }
static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) static void ehci_queues_rip_unused(EHCIState *ehci, int async)
{ {
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL; const char *warn = async ? "guest unlinked busy QH" : NULL;
uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
EHCIQueue *q, *tmp; EHCIQueue *q, *tmp;
@ -866,13 +862,25 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
q->ts = ehci->last_run_ns; q->ts = ehci->last_run_ns;
continue; continue;
} }
if (!flush && ehci->last_run_ns < q->ts + maxage) { if (ehci->last_run_ns < q->ts + maxage) {
continue; continue;
} }
ehci_free_queue(q, warn); ehci_free_queue(q, warn);
} }
} }
static void ehci_queues_rip_unseen(EHCIState *ehci, int async)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
if (!q->seen) {
ehci_free_queue(q, NULL);
}
}
}
static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
{ {
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
@ -1018,7 +1026,7 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
} }
s->companion_count++; s->companion_count++;
s->mmio[0x05] = (s->companion_count << 4) | portcount; s->caps[0x05] = (s->companion_count << 4) | portcount;
return 0; return 0;
} }
@ -1063,7 +1071,8 @@ static void ehci_reset(void *opaque)
} }
} }
memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); memset(&s->opreg, 0x00, sizeof(s->opreg));
memset(&s->portsc, 0x00, sizeof(s->portsc));
s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
s->usbsts = USBSTS_HALT; s->usbsts = USBSTS_HALT;
@ -1090,50 +1099,35 @@ static void ehci_reset(void *opaque)
qemu_bh_cancel(s->async_bh); qemu_bh_cancel(s->async_bh);
} }
static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) static uint64_t ehci_caps_read(void *ptr, target_phys_addr_t addr,
unsigned size)
{
EHCIState *s = ptr;
return s->caps[addr];
}
static uint64_t ehci_opreg_read(void *ptr, target_phys_addr_t addr,
unsigned size)
{ {
EHCIState *s = ptr; EHCIState *s = ptr;
uint32_t val; uint32_t val;
val = s->mmio[addr]; val = s->opreg[addr >> 2];
trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val);
return val; return val;
} }
static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr) static uint64_t ehci_port_read(void *ptr, target_phys_addr_t addr,
unsigned size)
{ {
EHCIState *s = ptr; EHCIState *s = ptr;
uint32_t val; uint32_t val;
val = s->mmio[addr] | (s->mmio[addr+1] << 8); val = s->portsc[addr >> 2];
trace_usb_ehci_portsc_read(addr + PORTSC_BEGIN, addr >> 2, val);
return val; return val;
} }
static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
{
EHCIState *s = ptr;
uint32_t val;
val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
(s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
trace_usb_ehci_mmio_readl(addr, addr2str(addr), val);
return val;
}
static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
{
fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
exit(1);
}
static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
{
fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
exit(1);
}
static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
{ {
USBDevice *dev = s->ports[port].dev; USBDevice *dev = s->ports[port].dev;
@ -1162,11 +1156,17 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
} }
} }
static void handle_port_status_write(EHCIState *s, int port, uint32_t val) static void ehci_port_write(void *ptr, target_phys_addr_t addr,
uint64_t val, unsigned size)
{ {
EHCIState *s = ptr;
int port = addr >> 2;
uint32_t *portsc = &s->portsc[port]; uint32_t *portsc = &s->portsc[port];
uint32_t old = *portsc;
USBDevice *dev = s->ports[port].dev; USBDevice *dev = s->ports[port].dev;
trace_usb_ehci_portsc_write(addr + PORTSC_BEGIN, addr >> 2, val);
/* Clear rwc bits */ /* Clear rwc bits */
*portsc &= ~(val & PORTSC_RWC_MASK); *portsc &= ~(val & PORTSC_RWC_MASK);
/* The guest may clear, but not set the PED bit */ /* The guest may clear, but not set the PED bit */
@ -1198,39 +1198,20 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
*portsc &= ~PORTSC_RO_MASK; *portsc &= ~PORTSC_RO_MASK;
*portsc |= val; *portsc |= val;
trace_usb_ehci_portsc_change(addr + PORTSC_BEGIN, addr >> 2, *portsc, old);
} }
static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) static void ehci_opreg_write(void *ptr, target_phys_addr_t addr,
uint64_t val, unsigned size)
{ {
EHCIState *s = ptr; EHCIState *s = ptr;
uint32_t *mmio = (uint32_t *)(&s->mmio[addr]); uint32_t *mmio = s->opreg + (addr >> 2);
uint32_t old = *mmio; uint32_t old = *mmio;
int i; int i;
trace_usb_ehci_mmio_writel(addr, addr2str(addr), val); trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val);
/* Only aligned reads are allowed on OHCI */ switch (addr + OPREGBASE) {
if (addr & 3) {
fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
TARGET_FMT_plx "\n", addr);
return;
}
if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
handle_port_status_write(s, (addr-PORTSC)/4, val);
trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
return;
}
if (addr < OPREGBASE) {
fprintf(stderr, "usb-ehci: write attempt to read-only register"
TARGET_FMT_plx "\n", addr);
return;
}
/* Do any register specific pre-write processing here. */
switch(addr) {
case USBCMD: case USBCMD:
if (val & USBCMD_HCRESET) { if (val & USBCMD_HCRESET) {
ehci_reset(s); ehci_reset(s);
@ -1241,7 +1222,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
/* not supporting dynamic frame list size at the moment */ /* not supporting dynamic frame list size at the moment */
if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
fprintf(stderr, "attempt to set frame list size -- value %d\n", fprintf(stderr, "attempt to set frame list size -- value %d\n",
val & USBCMD_FLS); (int)val & USBCMD_FLS);
val &= ~USBCMD_FLS; val &= ~USBCMD_FLS;
} }
@ -1308,7 +1289,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
} }
*mmio = val; *mmio = val;
trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old); trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old);
} }
@ -1732,7 +1713,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async)
ehci_set_usbsts(ehci, USBSTS_REC); ehci_set_usbsts(ehci, USBSTS_REC);
} }
ehci_queues_rip_unused(ehci, async, 0); ehci_queues_rip_unused(ehci, async);
/* Find the head of the list (4.9.1.1) */ /* Find the head of the list (4.9.1.1) */
for(i = 0; i < MAX_QH; i++) { for(i = 0; i < MAX_QH; i++) {
@ -2364,7 +2345,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
*/ */
if (ehci->usbcmd & USBCMD_IAAD) { if (ehci->usbcmd & USBCMD_IAAD) {
/* Remove all unseen qhs from the async qhs queue */ /* Remove all unseen qhs from the async qhs queue */
ehci_queues_rip_unused(ehci, async, 1); ehci_queues_rip_unseen(ehci, async);
trace_usb_ehci_doorbell_ack(); trace_usb_ehci_doorbell_ack();
ehci->usbcmd &= ~USBCMD_IAAD; ehci->usbcmd &= ~USBCMD_IAAD;
ehci_raise_irq(ehci, USBSTS_IAA); ehci_raise_irq(ehci, USBSTS_IAA);
@ -2417,7 +2398,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
ehci_set_fetch_addr(ehci, async,entry); ehci_set_fetch_addr(ehci, async,entry);
ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_set_state(ehci, async, EST_FETCHENTRY);
ehci_advance_state(ehci, async); ehci_advance_state(ehci, async);
ehci_queues_rip_unused(ehci, async, 0); ehci_queues_rip_unused(ehci, async);
break; break;
default: default:
@ -2446,7 +2427,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames)
if (ehci->frindex == 0x00004000) { if (ehci->frindex == 0x00004000) {
ehci_raise_irq(ehci, USBSTS_FLR); ehci_raise_irq(ehci, USBSTS_FLR);
ehci->frindex = 0; ehci->frindex = 0;
if (ehci->usbsts_frindex > 0x00004000) { if (ehci->usbsts_frindex >= 0x00004000) {
ehci->usbsts_frindex -= 0x00004000; ehci->usbsts_frindex -= 0x00004000;
} else { } else {
ehci->usbsts_frindex = 0; ehci->usbsts_frindex = 0;
@ -2481,6 +2462,19 @@ static void ehci_frame_timer(void *opaque)
} }
for (i = 0; i < frames; i++) { for (i = 0; i < frames; i++) {
/*
* If we're running behind schedule, we should not catch up
* too fast, as that will make some guests unhappy:
* 1) We must process a minimum of MIN_FR_PER_TICK frames,
* otherwise we will never catch up
* 2) Process frames until the guest has requested an irq (IOC)
*/
if (i >= MIN_FR_PER_TICK) {
ehci_commit_irq(ehci);
if ((ehci->usbsts & USBINTR_MASK) & ehci->usbintr) {
break;
}
}
ehci_update_frindex(ehci, 1); ehci_update_frindex(ehci, 1);
ehci_advance_periodic_state(ehci); ehci_advance_periodic_state(ehci);
ehci->last_run_ns += FRAME_TIMER_NS; ehci->last_run_ns += FRAME_TIMER_NS;
@ -2520,11 +2514,28 @@ static void ehci_async_bh(void *opaque)
ehci_advance_async_state(ehci); ehci_advance_async_state(ehci);
} }
static const MemoryRegionOps ehci_mem_ops = { static const MemoryRegionOps ehci_mmio_caps_ops = {
.old_mmio = { .read = ehci_caps_read,
.read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl }, .valid.min_access_size = 1,
.write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel }, .valid.max_access_size = 4,
}, .impl.min_access_size = 1,
.impl.max_access_size = 1,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static const MemoryRegionOps ehci_mmio_opreg_ops = {
.read = ehci_opreg_read,
.write = ehci_opreg_write,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.endianness = DEVICE_LITTLE_ENDIAN,
};
static const MemoryRegionOps ehci_mmio_port_ops = {
.read = ehci_port_read,
.write = ehci_port_write,
.valid.min_access_size = 4,
.valid.max_access_size = 4,
.endianness = DEVICE_LITTLE_ENDIAN, .endianness = DEVICE_LITTLE_ENDIAN,
}; };
@ -2562,6 +2573,32 @@ static int usb_ehci_post_load(void *opaque, int version_id)
return 0; return 0;
} }
static void usb_ehci_vm_state_change(void *opaque, int running, RunState state)
{
EHCIState *ehci = opaque;
/*
* We don't migrate the EHCIQueue-s, instead we rebuild them for the
* schedule in guest memory. We must do the rebuilt ASAP, so that
* USB-devices which have async handled packages have a packet in the
* ep queue to match the completion with.
*/
if (state == RUN_STATE_RUNNING) {
ehci_advance_async_state(ehci);
}
/*
* The schedule rebuilt from guest memory could cause the migration dest
* to miss a QH unlink, and fail to cancel packets, since the unlinked QH
* will never have existed on the destination. Therefor we must flush the
* async schedule on savevm to catch any not yet noticed unlinks.
*/
if (state == RUN_STATE_SAVE_VM) {
ehci_advance_async_state(ehci);
ehci_queues_rip_unseen(ehci, 1);
}
}
static const VMStateDescription vmstate_ehci = { static const VMStateDescription vmstate_ehci = {
.name = "ehci", .name = "ehci",
.version_id = 2, .version_id = 2,
@ -2681,19 +2718,19 @@ static int usb_ehci_initfn(PCIDevice *dev)
pci_conf[0x6e] = 0x00; pci_conf[0x6e] = 0x00;
pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS
// 2.2 host controller interface version /* 2.2 host controller interface version */
s->mmio[0x00] = (uint8_t) OPREGBASE; s->caps[0x00] = (uint8_t) OPREGBASE;
s->mmio[0x01] = 0x00; s->caps[0x01] = 0x00;
s->mmio[0x02] = 0x00; s->caps[0x02] = 0x00;
s->mmio[0x03] = 0x01; // HC version s->caps[0x03] = 0x01; /* HC version */
s->mmio[0x04] = NB_PORTS; // Number of downstream ports s->caps[0x04] = NB_PORTS; /* Number of downstream ports */
s->mmio[0x05] = 0x00; // No companion ports at present s->caps[0x05] = 0x00; /* No companion ports at present */
s->mmio[0x06] = 0x00; s->caps[0x06] = 0x00;
s->mmio[0x07] = 0x00; s->caps[0x07] = 0x00;
s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */
s->mmio[0x09] = 0x68; // EECP s->caps[0x09] = 0x68; /* EECP */
s->mmio[0x0a] = 0x00; s->caps[0x0a] = 0x00;
s->mmio[0x0b] = 0x00; s->caps[0x0b] = 0x00;
s->irq = s->dev.irq[3]; s->irq = s->dev.irq[3];
@ -2711,8 +2748,20 @@ static int usb_ehci_initfn(PCIDevice *dev)
usb_packet_init(&s->ipacket); usb_packet_init(&s->ipacket);
qemu_register_reset(ehci_reset, s); qemu_register_reset(ehci_reset, s);
qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s);
memory_region_init(&s->mem, "ehci", MMIO_SIZE);
memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s,
"capabilities", OPREGBASE);
memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s,
"operational", PORTSC_BEGIN - OPREGBASE);
memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s,
"ports", PORTSC_END - PORTSC_BEGIN);
memory_region_add_subregion(&s->mem, 0, &s->mem_caps);
memory_region_add_subregion(&s->mem, OPREGBASE, &s->mem_opreg);
memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports);
memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
return 0; return 0;

View File

@ -1000,6 +1000,9 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
} }
assert(ret == TD_RESULT_ASYNC_START); assert(ret == TD_RESULT_ASYNC_START);
assert(int_mask == 0); assert(int_mask == 0);
if (ptd.ctrl & TD_CTRL_SPD) {
break;
}
plink = ptd.link; plink = ptd.link;
} }
} }
@ -1097,7 +1100,7 @@ static void uhci_process_frame(UHCIState *s)
case TD_RESULT_ASYNC_START: case TD_RESULT_ASYNC_START:
trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf); trace_usb_uhci_td_async(curr_qh & ~0xf, link & ~0xf);
if (is_valid(td.link)) { if (is_valid(td.link) && !(td.ctrl & TD_CTRL_SPD)) {
uhci_fill_queue(s, &td); uhci_fill_queue(s, &td);
} }
link = curr_qh ? qh.link : td.link; link = curr_qh ? qh.link : td.link;

View File

@ -1045,7 +1045,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
/* Note request is (bRequestType << 8) | bRequest */ /* Note request is (bRequestType << 8) | bRequest */
trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
assert(p->result == 0);
switch (request) { switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS: case DeviceOutRequest | USB_REQ_SET_ADDRESS:
@ -1074,6 +1073,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
} }
/* The rest are asynchronous */ /* The rest are asynchronous */
assert(p && p->result == 0);
if (length > sizeof(dev->data_buf)) { if (length > sizeof(dev->data_buf)) {
fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",

View File

@ -43,7 +43,6 @@
#define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
#define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
typedef struct Cancelled Cancelled;
typedef struct USBRedirDevice USBRedirDevice; typedef struct USBRedirDevice USBRedirDevice;
/* Struct to hold buffered packets (iso or int input packets) */ /* Struct to hold buffered packets (iso or int input packets) */
@ -58,6 +57,7 @@ struct endp_data {
uint8_t type; uint8_t type;
uint8_t interval; uint8_t interval;
uint8_t interface; /* bInterfaceNumber this ep belongs to */ uint8_t interface; /* bInterfaceNumber this ep belongs to */
uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
uint8_t iso_started; uint8_t iso_started;
uint8_t iso_error; /* For reporting iso errors to the HC */ uint8_t iso_error; /* For reporting iso errors to the HC */
uint8_t interrupt_started; uint8_t interrupt_started;
@ -65,8 +65,20 @@ struct endp_data {
uint8_t bufpq_prefilled; uint8_t bufpq_prefilled;
uint8_t bufpq_dropping_packets; uint8_t bufpq_dropping_packets;
QTAILQ_HEAD(, buf_packet) bufpq; QTAILQ_HEAD(, buf_packet) bufpq;
int bufpq_size; int32_t bufpq_size;
int bufpq_target_size; int32_t bufpq_target_size;
};
struct PacketIdQueueEntry {
uint64_t id;
QTAILQ_ENTRY(PacketIdQueueEntry)next;
};
struct PacketIdQueue {
USBRedirDevice *dev;
const char *name;
QTAILQ_HEAD(, PacketIdQueueEntry) head;
int size;
}; };
struct USBRedirDevice { struct USBRedirDevice {
@ -86,7 +98,8 @@ struct USBRedirDevice {
int64_t next_attach_time; int64_t next_attach_time;
struct usbredirparser *parser; struct usbredirparser *parser;
struct endp_data endpoint[MAX_ENDPOINTS]; struct endp_data endpoint[MAX_ENDPOINTS];
QTAILQ_HEAD(, Cancelled) cancelled; struct PacketIdQueue cancelled;
struct PacketIdQueue already_in_flight;
/* Data for device filtering */ /* Data for device filtering */
struct usb_redir_device_connect_header device_info; struct usb_redir_device_connect_header device_info;
struct usb_redir_interface_info_header interface_info; struct usb_redir_interface_info_header interface_info;
@ -94,11 +107,6 @@ struct USBRedirDevice {
int filter_rules_count; int filter_rules_count;
}; };
struct Cancelled {
uint64_t id;
QTAILQ_ENTRY(Cancelled)next;
};
static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
static void usbredir_device_connect(void *priv, static void usbredir_device_connect(void *priv,
struct usb_redir_device_connect_header *device_connect); struct usb_redir_device_connect_header *device_connect);
@ -134,6 +142,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
static int usbredir_handle_status(USBRedirDevice *dev, static int usbredir_handle_status(USBRedirDevice *dev,
int status, int actual_len); int status, int actual_len);
#define VERSION "qemu usb-redir guest " QEMU_VERSION
/* /*
* Logging stuff * Logging stuff
*/ */
@ -232,6 +242,11 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
return 0; return 0;
} }
/* Don't send new data to the chardev until our state is fully synced */
if (!runstate_check(RUN_STATE_RUNNING)) {
return 0;
}
return qemu_chr_fe_write(dev->cs, data, count); return qemu_chr_fe_write(dev->cs, data, count);
} }
@ -239,37 +254,103 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
* Cancelled and buffered packets helpers * Cancelled and buffered packets helpers
*/ */
static void packet_id_queue_init(struct PacketIdQueue *q,
USBRedirDevice *dev, const char *name)
{
q->dev = dev;
q->name = name;
QTAILQ_INIT(&q->head);
q->size = 0;
}
static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id)
{
USBRedirDevice *dev = q->dev;
struct PacketIdQueueEntry *e;
DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name);
e = g_malloc0(sizeof(struct PacketIdQueueEntry));
e->id = id;
QTAILQ_INSERT_TAIL(&q->head, e, next);
q->size++;
}
static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id)
{
USBRedirDevice *dev = q->dev;
struct PacketIdQueueEntry *e;
QTAILQ_FOREACH(e, &q->head, next) {
if (e->id == id) {
DPRINTF("removing packet id %"PRIu64" from %s queue\n",
id, q->name);
QTAILQ_REMOVE(&q->head, e, next);
q->size--;
g_free(e);
return 1;
}
}
return 0;
}
static void packet_id_queue_empty(struct PacketIdQueue *q)
{
USBRedirDevice *dev = q->dev;
struct PacketIdQueueEntry *e, *next_e;
DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name);
QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) {
QTAILQ_REMOVE(&q->head, e, next);
g_free(e);
}
q->size = 0;
}
static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
{ {
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
Cancelled *c;
DPRINTF("cancel packet id %"PRIu64"\n", p->id);
c = g_malloc0(sizeof(Cancelled));
c->id = p->id;
QTAILQ_INSERT_TAIL(&dev->cancelled, c, next);
packet_id_queue_add(&dev->cancelled, p->id);
usbredirparser_send_cancel_data_packet(dev->parser, p->id); usbredirparser_send_cancel_data_packet(dev->parser, p->id);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
} }
static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
{ {
Cancelled *c;
if (!dev->dev.attached) { if (!dev->dev.attached) {
return 1; /* Treat everything as cancelled after a disconnect */ return 1; /* Treat everything as cancelled after a disconnect */
} }
return packet_id_queue_remove(&dev->cancelled, id);
}
QTAILQ_FOREACH(c, &dev->cancelled, next) { static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev,
if (c->id == id) { struct USBEndpoint *ep)
QTAILQ_REMOVE(&dev->cancelled, c, next); {
g_free(c); static USBPacket *p;
return 1;
} QTAILQ_FOREACH(p, &ep->queue, queue) {
packet_id_queue_add(&dev->already_in_flight, p->id);
} }
return 0; }
static void usbredir_fill_already_in_flight(USBRedirDevice *dev)
{
int ep;
struct USBDevice *udev = &dev->dev;
usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl);
for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]);
usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]);
}
}
static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id)
{
return packet_id_queue_remove(&dev->already_in_flight, id);
} }
static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
@ -487,6 +568,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC;
}
bulk_packet.endpoint = ep; bulk_packet.endpoint = ep;
bulk_packet.length = p->iov.size; bulk_packet.length = p->iov.size;
bulk_packet.stream_id = 0; bulk_packet.stream_id = 0;
@ -567,6 +652,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep,
p->iov.size, p->id); p->iov.size, p->id);
if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC;
}
interrupt_packet.endpoint = ep; interrupt_packet.endpoint = ep;
interrupt_packet.length = p->iov.size; interrupt_packet.length = p->iov.size;
@ -709,6 +798,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
struct usb_redir_control_packet_header control_packet; struct usb_redir_control_packet_header control_packet;
if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC;
}
/* Special cases for certain standard device requests */ /* Special cases for certain standard device requests */
switch (request) { switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS: case DeviceOutRequest | USB_REQ_SET_ADDRESS:
@ -763,6 +856,7 @@ static void usbredir_chardev_close_bh(void *opaque)
usbredir_device_disconnect(dev); usbredir_device_disconnect(dev);
if (dev->parser) { if (dev->parser) {
DPRINTF("destroying usbredirparser\n");
usbredirparser_destroy(dev->parser); usbredirparser_destroy(dev->parser);
dev->parser = NULL; dev->parser = NULL;
} }
@ -771,14 +865,13 @@ static void usbredir_chardev_close_bh(void *opaque)
static void usbredir_chardev_open(USBRedirDevice *dev) static void usbredir_chardev_open(USBRedirDevice *dev)
{ {
uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
char version[32]; int flags = 0;
/* Make sure any pending closes are handled (no-op if none pending) */ /* Make sure any pending closes are handled (no-op if none pending) */
usbredir_chardev_close_bh(dev); usbredir_chardev_close_bh(dev);
qemu_bh_cancel(dev->chardev_close_bh); qemu_bh_cancel(dev->chardev_close_bh);
strcpy(version, "qemu usb-redir guest "); DPRINTF("creating usbredirparser\n");
pstrcat(version, sizeof(version), qemu_get_version());
dev->parser = qemu_oom_check(usbredirparser_create()); dev->parser = qemu_oom_check(usbredirparser_create());
dev->parser->priv = dev; dev->parser->priv = dev;
@ -807,7 +900,12 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); 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_ep_info_max_packet_size);
usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
if (runstate_check(RUN_STATE_INMIGRATE)) {
flags |= usbredirparser_fl_no_hello;
}
usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE,
flags);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
} }
@ -853,6 +951,11 @@ static int usbredir_chardev_can_read(void *opaque)
return 0; return 0;
} }
/* Don't read new data from the chardev until our state is fully synced */
if (!runstate_check(RUN_STATE_RUNNING)) {
return 0;
}
/* usbredir_parser_do_read will consume *all* data we give it */ /* usbredir_parser_do_read will consume *all* data we give it */
return 1024 * 1024; return 1024 * 1024;
} }
@ -878,9 +981,11 @@ static void usbredir_chardev_event(void *opaque, int event)
switch (event) { switch (event) {
case CHR_EVENT_OPENED: case CHR_EVENT_OPENED:
DPRINTF("chardev open\n");
usbredir_chardev_open(dev); usbredir_chardev_open(dev);
break; break;
case CHR_EVENT_CLOSED: case CHR_EVENT_CLOSED:
DPRINTF("chardev close\n");
qemu_bh_schedule(dev->chardev_close_bh); qemu_bh_schedule(dev->chardev_close_bh);
break; break;
} }
@ -890,6 +995,15 @@ static void usbredir_chardev_event(void *opaque, int event)
* init + destroy * init + destroy
*/ */
static void usbredir_vm_state_change(void *priv, int running, RunState state)
{
USBRedirDevice *dev = priv;
if (state == RUN_STATE_RUNNING && dev->parser != NULL) {
usbredirparser_do_write(dev->parser); /* Flush any pending writes */
}
}
static int usbredir_initfn(USBDevice *udev) static int usbredir_initfn(USBDevice *udev)
{ {
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
@ -914,7 +1028,8 @@ static int usbredir_initfn(USBDevice *udev)
dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_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); dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
QTAILQ_INIT(&dev->cancelled); packet_id_queue_init(&dev->cancelled, dev, "cancelled");
packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight");
for (i = 0; i < MAX_ENDPOINTS; i++) { for (i = 0; i < MAX_ENDPOINTS; i++) {
QTAILQ_INIT(&dev->endpoint[i].bufpq); QTAILQ_INIT(&dev->endpoint[i].bufpq);
} }
@ -927,19 +1042,17 @@ static int usbredir_initfn(USBDevice *udev)
qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
usbredir_chardev_read, usbredir_chardev_event, dev); usbredir_chardev_read, usbredir_chardev_event, dev);
qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
add_boot_device_path(dev->bootindex, &udev->qdev, NULL); add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
return 0; return 0;
} }
static void usbredir_cleanup_device_queues(USBRedirDevice *dev) static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
{ {
Cancelled *c, *next_c;
int i; int i;
QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) { packet_id_queue_empty(&dev->cancelled);
QTAILQ_REMOVE(&dev->cancelled, c, next); packet_id_queue_empty(&dev->already_in_flight);
g_free(c);
}
for (i = 0; i < MAX_ENDPOINTS; i++) { for (i = 0; i < MAX_ENDPOINTS; i++) {
usbredir_free_bufpq(dev, I2EP(i)); usbredir_free_bufpq(dev, I2EP(i));
} }
@ -1118,6 +1231,7 @@ static void usbredir_device_disconnect(void *priv)
qemu_del_timer(dev->attach_timer); qemu_del_timer(dev->attach_timer);
if (dev->dev.attached) { if (dev->dev.attached) {
DPRINTF("detaching device\n");
usb_device_detach(&dev->dev); usb_device_detach(&dev->dev);
/* /*
* Delay next usb device attach to give the guest a chance to see * Delay next usb device attach to give the guest a chance to see
@ -1195,7 +1309,8 @@ static void usbredir_ep_info(void *priv,
usb_ep->ifnum = dev->endpoint[i].interface; usb_ep->ifnum = dev->endpoint[i].interface;
if (usbredirparser_peer_has_cap(dev->parser, if (usbredirparser_peer_has_cap(dev->parser,
usb_redir_cap_ep_info_max_packet_size)) { usb_redir_cap_ep_info_max_packet_size)) {
usb_ep->max_packet_size = ep_info->max_packet_size[i]; dev->endpoint[i].max_packet_size =
usb_ep->max_packet_size = ep_info->max_packet_size[i];
} }
if (ep_info->type[i] == usb_redir_type_bulk) { if (ep_info->type[i] == usb_redir_type_bulk) {
usb_ep->pipeline = true; usb_ep->pipeline = true;
@ -1418,6 +1533,322 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
} }
} }
/*
* Migration code
*/
static void usbredir_pre_save(void *priv)
{
USBRedirDevice *dev = priv;
usbredir_fill_already_in_flight(dev);
}
static int usbredir_post_load(void *priv, int version_id)
{
USBRedirDevice *dev = priv;
struct USBEndpoint *usb_ep;
int i;
switch (dev->device_info.speed) {
case usb_redir_speed_low:
dev->dev.speed = USB_SPEED_LOW;
break;
case usb_redir_speed_full:
dev->dev.speed = USB_SPEED_FULL;
break;
case usb_redir_speed_high:
dev->dev.speed = USB_SPEED_HIGH;
break;
case usb_redir_speed_super:
dev->dev.speed = USB_SPEED_SUPER;
break;
default:
dev->dev.speed = USB_SPEED_FULL;
}
dev->dev.speedmask = (1 << dev->dev.speed);
for (i = 0; i < MAX_ENDPOINTS; i++) {
usb_ep = usb_ep_get(&dev->dev,
(i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
i & 0x0f);
usb_ep->type = dev->endpoint[i].type;
usb_ep->ifnum = dev->endpoint[i].interface;
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
if (dev->endpoint[i].type == usb_redir_type_bulk) {
usb_ep->pipeline = true;
}
}
return 0;
}
/* For usbredirparser migration */
static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused)
{
USBRedirDevice *dev = priv;
uint8_t *data;
int len;
if (dev->parser == NULL) {
qemu_put_be32(f, 0);
return;
}
usbredirparser_serialize(dev->parser, &data, &len);
qemu_oom_check(data);
qemu_put_be32(f, len);
qemu_put_buffer(f, data, len);
free(data);
}
static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused)
{
USBRedirDevice *dev = priv;
uint8_t *data;
int len, ret;
len = qemu_get_be32(f);
if (len == 0) {
return 0;
}
/*
* Our chardev should be open already at this point, otherwise
* the usbredir channel will be broken (ie spice without seamless)
*/
if (dev->parser == NULL) {
ERROR("get_parser called with closed chardev, failing migration\n");
return -1;
}
data = g_malloc(len);
qemu_get_buffer(f, data, len);
ret = usbredirparser_unserialize(dev->parser, data, len);
g_free(data);
return ret;
}
static const VMStateInfo usbredir_parser_vmstate_info = {
.name = "usb-redir-parser",
.put = usbredir_put_parser,
.get = usbredir_get_parser,
};
/* For buffered packets (iso/irq) queue migration */
static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused)
{
struct endp_data *endp = priv;
struct buf_packet *bufp;
int remain = endp->bufpq_size;
qemu_put_be32(f, endp->bufpq_size);
QTAILQ_FOREACH(bufp, &endp->bufpq, next) {
qemu_put_be32(f, bufp->len);
qemu_put_be32(f, bufp->status);
qemu_put_buffer(f, bufp->data, bufp->len);
remain--;
}
assert(remain == 0);
}
static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
{
struct endp_data *endp = priv;
struct buf_packet *bufp;
int i;
endp->bufpq_size = qemu_get_be32(f);
for (i = 0; i < endp->bufpq_size; i++) {
bufp = g_malloc(sizeof(struct buf_packet));
bufp->len = qemu_get_be32(f);
bufp->status = qemu_get_be32(f);
bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */
qemu_get_buffer(f, bufp->data, bufp->len);
QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
}
return 0;
}
static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
.name = "usb-redir-bufpq",
.put = usbredir_put_bufpq,
.get = usbredir_get_bufpq,
};
/* For endp_data migration */
static const VMStateDescription usbredir_ep_vmstate = {
.name = "usb-redir-ep",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(type, struct endp_data),
VMSTATE_UINT8(interval, struct endp_data),
VMSTATE_UINT8(interface, struct endp_data),
VMSTATE_UINT16(max_packet_size, struct endp_data),
VMSTATE_UINT8(iso_started, struct endp_data),
VMSTATE_UINT8(iso_error, struct endp_data),
VMSTATE_UINT8(interrupt_started, struct endp_data),
VMSTATE_UINT8(interrupt_error, struct endp_data),
VMSTATE_UINT8(bufpq_prefilled, struct endp_data),
VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data),
{
.name = "bufpq",
.version_id = 0,
.field_exists = NULL,
.size = 0,
.info = &usbredir_ep_bufpq_vmstate_info,
.flags = VMS_SINGLE,
.offset = 0,
},
VMSTATE_INT32(bufpq_target_size, struct endp_data),
VMSTATE_END_OF_LIST()
}
};
/* For PacketIdQueue migration */
static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused)
{
struct PacketIdQueue *q = priv;
USBRedirDevice *dev = q->dev;
struct PacketIdQueueEntry *e;
int remain = q->size;
DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size);
qemu_put_be32(f, q->size);
QTAILQ_FOREACH(e, &q->head, next) {
qemu_put_be64(f, e->id);
remain--;
}
assert(remain == 0);
}
static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused)
{
struct PacketIdQueue *q = priv;
USBRedirDevice *dev = q->dev;
int i, size;
uint64_t id;
size = qemu_get_be32(f);
DPRINTF("get_packet_id_q %s size %d\n", q->name, size);
for (i = 0; i < size; i++) {
id = qemu_get_be64(f);
packet_id_queue_add(q, id);
}
assert(q->size == size);
return 0;
}
static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = {
.name = "usb-redir-packet-id-q",
.put = usbredir_put_packet_id_q,
.get = usbredir_get_packet_id_q,
};
static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = {
.name = "usb-redir-packet-id-queue",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
{
.name = "queue",
.version_id = 0,
.field_exists = NULL,
.size = 0,
.info = &usbredir_ep_packet_id_q_vmstate_info,
.flags = VMS_SINGLE,
.offset = 0,
},
VMSTATE_END_OF_LIST()
}
};
/* For usb_redir_device_connect_header migration */
static const VMStateDescription usbredir_device_info_vmstate = {
.name = "usb-redir-device-info",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(speed, struct usb_redir_device_connect_header),
VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header),
VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header),
VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header),
VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header),
VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header),
VMSTATE_UINT16(device_version_bcd,
struct usb_redir_device_connect_header),
VMSTATE_END_OF_LIST()
}
};
/* For usb_redir_interface_info_header migration */
static const VMStateDescription usbredir_interface_info_vmstate = {
.name = "usb-redir-interface-info",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(interface_count,
struct usb_redir_interface_info_header),
VMSTATE_UINT8_ARRAY(interface,
struct usb_redir_interface_info_header, 32),
VMSTATE_UINT8_ARRAY(interface_class,
struct usb_redir_interface_info_header, 32),
VMSTATE_UINT8_ARRAY(interface_subclass,
struct usb_redir_interface_info_header, 32),
VMSTATE_UINT8_ARRAY(interface_protocol,
struct usb_redir_interface_info_header, 32),
VMSTATE_END_OF_LIST()
}
};
/* And finally the USBRedirDevice vmstate itself */
static const VMStateDescription usbredir_vmstate = {
.name = "usb-redir",
.version_id = 1,
.minimum_version_id = 1,
.pre_save = usbredir_pre_save,
.post_load = usbredir_post_load,
.fields = (VMStateField[]) {
VMSTATE_USB_DEVICE(dev, USBRedirDevice),
VMSTATE_TIMER(attach_timer, USBRedirDevice),
{
.name = "parser",
.version_id = 0,
.field_exists = NULL,
.size = 0,
.info = &usbredir_parser_vmstate_info,
.flags = VMS_SINGLE,
.offset = 0,
},
VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1,
usbredir_ep_vmstate, struct endp_data),
VMSTATE_STRUCT(cancelled, USBRedirDevice, 1,
usbredir_ep_packet_id_queue_vmstate,
struct PacketIdQueue),
VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1,
usbredir_ep_packet_id_queue_vmstate,
struct PacketIdQueue),
VMSTATE_STRUCT(device_info, USBRedirDevice, 1,
usbredir_device_info_vmstate,
struct usb_redir_device_connect_header),
VMSTATE_STRUCT(interface_info, USBRedirDevice, 1,
usbredir_interface_info_vmstate,
struct usb_redir_interface_info_header),
VMSTATE_END_OF_LIST()
}
};
static Property usbredir_properties[] = { static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
@ -1438,6 +1869,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
uc->handle_reset = usbredir_handle_reset; uc->handle_reset = usbredir_handle_reset;
uc->handle_data = usbredir_handle_data; uc->handle_data = usbredir_handle_data;
uc->handle_control = usbredir_handle_control; uc->handle_control = usbredir_handle_control;
dc->vmsd = &usbredir_vmstate;
dc->props = usbredir_properties; dc->props = usbredir_properties;
} }

View File

@ -243,9 +243,12 @@ usb_port_release(int bus, const char *port) "bus %d, port %s"
# hw/usb/hcd-ehci.c # hw/usb/hcd-ehci.c
usb_ehci_reset(void) "=== RESET ===" usb_ehci_reset(void) "=== RESET ==="
usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x" usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)" usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
usb_ehci_portsc_read(uint32_t addr, uint32_t port, uint32_t val) "rd mmio %04x [port %d] = %x"
usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio %04x [port %d] = %x"
usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio %04x [port %d] = %x (old: %x)"
usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x" usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"