From 63587e31353b6652cadfcfb869f5692a2b69daeb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 6 Sep 2012 12:03:41 +0200 Subject: usb-host: allow emulated (non-async) control requests without USBPacket xhci needs this for USB_REQ_SET_ADDRESS due to the way usb addressing is handled by the xhci hardware. Signed-off-by: Gerd Hoffmann --- hw/usb/host-linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index 8df92074d3..44f1a64b35 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -1045,7 +1045,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, /* Note request is (bRequestType << 8) | bRequest */ trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index); - assert(p->result == 0); switch (request) { case DeviceOutRequest | USB_REQ_SET_ADDRESS: @@ -1074,6 +1073,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, } /* The rest are asynchronous */ + assert(p && p->result == 0); if (length > sizeof(dev->data_buf)) { fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", -- cgit v1.2.3-55-g7522 From 3e4f910c8d490a1490409a7e381dbbb229f9d272 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 6 Sep 2012 11:24:51 +0200 Subject: ehci: switch to new-style memory ops Also register different memory regions for capabilities, operational registers and port status registers. Create separate tracepoints for operational regs and port status regs. Ditch a bunch of sanity checks because the memory core will do this for us now. Offloading the byte, word and dword access handling to the memory core also has the side effect of fixing ehci register access on bigendian hosts. Cc: David Gibson Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 173 ++++++++++++++++++++++++++---------------------------- trace-events | 9 ++- 2 files changed, 90 insertions(+), 92 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 2f3e9c03a1..f5ba8e1469 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -389,6 +389,9 @@ struct EHCIState { USBBus bus; qemu_irq irq; MemoryRegion mem; + MemoryRegion mem_caps; + MemoryRegion mem_opreg; + MemoryRegion mem_ports; int companion_count; /* properties */ @@ -398,10 +401,10 @@ struct EHCIState { * EHCI spec version 1.0 Section 2.3 * Host Controller Operational Registers */ + uint8_t caps[OPREGBASE]; union { - uint8_t mmio[MMIO_SIZE]; + uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)]; struct { - uint8_t cap[OPREGBASE]; uint32_t usbcmd; uint32_t usbsts; uint32_t usbintr; @@ -411,9 +414,9 @@ struct EHCIState { uint32_t asynclistaddr; uint32_t notused[9]; uint32_t configflag; - uint32_t portsc[NB_PORTS]; }; }; + uint32_t portsc[NB_PORTS]; /* * Internal states, shadow registers, etc @@ -471,22 +474,12 @@ static const char *ehci_state_names[] = { }; static const char *ehci_mmio_names[] = { - [CAPLENGTH] = "CAPLENGTH", - [HCIVERSION] = "HCIVERSION", - [HCSPARAMS] = "HCSPARAMS", - [HCCPARAMS] = "HCCPARAMS", [USBCMD] = "USBCMD", [USBSTS] = "USBSTS", [USBINTR] = "USBINTR", [FRINDEX] = "FRINDEX", [PERIODICLISTBASE] = "P-LIST BASE", [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", }; @@ -509,7 +502,8 @@ static const char *state2str(uint32_t state) 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) @@ -1018,7 +1012,7 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[], } s->companion_count++; - s->mmio[0x05] = (s->companion_count << 4) | portcount; + s->caps[0x05] = (s->companion_count << 4) | portcount; return 0; } @@ -1063,7 +1057,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->usbsts = USBSTS_HALT; @@ -1090,50 +1085,35 @@ static void ehci_reset(void *opaque) 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; - uint32_t val; - - val = s->mmio[addr]; - - return val; + return s->caps[addr]; } -static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr) +static uint64_t ehci_opreg_read(void *ptr, target_phys_addr_t addr, + unsigned size) { EHCIState *s = ptr; uint32_t val; - val = s->mmio[addr] | (s->mmio[addr+1] << 8); - + val = s->opreg[addr >> 2]; + trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val); return val; } -static uint32_t ehci_mem_readl(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; 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); + val = s->portsc[addr >> 2]; + trace_usb_ehci_portsc_read(addr + PORTSC_BEGIN, addr >> 2, 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) { USBDevice *dev = s->ports[port].dev; @@ -1162,11 +1142,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 old = *portsc; USBDevice *dev = s->ports[port].dev; + trace_usb_ehci_portsc_write(addr + PORTSC_BEGIN, addr >> 2, val); + /* Clear rwc bits */ *portsc &= ~(val & PORTSC_RWC_MASK); /* The guest may clear, but not set the PED bit */ @@ -1198,39 +1184,20 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val) *portsc &= ~PORTSC_RO_MASK; *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; - uint32_t *mmio = (uint32_t *)(&s->mmio[addr]); + uint32_t *mmio = s->opreg + (addr >> 2); uint32_t old = *mmio; int i; - trace_usb_ehci_mmio_writel(addr, addr2str(addr), val); - - /* Only aligned reads are allowed on OHCI */ - 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; - } - + trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val); - /* Do any register specific pre-write processing here. */ - switch(addr) { + switch (addr + OPREGBASE) { case USBCMD: if (val & USBCMD_HCRESET) { ehci_reset(s); @@ -1241,7 +1208,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 */ if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { fprintf(stderr, "attempt to set frame list size -- value %d\n", - val & USBCMD_FLS); + (int)val & USBCMD_FLS); val &= ~USBCMD_FLS; } @@ -1308,7 +1275,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) } *mmio = val; - trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old); + trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old); } @@ -2520,11 +2487,28 @@ static void ehci_async_bh(void *opaque) ehci_advance_async_state(ehci); } -static const MemoryRegionOps ehci_mem_ops = { - .old_mmio = { - .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl }, - .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel }, - }, +static const MemoryRegionOps ehci_mmio_caps_ops = { + .read = ehci_caps_read, + .valid.min_access_size = 1, + .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, }; @@ -2681,19 +2665,19 @@ static int usb_ehci_initfn(PCIDevice *dev) pci_conf[0x6e] = 0x00; pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS - // 2.2 host controller interface version - s->mmio[0x00] = (uint8_t) OPREGBASE; - s->mmio[0x01] = 0x00; - s->mmio[0x02] = 0x00; - s->mmio[0x03] = 0x01; // HC version - s->mmio[0x04] = NB_PORTS; // Number of downstream ports - s->mmio[0x05] = 0x00; // No companion ports at present - s->mmio[0x06] = 0x00; - s->mmio[0x07] = 0x00; - s->mmio[0x08] = 0x80; // We can cache whole frame, not 64-bit capable - s->mmio[0x09] = 0x68; // EECP - s->mmio[0x0a] = 0x00; - s->mmio[0x0b] = 0x00; + /* 2.2 host controller interface version */ + s->caps[0x00] = (uint8_t) OPREGBASE; + s->caps[0x01] = 0x00; + s->caps[0x02] = 0x00; + s->caps[0x03] = 0x01; /* HC version */ + s->caps[0x04] = NB_PORTS; /* Number of downstream ports */ + s->caps[0x05] = 0x00; /* No companion ports at present */ + s->caps[0x06] = 0x00; + s->caps[0x07] = 0x00; + s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ + s->caps[0x09] = 0x68; /* EECP */ + s->caps[0x0a] = 0x00; + s->caps[0x0b] = 0x00; s->irq = s->dev.irq[3]; @@ -2712,7 +2696,18 @@ static int usb_ehci_initfn(PCIDevice *dev) qemu_register_reset(ehci_reset, s); - memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); + 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); + pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); return 0; diff --git a/trace-events b/trace-events index b25ae1c437..a58b0b71e3 100644 --- a/trace-events +++ b/trace-events @@ -243,9 +243,12 @@ usb_port_release(int bus, const char *port) "bus %d, port %s" # hw/usb/hcd-ehci.c 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_mmio_writel(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_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x" +usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %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_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" -- cgit v1.2.3-55-g7522 From ffa1f2e088eb7e3d57f2fc35f21e7bdb23e592c5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 10 Sep 2012 12:44:10 +0200 Subject: ehci: Fix interrupts stopping when Interrupt Threshold Control is 8 If Interrupt Threshold Control is 8 or a multiple of 8, then s->usbsts_frindex can become exactly 0x4000, at which point (s->usbsts_frindex > s->frindex) will never become true, as s->usbsts_frindex will not be lowered / reset in this case. This patch fixes this. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f5ba8e1469..54273d777b 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2413,7 +2413,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames) if (ehci->frindex == 0x00004000) { ehci_raise_irq(ehci, USBSTS_FLR); ehci->frindex = 0; - if (ehci->usbsts_frindex > 0x00004000) { + if (ehci->usbsts_frindex >= 0x00004000) { ehci->usbsts_frindex -= 0x00004000; } else { ehci->usbsts_frindex = 0; -- cgit v1.2.3-55-g7522 From 8f74ed1e43263293301031a10e440549bab19a6e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 10 Sep 2012 12:44:11 +0200 Subject: ehci: Don't process too much frames in 1 timer tick (v2) The Linux ehci isoc scheduling code fills the entire schedule ahead of time minus 80 frames. If we make a large jump in where we are in the schedule, ie 40 frames, then the scheduler all of a sudden will only have 40 frames left to work in, causing it to fail packet submissions with error -27 (-EFBIG). Changes in v2: -Don't hardcode a maximum number of frames to process in one tick, instead: -Process a minimum number of frames to ensure we do eventually catch up -Stop (after the minimum number) when the guest has requested an irq Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 54273d777b..017a01de8e 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -139,6 +139,7 @@ #define NB_PORTS 6 // Number of downstream ports #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #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 */ @@ -2448,6 +2449,19 @@ static void ehci_frame_timer(void *opaque) } 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_advance_periodic_state(ehci); ehci->last_run_ns += FRAME_TIMER_NS; -- cgit v1.2.3-55-g7522 From 56ab2ad177dc43d474dc0a0bd84e81ef00f31e11 Mon Sep 17 00:00:00 2001 From: Aurelien Jarno Date: Tue, 11 Sep 2012 20:57:58 +0200 Subject: configure: usbredir fixes usbredir is only used by system emulation, so add the libraries to libs_softmmu instead of LIBS. Cc: Michael Tokarev Cc: Gerd Hoffmann Signed-off-by: Aurelien Jarno Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 7656c32b24..e42c812529 100755 --- a/configure +++ b/configure @@ -2763,7 +2763,7 @@ if test "$usb_redir" != "no" ; then usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" - LIBS="$LIBS $usb_redir_libs" + libs_softmmu="$libs_softmmu $usb_redir_libs" else if test "$usb_redir" = "yes"; then feature_not_found "usb-redir" -- cgit v1.2.3-55-g7522 From 8f5457eb04140714eaf57a99bc08dc661d83fa87 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:32 +0200 Subject: ehci: Don't set seen to 0 when removing unseen queue-heads When removing unseen queue-heads from the async queue list, we should not set the seen flag to 0, as this may cause them to be removed by ehci_queues_rip_unused() during the next call to ehci_advance_async_state() if the timer is late or running at a low frequency. Note: 1) This *may* have caused the instant unlink / relinks described in commit 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0 2) Rather then putting more if-s inside ehci_queues_rip_unused, this patch instead introduces a new ehci_queues_rip_unseen function. 3) This patch also makes it save to call ehci_queues_rip_unseen() multiple times, which gets used in the folluw up patch titled: "ehci: Walk async schedule before and after migration" Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 017a01de8e..bc8646081e 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -848,10 +848,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, 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; - 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; EHCIQueue *q, *tmp; @@ -861,13 +861,25 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) q->ts = ehci->last_run_ns; continue; } - if (!flush && ehci->last_run_ns < q->ts + maxage) { + if (ehci->last_run_ns < q->ts + maxage) { continue; } 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) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; @@ -1700,7 +1712,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) 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) */ for(i = 0; i < MAX_QH; i++) { @@ -2332,7 +2344,7 @@ static void ehci_advance_async_state(EHCIState *ehci) */ if (ehci->usbcmd & USBCMD_IAAD) { /* 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(); ehci->usbcmd &= ~USBCMD_IAAD; ehci_raise_irq(ehci, USBSTS_IAA); @@ -2385,7 +2397,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, 0); + ehci_queues_rip_unused(ehci, async); break; default: -- cgit v1.2.3-55-g7522 From ceab6f96454fe6589d1b09ce64403c041d79f9d9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:33 +0200 Subject: ehci: Walk async schedule before and after migration Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index bc8646081e..6a5da8413f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -34,6 +34,7 @@ #include "monitor.h" #include "trace.h" #include "dma.h" +#include "sysemu.h" #define EHCI_DEBUG 0 @@ -2572,6 +2573,32 @@ static int usb_ehci_post_load(void *opaque, int version_id) 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 = { .name = "ehci", .version_id = 2, @@ -2721,6 +2748,7 @@ static int usb_ehci_initfn(PCIDevice *dev) usb_packet_init(&s->ipacket); 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, -- cgit v1.2.3-55-g7522 From 8e60452a954978f2f52026aa5f5cfd948e4e2be0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:34 +0200 Subject: usb-redir: Change cancelled packet code into a generic packet-id queue Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 102 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 31 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5301a69c4f..603262a45c 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -43,7 +43,6 @@ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) -typedef struct Cancelled Cancelled; typedef struct USBRedirDevice USBRedirDevice; /* Struct to hold buffered packets (iso or int input packets) */ @@ -69,6 +68,18 @@ struct endp_data { int 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 { USBDevice dev; /* Properties */ @@ -86,7 +97,7 @@ struct USBRedirDevice { int64_t next_attach_time; struct usbredirparser *parser; struct endp_data endpoint[MAX_ENDPOINTS]; - QTAILQ_HEAD(, Cancelled) cancelled; + struct PacketIdQueue cancelled; /* Data for device filtering */ struct usb_redir_device_connect_header device_info; struct usb_redir_interface_info_header interface_info; @@ -94,11 +105,6 @@ struct USBRedirDevice { 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_device_connect(void *priv, struct usb_redir_device_connect_header *device_connect); @@ -239,37 +245,75 @@ static int usbredir_write(void *priv, uint8_t *data, int count) * Cancelled and buffered packets helpers */ -static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +static void packet_id_queue_init(struct PacketIdQueue *q, + USBRedirDevice *dev, const char *name) { - USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); - Cancelled *c; + 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("cancel packet id %"PRIu64"\n", p->id); + DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name); - c = g_malloc0(sizeof(Cancelled)); - c->id = p->id; - QTAILQ_INSERT_TAIL(&dev->cancelled, c, next); + 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) +{ + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + + packet_id_queue_add(&dev->cancelled, p->id); 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; + return packet_id_queue_remove(&dev->cancelled, id); } static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, @@ -914,7 +958,7 @@ static int usbredir_initfn(USBDevice *udev) 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->cancelled); + packet_id_queue_init(&dev->cancelled, dev, "cancelled"); for (i = 0; i < MAX_ENDPOINTS; i++) { QTAILQ_INIT(&dev->endpoint[i].bufpq); } @@ -933,13 +977,9 @@ static int usbredir_initfn(USBDevice *udev) static void usbredir_cleanup_device_queues(USBRedirDevice *dev) { - Cancelled *c, *next_c; int i; - QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) { - QTAILQ_REMOVE(&dev->cancelled, c, next); - g_free(c); - } + packet_id_queue_empty(&dev->cancelled); for (i = 0; i < MAX_ENDPOINTS; i++) { usbredir_free_bufpq(dev, I2EP(i)); } -- cgit v1.2.3-55-g7522 From 9a8d4067a63702dfb62d0ae3399600a853ad5daa Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:35 +0200 Subject: usb-redir: Add an already_in_flight packet-id queue After a live migration, the usb-hcd will re-queue all packets by walking over the schedule in the guest memory again, but requests which were encountered on the migration source before will already be in flight, so these should *not* be re-send to the usbredir-host. This patch adds an already in flight packet ud queue, which will be filled by the source before migration and then moved over to the migration dest, any async handled packets are then checked against this queue to avoid sending the same packet to the usbredir-host twice. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 603262a45c..f474da80bb 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -98,6 +98,7 @@ struct USBRedirDevice { struct usbredirparser *parser; struct endp_data endpoint[MAX_ENDPOINTS]; struct PacketIdQueue cancelled; + struct PacketIdQueue already_in_flight; /* Data for device filtering */ struct usb_redir_device_connect_header device_info; struct usb_redir_interface_info_header interface_info; @@ -316,6 +317,34 @@ static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) return packet_id_queue_remove(&dev->cancelled, id); } +static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, + struct USBEndpoint *ep) +{ + static USBPacket *p; + + QTAILQ_FOREACH(p, &ep->queue, queue) { + packet_id_queue_add(&dev->already_in_flight, p->id); + } +} + +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, uint8_t ep, uint64_t id) { @@ -531,6 +560,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); + if (usbredir_already_in_flight(dev, p->id)) { + return USB_RET_ASYNC; + } + bulk_packet.endpoint = ep; bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; @@ -611,6 +644,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, DPRINTF("interrupt-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; + } + interrupt_packet.endpoint = ep; interrupt_packet.length = p->iov.size; @@ -753,6 +790,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); 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 */ switch (request) { case DeviceOutRequest | USB_REQ_SET_ADDRESS: @@ -959,6 +1000,7 @@ static int usbredir_initfn(USBDevice *udev) dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); 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++) { QTAILQ_INIT(&dev->endpoint[i].bufpq); } @@ -980,6 +1022,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev) int i; packet_id_queue_empty(&dev->cancelled); + packet_id_queue_empty(&dev->already_in_flight); for (i = 0; i < MAX_ENDPOINTS; i++) { usbredir_free_bufpq(dev, I2EP(i)); } -- cgit v1.2.3-55-g7522 From 3f4be32824ccc5f6a5566b93c900df989a96d048 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:36 +0200 Subject: usb-redir: Store max_packet_size in endp_data So that we've a place to migrate it to / from to allow restoring it after migration. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index f474da80bb..3196665e7f 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -57,6 +57,7 @@ struct endp_data { uint8_t type; uint8_t interval; 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_error; /* For reporting iso errors to the HC */ uint8_t interrupt_started; @@ -1278,7 +1279,8 @@ static void usbredir_ep_info(void *priv, 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]; + 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) { usb_ep->pipeline = true; -- cgit v1.2.3-55-g7522 From fc3f6e1b106abcf6b8cf487ac8f8e5fc2fd86776 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:37 +0200 Subject: usb-redir: Add support for migration Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 346 insertions(+), 3 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 3196665e7f..5e987e99a4 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -65,8 +65,8 @@ struct endp_data { uint8_t bufpq_prefilled; uint8_t bufpq_dropping_packets; QTAILQ_HEAD(, buf_packet) bufpq; - int bufpq_size; - int bufpq_target_size; + int32_t bufpq_size; + int32_t bufpq_target_size; }; struct PacketIdQueueEntry { @@ -240,6 +240,11 @@ static int usbredir_write(void *priv, uint8_t *data, int count) 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); } @@ -858,6 +863,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) { 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) */ usbredir_chardev_close_bh(dev); @@ -893,7 +899,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_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); + + 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); } @@ -939,6 +950,11 @@ static int usbredir_chardev_can_read(void *opaque) 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 */ return 1024 * 1024; } @@ -976,6 +992,15 @@ static void usbredir_chardev_event(void *opaque, int event) * 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) { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); @@ -1014,6 +1039,7 @@ static int usbredir_initfn(USBDevice *udev) qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, 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); return 0; } @@ -1503,6 +1529,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[] = { DEFINE_PROP_CHR("chardev", USBRedirDevice, cs), DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0), @@ -1523,6 +1865,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data) uc->handle_reset = usbredir_handle_reset; uc->handle_data = usbredir_handle_data; uc->handle_control = usbredir_handle_control; + dc->vmsd = &usbredir_vmstate; dc->props = usbredir_properties; } -- cgit v1.2.3-55-g7522 From 09054d19e72f6991cd359d41c479badd92f62a06 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:38 +0200 Subject: usb-redir: Add chardev open / close debug logging Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5e987e99a4..9a4be6d835 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -854,6 +854,7 @@ static void usbredir_chardev_close_bh(void *opaque) usbredir_device_disconnect(dev); if (dev->parser) { + DPRINTF("destroying usbredirparser\n"); usbredirparser_destroy(dev->parser); dev->parser = NULL; } @@ -869,6 +870,8 @@ static void usbredir_chardev_open(USBRedirDevice *dev) usbredir_chardev_close_bh(dev); qemu_bh_cancel(dev->chardev_close_bh); + DPRINTF("creating usbredirparser\n"); + strcpy(version, "qemu usb-redir guest "); pstrcat(version, sizeof(version), qemu_get_version()); @@ -980,9 +983,11 @@ static void usbredir_chardev_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: + DPRINTF("chardev open\n"); usbredir_chardev_open(dev); break; case CHR_EVENT_CLOSED: + DPRINTF("chardev close\n"); qemu_bh_schedule(dev->chardev_close_bh); break; } @@ -1228,6 +1233,7 @@ static void usbredir_device_disconnect(void *priv) qemu_del_timer(dev->attach_timer); if (dev->dev.attached) { + DPRINTF("detaching device\n"); usb_device_detach(&dev->dev); /* * Delay next usb device attach to give the guest a chance to see -- cgit v1.2.3-55-g7522 From 35efba2cc6812dc980c336d7b9bf81dbfb5daf00 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:39 +0200 Subject: usb-redir: Revert usb-redir part of commit 93bfef4c Commit 93bfef4c6e4b23caea9d51e1099d06433d8835a4 makes qemu-devices which report the qemu version string to the guest in some way use a qemu_get_version function which reports a machine-specific version string. However usb-redir does not expose the qemu version to the guest, only to the usbredir-host as part of the initial handshake. This can then be logged on the usbredir-host side for debugging purposes and is otherwise completely unused! For debugging purposes it is important to have the real qemu version in there, rather then the machine-specific version. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 9a4be6d835..b10241a139 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -142,6 +142,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id, static int usbredir_handle_status(USBRedirDevice *dev, int status, int actual_len); +#define VERSION "qemu usb-redir guest " QEMU_VERSION + /* * Logging stuff */ @@ -863,7 +865,6 @@ static void usbredir_chardev_close_bh(void *opaque) static void usbredir_chardev_open(USBRedirDevice *dev) { 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) */ @@ -872,9 +873,6 @@ static void usbredir_chardev_open(USBRedirDevice *dev) DPRINTF("creating usbredirparser\n"); - 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; @@ -906,7 +904,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) if (runstate_check(RUN_STATE_INMIGRATE)) { flags |= usbredirparser_fl_no_hello; } - usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, + usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, flags); usbredirparser_do_write(dev->parser); } -- cgit v1.2.3-55-g7522 From 72a04d0c178f01908d74539230d9de64ffc6da19 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 12 Sep 2012 15:08:40 +0200 Subject: uhci: Don't queue up packets after one with the SPD flag set Don't queue up packets after a packet with the SPD (short packet detect) flag set. Since we won't know if the packet will actually be short until it has completed, and if it is short we should stop the queue. This fixes a miniature photoframe emulating a USB cdrom with the windows software for it not working. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index c7c878635a..cdc8bc3fba 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1000,6 +1000,9 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td) } assert(ret == TD_RESULT_ASYNC_START); assert(int_mask == 0); + if (ptd.ctrl & TD_CTRL_SPD) { + break; + } plink = ptd.link; } } @@ -1097,7 +1100,7 @@ static void uhci_process_frame(UHCIState *s) case TD_RESULT_ASYNC_START: 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); } link = curr_qh ? qh.link : td.link; -- cgit v1.2.3-55-g7522