From 85e05d825f7a9ecc1b5100bdd553fcc682c8366e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 10:10:54 +0200 Subject: xhci: add {get,set}_field macros & enum for pls Add {get,set}_field macros (simliar to ehci) to read and update some bits of a word. Put them into use for updating pls (port link state) values. Also add a enum for pls values. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 7b65741dd5..c062330f8a 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -146,6 +146,21 @@ typedef struct XHCITRB { bool ccs; } XHCITRB; +enum { + PLS_U0 = 0, + PLS_U1 = 1, + PLS_U2 = 2, + PLS_U3 = 3, + PLS_DISABLED = 4, + PLS_RX_DETECT = 5, + PLS_INACTIVE = 6, + PLS_POLLING = 7, + PLS_RECOVERY = 8, + PLS_HOT_RESET = 9, + PLS_COMPILANCE_MODE = 10, + PLS_TEST_MODE = 11, + PLS_RESUME = 15, +}; typedef enum TRBType { TRB_RESERVED = 0, @@ -287,6 +302,16 @@ typedef enum TRBCCode { typedef struct XHCIState XHCIState; +#define get_field(data, field) \ + (((data) >> field##_SHIFT) & field##_MASK) + +#define set_field(data, newval, field) do { \ + uint32_t val = *data; \ + val &= ~(field##_MASK << field##_SHIFT); \ + val |= ((newval) & field##_MASK) << field##_SHIFT; \ + *data = val; \ + } while (0) + typedef enum EPType { ET_INVALID = 0, ET_ISO_OUT, @@ -2334,7 +2359,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) } } - if (xhci_running(xhci)) { + if (xhci_running(port->xhci)) { port->portsc |= PORTSC_CSC; XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, port->portnr << 24}; @@ -2368,7 +2393,7 @@ static void xhci_reset(DeviceState *dev) } for (i = 0; i < xhci->numports; i++) { - xhci_update_port(xhci, xhci->ports + i, 0); + xhci_update_port(xhci->ports + i, 0); } for (i = 0; i < xhci->numintrs; i++) { @@ -2499,8 +2524,8 @@ static void xhci_port_write(void *ptr, hwaddr reg, PORTSC_PRC|PORTSC_PLC|PORTSC_CEC)); if (val & PORTSC_LWS) { /* overwrite PLS only when LWS=1 */ - portsc &= ~(PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); - portsc |= val & (PORTSC_PLS_MASK << PORTSC_PLS_SHIFT); + uint32_t pls = get_field(val, PORTSC_PLS); + set_field(&portsc, pls, PORTSC_PLS); } /* read/write bits */ portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE); @@ -2832,13 +2857,11 @@ static void xhci_wakeup(USBPort *usbport) XHCIPort *port = xhci_lookup_port(xhci, usbport); XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, port->portnr << 24}; - uint32_t pls; - pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; - if (pls != 3) { + if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) { return; } - port->portsc |= 0xf << PORTSC_PLS_SHIFT; + set_field(&port->portsc, PLS_RESUME, PORTSC_PLS); if (port->portsc & PORTSC_PLC) { return; } -- cgit v1.2.3 From f321402785f443d7b95e746aae686e28d74e344c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 10:13:13 +0200 Subject: xhci: s/xhci_update_port/xhci_port_update/ Rename the function for xhci_port_* naming scheme, also drop the xhci parameter as port carries a pointer to xhci anyway. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index c062330f8a..f5ab6928ae 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2337,7 +2337,7 @@ static void xhci_process_commands(XHCIState *xhci) } } -static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) +static void xhci_port_update(XHCIPort *port, int is_detach) { port->portsc = PORTSC_PP; if (port->uport->dev && port->uport->dev->attached && !is_detach && @@ -2363,7 +2363,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) port->portsc |= PORTSC_CSC; XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, port->portnr << 24}; - xhci_event(xhci, &ev, 0); + xhci_event(port->xhci, &ev, 0); DPRINTF("xhci: port change event for port %d\n", port->portnr); } } @@ -2393,7 +2393,7 @@ static void xhci_reset(DeviceState *dev) } for (i = 0; i < xhci->numports; i++) { - xhci_update_port(xhci->ports + i, 0); + xhci_port_update(xhci->ports + i, 0); } for (i = 0; i < xhci->numintrs; i++) { @@ -2840,7 +2840,7 @@ static void xhci_attach(USBPort *usbport) XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); - xhci_update_port(xhci, port, 0); + xhci_port_update(port, 0); } static void xhci_detach(USBPort *usbport) @@ -2848,7 +2848,7 @@ static void xhci_detach(USBPort *usbport) XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); - xhci_update_port(xhci, port, 1); + xhci_port_update(port, 1); } static void xhci_wakeup(USBPort *usbport) -- cgit v1.2.3 From 6a32f80f056b577d275268e4f6f3477ba721c94f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 10:15:47 +0200 Subject: xhci: add xhci_port_have_device Factor out the code which checks whenever a usb device is attached to the port in question. No functional change. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index f5ab6928ae..62cca90a3c 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2337,11 +2337,21 @@ static void xhci_process_commands(XHCIState *xhci) } } +static bool xhci_port_have_device(XHCIPort *port) +{ + if (!port->uport->dev || !port->uport->dev->attached) { + return false; /* no device present */ + } + if (!((1 << port->uport->dev->speed) & port->speedmask)) { + return false; /* speed mismatch */ + } + return true; +} + static void xhci_port_update(XHCIPort *port, int is_detach) { port->portsc = PORTSC_PP; - if (port->uport->dev && port->uport->dev->attached && !is_detach && - (1 << port->uport->dev->speed) & port->speedmask) { + if (!is_detach && xhci_port_have_device(port)) { port->portsc |= PORTSC_CCS; switch (port->uport->dev->speed) { case USB_SPEED_LOW: -- cgit v1.2.3 From f705a362800733c363f0458f75942f8dc61b7abb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 10:19:02 +0200 Subject: xhci: add xhci_port_notify Create a function to notify the guest about port status changes and put it into use. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 62cca90a3c..3af463986d 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2348,6 +2348,21 @@ static bool xhci_port_have_device(XHCIPort *port) return true; } +static void xhci_port_notify(XHCIPort *port, uint32_t bits) +{ + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, + port->portnr << 24 }; + + if ((port->portsc & bits) == bits) { + return; + } + port->portsc |= bits; + if (!xhci_running(port->xhci)) { + return; + } + xhci_event(port->xhci, &ev, 0); +} + static void xhci_port_update(XHCIPort *port, int is_detach) { port->portsc = PORTSC_PP; @@ -2369,13 +2384,7 @@ static void xhci_port_update(XHCIPort *port, int is_detach) } } - if (xhci_running(port->xhci)) { - port->portsc |= PORTSC_CSC; - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, - port->portnr << 24}; - xhci_event(port->xhci, &ev, 0); - DPRINTF("xhci: port change event for port %d\n", port->portnr); - } + xhci_port_notify(port, PORTSC_CSC); } static void xhci_reset(DeviceState *dev) @@ -2865,18 +2874,12 @@ static void xhci_wakeup(USBPort *usbport) { XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, - port->portnr << 24}; if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) { return; } set_field(&port->portsc, PLS_RESUME, PORTSC_PLS); - if (port->portsc & PORTSC_PLC) { - return; - } - port->portsc |= PORTSC_PLC; - xhci_event(xhci, &ev, 0); + xhci_port_notify(port, PORTSC_PLC); } static void xhci_complete(USBPort *port, USBPacket *packet) -- cgit v1.2.3 From 40030130d167f2bf762155a1a7e0073de5545e8b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 10:22:37 +0200 Subject: xhci: add xhci_port_reset Move port reset logic to its own function. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 3af463986d..1db803cda9 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2387,6 +2387,13 @@ static void xhci_port_update(XHCIPort *port, int is_detach) xhci_port_notify(port, PORTSC_CSC); } +static void xhci_port_reset(XHCIPort *port) +{ + DPRINTF("xhci: port %d reset\n", port); + usb_device_reset(port->uport->dev); + port->portsc |= PORTSC_PRC | PORTSC_PED; +} + static void xhci_reset(DeviceState *dev) { XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev.qdev, dev); @@ -2549,13 +2556,11 @@ static void xhci_port_write(void *ptr, hwaddr reg, /* read/write bits */ portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE); portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE)); + port->portsc = portsc; /* write-1-to-start bits */ if (val & PORTSC_PR) { - DPRINTF("xhci: port %d reset\n", port); - usb_device_reset(port->uport->dev); - portsc |= PORTSC_PRC | PORTSC_PED; + xhci_port_reset(port); } - port->portsc = portsc; break; case 0x04: /* PORTPMSC */ case 0x08: /* PORTLI */ -- cgit v1.2.3 From b62b08282d9539a109b02e217d0cc7f7dcdb87ac Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 10:30:53 +0200 Subject: xhci: set pls in xhci_port_update & xhci_port_reset Set the port link state to the correct values in xhci_port_update and xhci_port_reset functions. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 1db803cda9..84d1b26bbb 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2365,33 +2365,55 @@ static void xhci_port_notify(XHCIPort *port, uint32_t bits) static void xhci_port_update(XHCIPort *port, int is_detach) { + uint32_t pls = PLS_RX_DETECT; + port->portsc = PORTSC_PP; if (!is_detach && xhci_port_have_device(port)) { port->portsc |= PORTSC_CCS; switch (port->uport->dev->speed) { case USB_SPEED_LOW: port->portsc |= PORTSC_SPEED_LOW; + pls = PLS_POLLING; break; case USB_SPEED_FULL: port->portsc |= PORTSC_SPEED_FULL; + pls = PLS_POLLING; break; case USB_SPEED_HIGH: port->portsc |= PORTSC_SPEED_HIGH; + pls = PLS_POLLING; break; case USB_SPEED_SUPER: port->portsc |= PORTSC_SPEED_SUPER; + port->portsc |= PORTSC_PED; + pls = PLS_U0; break; } } - + set_field(&port->portsc, pls, PORTSC_PLS); xhci_port_notify(port, PORTSC_CSC); } static void xhci_port_reset(XHCIPort *port) { DPRINTF("xhci: port %d reset\n", port); + if (!xhci_port_have_device(port)) { + return; + } + usb_device_reset(port->uport->dev); - port->portsc |= PORTSC_PRC | PORTSC_PED; + + switch (port->uport->dev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + set_field(&port->portsc, PLS_U0, PORTSC_PLS); + port->portsc |= PORTSC_PED; + break; + } + + port->portsc &= ~PORTSC_PR; + xhci_port_notify(port, PORTSC_PRC); } static void xhci_reset(DeviceState *dev) -- cgit v1.2.3 From 4f47f0f82eccbda44bac929df94fa244bf3452bd Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 11:49:06 +0200 Subject: xhci: add port trace points Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 6 +++++- trace-events | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 84d1b26bbb..325963882d 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2391,12 +2391,14 @@ static void xhci_port_update(XHCIPort *port, int is_detach) } } set_field(&port->portsc, pls, PORTSC_PLS); + trace_usb_xhci_port_link(port->portnr, pls); xhci_port_notify(port, PORTSC_CSC); } static void xhci_port_reset(XHCIPort *port) { - DPRINTF("xhci: port %d reset\n", port); + trace_usb_xhci_port_reset(port->portnr); + if (!xhci_port_have_device(port)) { return; } @@ -2408,6 +2410,7 @@ static void xhci_port_reset(XHCIPort *port) case USB_SPEED_FULL: case USB_SPEED_HIGH: set_field(&port->portsc, PLS_U0, PORTSC_PLS); + trace_usb_xhci_port_link(port->portnr, PLS_U0); port->portsc |= PORTSC_PED; break; } @@ -2574,6 +2577,7 @@ static void xhci_port_write(void *ptr, hwaddr reg, /* overwrite PLS only when LWS=1 */ uint32_t pls = get_field(val, PORTSC_PLS); set_field(&portsc, pls, PORTSC_PLS); + trace_usb_xhci_port_link(port->portnr, pls); } /* read/write bits */ portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE); diff --git a/trace-events b/trace-events index 7ee21e58ea..0cb991e789 100644 --- a/trace-events +++ b/trace-events @@ -333,6 +333,8 @@ usb_xhci_irq_msix_use(uint32_t nr) "nr %d" usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d" usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_port_reset(uint32_t port) "port %d" +usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d" usb_xhci_slot_enable(uint32_t slotid) "slotid %d" usb_xhci_slot_disable(uint32_t slotid) "slotid %d" usb_xhci_slot_address(uint32_t slotid) "slotid %d" -- cgit v1.2.3 From 0bc85da69e01f25c27f575ef0d57d29be4154d71 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 13:09:56 +0200 Subject: xhci: allow address slot being called multiple times win8 guests do that for some reason ... Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 325963882d..900abf5566 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -483,6 +483,8 @@ enum xhci_flags { static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); +static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid); static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); @@ -1075,8 +1077,7 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, slot = &xhci->slots[slotid-1]; if (slot->eps[epid-1]) { - fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid); - return CC_TRB_ERROR; + xhci_disable_ep(xhci, slotid, epid); } epctx = g_malloc(sizeof(XHCIEPContext)); @@ -1919,6 +1920,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, } for (i = 0; i < xhci->numslots; i++) { + if (i == slotid-1) { + continue; + } if (xhci->slots[i].uport == uport) { fprintf(stderr, "xhci: port %s already assigned to slot %d\n", uport->path, i+1); @@ -1936,6 +1940,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, slot->devaddr = xhci->devaddr++; slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr; DPRINTF("xhci: device address is %d\n", slot->devaddr); + usb_device_reset(dev); usb_device_handle_control(dev, NULL, DeviceOutRequest | USB_REQ_SET_ADDRESS, slot->devaddr, 0, 0, NULL); -- cgit v1.2.3 From 27a11324e07f23587abfac3f0fd2fd6fb536aa74 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Mon, 29 Oct 2012 11:34:34 +1000 Subject: usb/ehci: parameterise the register region offsets The capabilities register and operational register offsets can vary from one EHCI implementation to the next. Parameterise accordingly. Signed-off-by: Peter Crosthwaite Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 68 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f14f9d7018..7f04322485 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -48,20 +48,18 @@ #define USB_RET_PROCERR (-99) #define MMIO_SIZE 0x1000 +#define CAPA_SIZE 0x10 /* Capability Registers Base Address - section 2.2 */ -#define CAPREGBASE 0x0000 -#define CAPLENGTH CAPREGBASE + 0x0000 // 1-byte, 0x0001 reserved -#define HCIVERSION CAPREGBASE + 0x0002 // 2-bytes, i/f version # -#define HCSPARAMS CAPREGBASE + 0x0004 // 4-bytes, structural params -#define HCCPARAMS CAPREGBASE + 0x0008 // 4-bytes, capability params +#define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ +#define HCIVERSION 0x0002 /* 2-bytes, i/f version # */ +#define HCSPARAMS 0x0004 /* 4-bytes, structural params */ +#define HCCPARAMS 0x0008 /* 4-bytes, capability params */ #define EECP HCCPARAMS + 1 -#define HCSPPORTROUTE1 CAPREGBASE + 0x000c -#define HCSPPORTROUTE2 CAPREGBASE + 0x0010 +#define HCSPPORTROUTE1 0x000c +#define HCSPPORTROUTE2 0x0010 -#define OPREGBASE 0x0020 // Operational Registers Base Address - -#define USBCMD OPREGBASE + 0x0000 +#define USBCMD 0x0000 #define USBCMD_RUNSTOP (1 << 0) // run / Stop #define USBCMD_HCRESET (1 << 1) // HC Reset #define USBCMD_FLS (3 << 2) // Frame List Size @@ -75,7 +73,7 @@ #define USBCMD_ITC (0x7f << 16) // Int Threshold Control #define USBCMD_ITC_SH 16 // Int Threshold Control Shift -#define USBSTS OPREGBASE + 0x0004 +#define USBSTS 0x0004 #define USBSTS_RO_MASK 0x0000003f #define USBSTS_INT (1 << 0) // USB Interrupt #define USBSTS_ERRINT (1 << 1) // Error Interrupt @@ -92,18 +90,18 @@ * Interrupt enable bits correspond to the interrupt active bits in USBSTS * so no need to redefine here. */ -#define USBINTR OPREGBASE + 0x0008 +#define USBINTR 0x0008 #define USBINTR_MASK 0x0000003f -#define FRINDEX OPREGBASE + 0x000c -#define CTRLDSSEGMENT OPREGBASE + 0x0010 -#define PERIODICLISTBASE OPREGBASE + 0x0014 -#define ASYNCLISTADDR OPREGBASE + 0x0018 +#define FRINDEX 0x000c +#define CTRLDSSEGMENT 0x0010 +#define PERIODICLISTBASE 0x0014 +#define ASYNCLISTADDR 0x0018 #define ASYNCLISTADDR_MASK 0xffffffe0 -#define CONFIGFLAG OPREGBASE + 0x0040 +#define CONFIGFLAG 0x0040 -#define PORTSC (OPREGBASE + 0x0044) +#define PORTSC 0x0044 #define PORTSC_BEGIN PORTSC #define PORTSC_END (PORTSC + 4 * NB_PORTS) /* @@ -395,6 +393,8 @@ struct EHCIState { MemoryRegion mem_opreg; MemoryRegion mem_ports; int companion_count; + uint16_t capsbase; + uint16_t opregbase; /* properties */ uint32_t maxframes; @@ -403,9 +403,9 @@ struct EHCIState { * EHCI spec version 1.0 Section 2.3 * Host Controller Operational Registers */ - uint8_t caps[OPREGBASE]; + uint8_t caps[CAPA_SIZE]; union { - uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)]; + uint32_t opreg[PORTSC_BEGIN/sizeof(uint32_t)]; struct { uint32_t usbcmd; uint32_t usbsts; @@ -506,8 +506,7 @@ static const char *state2str(uint32_t state) static const char *addr2str(hwaddr addr) { - return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), - addr + OPREGBASE); + return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); } static void ehci_trace_usbsts(uint32_t mask, int state) @@ -1115,7 +1114,7 @@ static uint64_t ehci_opreg_read(void *ptr, hwaddr addr, uint32_t val; val = s->opreg[addr >> 2]; - trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val); + trace_usb_ehci_opreg_read(addr + s->opregbase, addr2str(addr), val); return val; } @@ -1211,9 +1210,9 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, uint32_t old = *mmio; int i; - trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val); + trace_usb_ehci_opreg_write(addr + s->opregbase, addr2str(addr), val); - switch (addr + OPREGBASE) { + switch (addr) { case USBCMD: if (val & USBCMD_HCRESET) { ehci_reset(s); @@ -1291,7 +1290,8 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, } *mmio = val; - trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old); + trace_usb_ehci_opreg_change(addr + s->opregbase, addr2str(addr), + *mmio, old); } @@ -2731,8 +2731,11 @@ static int usb_ehci_initfn(PCIDevice *dev) pci_conf[0x6e] = 0x00; pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS + s->capsbase = 0x00; + s->opregbase = 0x20; + /* 2.2 host controller interface version */ - s->caps[0x00] = (uint8_t) OPREGBASE; + s->caps[0x00] = (uint8_t)(s->opregbase - s->capsbase); s->caps[0x01] = 0x00; s->caps[0x02] = 0x00; s->caps[0x03] = 0x01; /* HC version */ @@ -2765,15 +2768,16 @@ static int usb_ehci_initfn(PCIDevice *dev) memory_region_init(&s->mem, "ehci", MMIO_SIZE); memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s, - "capabilities", OPREGBASE); + "capabilities", CAPA_SIZE); memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s, - "operational", PORTSC_BEGIN - OPREGBASE); + "operational", PORTSC_BEGIN); 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_add_subregion(&s->mem, s->capsbase, &s->mem_caps); + memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); + memory_region_add_subregion(&s->mem, s->opregbase + PORTSC_BEGIN, + &s->mem_ports); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); -- cgit v1.2.3 From 7ae6ce0258096d3ad974d274aafcf130d5a31bb7 Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Mon, 29 Oct 2012 11:34:35 +1000 Subject: usb/ehci: Abstract away PCI DMA API Pull the DMAContext for the PCI DMA out at device init time and put it into the device state. Use dma_memory_read/write() instead of pci specific versions. Signed-off-by: Peter Crosthwaite Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 7f04322485..28890b5f84 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -389,6 +389,7 @@ struct EHCIState { USBBus bus; qemu_irq irq; MemoryRegion mem; + DMAContext *dma; MemoryRegion mem_caps; MemoryRegion mem_opreg; MemoryRegion mem_ports; @@ -1304,7 +1305,7 @@ static inline int get_dwords(EHCIState *ehci, uint32_t addr, int i; for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { - pci_dma_read(&ehci->dev, addr, buf, sizeof(*buf)); + dma_memory_read(ehci->dma, addr, buf, sizeof(*buf)); *buf = le32_to_cpu(*buf); } @@ -1319,7 +1320,7 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr, for(i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { uint32_t tmp = cpu_to_le32(*buf); - pci_dma_write(&ehci->dev, addr, &tmp, sizeof(tmp)); + dma_memory_write(ehci->dma, addr, &tmp, sizeof(tmp)); } return 1; @@ -1402,7 +1403,7 @@ static int ehci_init_transfer(EHCIPacket *p) cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; - pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5); + qemu_sglist_init(&p->sgl, 5, p->queue->ehci->dma); while (bytes > 0) { if (cpage > 4) { @@ -1647,7 +1648,7 @@ static int ehci_process_itd(EHCIState *ehci, return USB_RET_PROCERR; } - pci_dma_sglist_init(&ehci->isgl, &ehci->dev, 2); + qemu_sglist_init(&ehci->isgl, 2, ehci->dma); if (off + len > 4096) { /* transfer crosses page border */ uint32_t len2 = off + len - 4096; @@ -2402,7 +2403,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } list |= ((ehci->frindex & 0x1ff8) >> 1); - pci_dma_read(&ehci->dev, list, &entry, sizeof entry); + dma_memory_read(ehci->dma, list, &entry, sizeof entry); entry = le32_to_cpu(entry); DPRINTF("PERIODIC state adv fr=%d. [%08X] -> %08X\n", @@ -2750,6 +2751,8 @@ static int usb_ehci_initfn(PCIDevice *dev) s->irq = s->dev.irq[3]; + s->dma = pci_dma_context(dev); + usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev); for(i = 0; i < NB_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, -- cgit v1.2.3 From 5010d4dc618b6b8e7c21129c487c06f6493f71fc Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Mon, 29 Oct 2012 11:34:36 +1000 Subject: usb/ehci: seperate out PCIisms Seperate the PCI stuff from the EHCI components. Extracted the PCIDevice out into a new wrapper struct to make EHCIState non-PCI-specific. Seperated tho non PCI init component out into a seperate "common" init function. Signed-off-by: Peter Crosthwaite Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 124 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 28890b5f84..59580fc80b 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -385,7 +385,6 @@ struct EHCIQueue { typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; struct EHCIState { - PCIDevice dev; USBBus bus; qemu_irq irq; MemoryRegion mem; @@ -447,6 +446,11 @@ struct EHCIState { bool int_req_by_async; }; +typedef struct EHCIPCIState { + PCIDevice pcidev; + EHCIState ehci; +} EHCIPCIState; + #define SET_LAST_RUN_CLOCK(s) \ (s)->last_run_ns = qemu_get_clock_ns(vm_clock); @@ -2553,7 +2557,7 @@ static const MemoryRegionOps ehci_mmio_port_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int usb_ehci_initfn(PCIDevice *dev); +static int usb_ehci_pci_initfn(PCIDevice *dev); static USBPortOps ehci_port_ops = { .attach = ehci_attach, @@ -2614,12 +2618,11 @@ static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) } static const VMStateDescription vmstate_ehci = { - .name = "ehci", + .name = "ehci-core", .version_id = 2, .minimum_version_id = 1, .post_load = usb_ehci_post_load, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, EHCIState), /* mmio registers */ VMSTATE_UINT32(usbcmd, EHCIState), VMSTATE_UINT32(usbsts, EHCIState), @@ -2650,8 +2653,19 @@ static const VMStateDescription vmstate_ehci = { } }; -static Property ehci_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), +static const VMStateDescription vmstate_ehci_pci = { + .name = "ehci", + .version_id = 2, + .minimum_version_id = 1, + .post_load = usb_ehci_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), + VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), + } +}; + +static Property ehci_pci_properties[] = { + DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), DEFINE_PROP_END_OF_LIST(), }; @@ -2660,13 +2674,13 @@ static void ehci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = usb_ehci_initfn; + k->init = usb_ehci_pci_initfn; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */ k->revision = 0x10; k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_ehci; - dc->props = ehci_properties; + dc->props = ehci_pci_properties; } static TypeInfo ehci_info = { @@ -2681,13 +2695,13 @@ static void ich9_ehci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = usb_ehci_initfn; + k->init = usb_ehci_pci_initfn; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1; k->revision = 0x03; k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_ehci; - dc->props = ehci_properties; + dc->props = ehci_pci_properties; } static TypeInfo ich9_ehci_info = { @@ -2697,44 +2711,10 @@ static TypeInfo ich9_ehci_info = { .class_init = ich9_ehci_class_init, }; -static int usb_ehci_initfn(PCIDevice *dev) +static void usb_ehci_initfn(EHCIState *s, DeviceState *dev) { - EHCIState *s = DO_UPCAST(EHCIState, dev, dev); - uint8_t *pci_conf = s->dev.config; int i; - pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); - - /* capabilities pointer */ - pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); - //pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); - - pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ - pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); - pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); - - // pci_conf[0x50] = 0x01; // power management caps - - pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); // release number (2.1.4) - pci_set_byte(&pci_conf[0x61], 0x20); // frame length adjustment (2.1.5) - pci_set_word(&pci_conf[0x62], 0x00); // port wake up capability (2.1.6) - - pci_conf[0x64] = 0x00; - pci_conf[0x65] = 0x00; - pci_conf[0x66] = 0x00; - pci_conf[0x67] = 0x00; - pci_conf[0x68] = 0x01; - pci_conf[0x69] = 0x00; - pci_conf[0x6a] = 0x00; - pci_conf[0x6b] = 0x00; // USBLEGSUP - pci_conf[0x6c] = 0x00; - pci_conf[0x6d] = 0x00; - pci_conf[0x6e] = 0x00; - pci_conf[0x6f] = 0xc0; // USBLEFCTLSTS - - s->capsbase = 0x00; - s->opregbase = 0x20; - /* 2.2 host controller interface version */ s->caps[0x00] = (uint8_t)(s->opregbase - s->capsbase); s->caps[0x01] = 0x00; @@ -2745,15 +2725,10 @@ static int usb_ehci_initfn(PCIDevice *dev) 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]; - - s->dma = pci_dma_context(dev); - - usb_bus_new(&s->bus, &ehci_bus_ops, &s->dev.qdev); + usb_bus_new(&s->bus, &ehci_bus_ops, dev); for(i = 0; i < NB_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, USB_SPEED_MASK_HIGH); @@ -2781,8 +2756,53 @@ static int usb_ehci_initfn(PCIDevice *dev) memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); memory_region_add_subregion(&s->mem, s->opregbase + PORTSC_BEGIN, &s->mem_ports); +} + +static int usb_ehci_pci_initfn(PCIDevice *dev) +{ + EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); + EHCIState *s = &i->ehci; + uint8_t *pci_conf = dev->config; + + pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); + + /* capabilities pointer */ + pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); + /* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */ + + pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ + pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); + pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); + + /* pci_conf[0x50] = 0x01; *//* power management caps */ + + pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */ + pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */ + pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */ + + pci_conf[0x64] = 0x00; + pci_conf[0x65] = 0x00; + pci_conf[0x66] = 0x00; + pci_conf[0x67] = 0x00; + pci_conf[0x68] = 0x01; + pci_conf[0x69] = 0x00; + pci_conf[0x6a] = 0x00; + pci_conf[0x6b] = 0x00; /* USBLEGSUP */ + pci_conf[0x6c] = 0x00; + pci_conf[0x6d] = 0x00; + pci_conf[0x6e] = 0x00; + pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ + + s->caps[0x09] = 0x68; /* EECP */ + + s->irq = dev->irq[3]; + s->dma = pci_dma_context(dev); + + s->capsbase = 0x00; + s->opregbase = 0x20; - pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); + usb_ehci_initfn(s, DEVICE(dev)); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); return 0; } -- cgit v1.2.3 From 569c7fc8400ea3a7b13589af889003208aaae2ed Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Mon, 29 Oct 2012 11:34:39 +1000 Subject: usb/ehci: Guard definition of EHCI_DEBUG Guard against re-definition of EHCI_DEBUG. Allows for turning on of debug info from configure (using --qemu-extra-cflags="-DEHCI_DEBUG=1") rather than source code hacking. Signed-off-by: Peter Crosthwaite Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 59580fc80b..d3168c954d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -36,7 +36,9 @@ #include "dma.h" #include "sysemu.h" +#ifndef EHCI_DEBUG #define EHCI_DEBUG 0 +#endif #if EHCI_DEBUG #define DPRINTF printf -- cgit v1.2.3 From 0bf96f9457cdaf620bb8195a971bbdcc732ba32b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2012 12:20:06 +0100 Subject: usb/ehci: split into multiple source files Signed-off-by: Gerd Hoffmann --- hw/usb/Makefile.objs | 2 +- hw/usb/hcd-ehci-pci.c | 138 ++++++++++++++++ hw/usb/hcd-ehci.c | 426 +------------------------------------------------- hw/usb/hcd-ehci.h | 320 +++++++++++++++++++++++++++++++++++++ 4 files changed, 462 insertions(+), 424 deletions(-) create mode 100644 hw/usb/hcd-ehci-pci.c create mode 100644 hw/usb/hcd-ehci.h diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 6425c1ff73..5c7b024103 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,6 +1,6 @@ common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o -common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o +common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-y += libhw.o diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c new file mode 100644 index 0000000000..daac41d7f8 --- /dev/null +++ b/hw/usb/hcd-ehci-pci.c @@ -0,0 +1,138 @@ +/* + * QEMU USB EHCI Emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "hw/usb/hcd-ehci.h" +#include "hw/pci.h" + +typedef struct EHCIPCIState { + PCIDevice pcidev; + EHCIState ehci; +} EHCIPCIState; + +static int usb_ehci_pci_initfn(PCIDevice *dev) +{ + EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); + EHCIState *s = &i->ehci; + uint8_t *pci_conf = dev->config; + + pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); + + /* capabilities pointer */ + pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); + /* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */ + + pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ + pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); + pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); + + /* pci_conf[0x50] = 0x01; *//* power management caps */ + + pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */ + pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */ + pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */ + + pci_conf[0x64] = 0x00; + pci_conf[0x65] = 0x00; + pci_conf[0x66] = 0x00; + pci_conf[0x67] = 0x00; + pci_conf[0x68] = 0x01; + pci_conf[0x69] = 0x00; + pci_conf[0x6a] = 0x00; + pci_conf[0x6b] = 0x00; /* USBLEGSUP */ + pci_conf[0x6c] = 0x00; + pci_conf[0x6d] = 0x00; + pci_conf[0x6e] = 0x00; + pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ + + s->caps[0x09] = 0x68; /* EECP */ + + s->irq = dev->irq[3]; + s->dma = pci_dma_context(dev); + + s->capsbase = 0x00; + s->opregbase = 0x20; + + usb_ehci_initfn(s, DEVICE(dev)); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); + + return 0; +} + +static Property ehci_pci_properties[] = { + DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_ehci_pci = { + .name = "ehci", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), + VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), + } +}; + +static void ehci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = usb_ehci_pci_initfn; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */ + k->revision = 0x10; + k->class_id = PCI_CLASS_SERIAL_USB; + dc->vmsd = &vmstate_ehci; + dc->props = ehci_pci_properties; +} + +static TypeInfo ehci_info = { + .name = "usb-ehci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(EHCIState), + .class_init = ehci_class_init, +}; + +static void ich9_ehci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = usb_ehci_pci_initfn; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1; + k->revision = 0x03; + k->class_id = PCI_CLASS_SERIAL_USB; + dc->vmsd = &vmstate_ehci; + dc->props = ehci_pci_properties; +} + +static TypeInfo ich9_ehci_info = { + .name = "ich9-usb-ehci1", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(EHCIState), + .class_init = ich9_ehci_class_init, +}; + +static void ehci_pci_register_types(void) +{ + type_register_static(&ehci_info); + type_register_static(&ich9_ehci_info); +} + +type_init(ehci_pci_register_types) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d3168c954d..d9dc576e7c 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -27,31 +27,11 @@ * along with this program; if not, see . */ -#include "hw/hw.h" -#include "qemu-timer.h" -#include "hw/usb.h" -#include "hw/pci.h" -#include "monitor.h" -#include "trace.h" -#include "dma.h" -#include "sysemu.h" - -#ifndef EHCI_DEBUG -#define EHCI_DEBUG 0 -#endif - -#if EHCI_DEBUG -#define DPRINTF printf -#else -#define DPRINTF(...) -#endif +#include "hw/usb/hcd-ehci.h" /* internal processing - reset HC to try and recover */ #define USB_RET_PROCERR (-99) -#define MMIO_SIZE 0x1000 -#define CAPA_SIZE 0x10 - /* Capability Registers Base Address - section 2.2 */ #define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ #define HCIVERSION 0x0002 /* 2-bytes, i/f version # */ @@ -103,9 +83,6 @@ #define CONFIGFLAG 0x0040 -#define PORTSC 0x0044 -#define PORTSC_BEGIN PORTSC -#define PORTSC_END (PORTSC + 4 * NB_PORTS) /* * Bits that are reserved or are read-only are masked out of values * written to us by software @@ -137,7 +114,6 @@ #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints -#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 @@ -174,285 +150,6 @@ typedef enum { #define NLPTR_TYPE_STITD 2 // split xaction, isoc xfer descriptor #define NLPTR_TYPE_FSTN 3 // frame span traversal node - -/* EHCI spec version 1.0 Section 3.3 - */ -typedef struct EHCIitd { - uint32_t next; - - uint32_t transact[8]; -#define ITD_XACT_ACTIVE (1 << 31) -#define ITD_XACT_DBERROR (1 << 30) -#define ITD_XACT_BABBLE (1 << 29) -#define ITD_XACT_XACTERR (1 << 28) -#define ITD_XACT_LENGTH_MASK 0x0fff0000 -#define ITD_XACT_LENGTH_SH 16 -#define ITD_XACT_IOC (1 << 15) -#define ITD_XACT_PGSEL_MASK 0x00007000 -#define ITD_XACT_PGSEL_SH 12 -#define ITD_XACT_OFFSET_MASK 0x00000fff - - uint32_t bufptr[7]; -#define ITD_BUFPTR_MASK 0xfffff000 -#define ITD_BUFPTR_SH 12 -#define ITD_BUFPTR_EP_MASK 0x00000f00 -#define ITD_BUFPTR_EP_SH 8 -#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f -#define ITD_BUFPTR_DEVADDR_SH 0 -#define ITD_BUFPTR_DIRECTION (1 << 11) -#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff -#define ITD_BUFPTR_MAXPKT_SH 0 -#define ITD_BUFPTR_MULT_MASK 0x00000003 -#define ITD_BUFPTR_MULT_SH 0 -} EHCIitd; - -/* EHCI spec version 1.0 Section 3.4 - */ -typedef struct EHCIsitd { - uint32_t next; // Standard next link pointer - uint32_t epchar; -#define SITD_EPCHAR_IO (1 << 31) -#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 -#define SITD_EPCHAR_PORTNUM_SH 24 -#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 -#define SITD_EPCHAR_HUBADDR_SH 16 -#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 -#define SITD_EPCHAR_EPNUM_SH 8 -#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f - - uint32_t uframe; -#define SITD_UFRAME_CMASK_MASK 0x0000ff00 -#define SITD_UFRAME_CMASK_SH 8 -#define SITD_UFRAME_SMASK_MASK 0x000000ff - - uint32_t results; -#define SITD_RESULTS_IOC (1 << 31) -#define SITD_RESULTS_PGSEL (1 << 30) -#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 -#define SITD_RESULTS_TYBYTES_SH 16 -#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 -#define SITD_RESULTS_CPROGMASK_SH 8 -#define SITD_RESULTS_ACTIVE (1 << 7) -#define SITD_RESULTS_ERR (1 << 6) -#define SITD_RESULTS_DBERR (1 << 5) -#define SITD_RESULTS_BABBLE (1 << 4) -#define SITD_RESULTS_XACTERR (1 << 3) -#define SITD_RESULTS_MISSEDUF (1 << 2) -#define SITD_RESULTS_SPLITXSTATE (1 << 1) - - uint32_t bufptr[2]; -#define SITD_BUFPTR_MASK 0xfffff000 -#define SITD_BUFPTR_CURROFF_MASK 0x00000fff -#define SITD_BUFPTR_TPOS_MASK 0x00000018 -#define SITD_BUFPTR_TPOS_SH 3 -#define SITD_BUFPTR_TCNT_MASK 0x00000007 - - uint32_t backptr; // Standard next link pointer -} EHCIsitd; - -/* EHCI spec version 1.0 Section 3.5 - */ -typedef struct EHCIqtd { - uint32_t next; // Standard next link pointer - uint32_t altnext; // Standard next link pointer - uint32_t token; -#define QTD_TOKEN_DTOGGLE (1 << 31) -#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 -#define QTD_TOKEN_TBYTES_SH 16 -#define QTD_TOKEN_IOC (1 << 15) -#define QTD_TOKEN_CPAGE_MASK 0x00007000 -#define QTD_TOKEN_CPAGE_SH 12 -#define QTD_TOKEN_CERR_MASK 0x00000c00 -#define QTD_TOKEN_CERR_SH 10 -#define QTD_TOKEN_PID_MASK 0x00000300 -#define QTD_TOKEN_PID_SH 8 -#define QTD_TOKEN_ACTIVE (1 << 7) -#define QTD_TOKEN_HALT (1 << 6) -#define QTD_TOKEN_DBERR (1 << 5) -#define QTD_TOKEN_BABBLE (1 << 4) -#define QTD_TOKEN_XACTERR (1 << 3) -#define QTD_TOKEN_MISSEDUF (1 << 2) -#define QTD_TOKEN_SPLITXSTATE (1 << 1) -#define QTD_TOKEN_PING (1 << 0) - - uint32_t bufptr[5]; // Standard buffer pointer -#define QTD_BUFPTR_MASK 0xfffff000 -#define QTD_BUFPTR_SH 12 -} EHCIqtd; - -/* EHCI spec version 1.0 Section 3.6 - */ -typedef struct EHCIqh { - uint32_t next; // Standard next link pointer - - /* endpoint characteristics */ - uint32_t epchar; -#define QH_EPCHAR_RL_MASK 0xf0000000 -#define QH_EPCHAR_RL_SH 28 -#define QH_EPCHAR_C (1 << 27) -#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 -#define QH_EPCHAR_MPLEN_SH 16 -#define QH_EPCHAR_H (1 << 15) -#define QH_EPCHAR_DTC (1 << 14) -#define QH_EPCHAR_EPS_MASK 0x00003000 -#define QH_EPCHAR_EPS_SH 12 -#define EHCI_QH_EPS_FULL 0 -#define EHCI_QH_EPS_LOW 1 -#define EHCI_QH_EPS_HIGH 2 -#define EHCI_QH_EPS_RESERVED 3 - -#define QH_EPCHAR_EP_MASK 0x00000f00 -#define QH_EPCHAR_EP_SH 8 -#define QH_EPCHAR_I (1 << 7) -#define QH_EPCHAR_DEVADDR_MASK 0x0000007f -#define QH_EPCHAR_DEVADDR_SH 0 - - /* endpoint capabilities */ - uint32_t epcap; -#define QH_EPCAP_MULT_MASK 0xc0000000 -#define QH_EPCAP_MULT_SH 30 -#define QH_EPCAP_PORTNUM_MASK 0x3f800000 -#define QH_EPCAP_PORTNUM_SH 23 -#define QH_EPCAP_HUBADDR_MASK 0x007f0000 -#define QH_EPCAP_HUBADDR_SH 16 -#define QH_EPCAP_CMASK_MASK 0x0000ff00 -#define QH_EPCAP_CMASK_SH 8 -#define QH_EPCAP_SMASK_MASK 0x000000ff -#define QH_EPCAP_SMASK_SH 0 - - uint32_t current_qtd; // Standard next link pointer - uint32_t next_qtd; // Standard next link pointer - uint32_t altnext_qtd; -#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e -#define QH_ALTNEXT_NAKCNT_SH 1 - - uint32_t token; // Same as QTD token - uint32_t bufptr[5]; // Standard buffer pointer -#define BUFPTR_CPROGMASK_MASK 0x000000ff -#define BUFPTR_FRAMETAG_MASK 0x0000001f -#define BUFPTR_SBYTES_MASK 0x00000fe0 -#define BUFPTR_SBYTES_SH 5 -} EHCIqh; - -/* EHCI spec version 1.0 Section 3.7 - */ -typedef struct EHCIfstn { - uint32_t next; // Standard next link pointer - uint32_t backptr; // Standard next link pointer -} EHCIfstn; - -typedef struct EHCIPacket EHCIPacket; -typedef struct EHCIQueue EHCIQueue; -typedef struct EHCIState EHCIState; - -enum async_state { - EHCI_ASYNC_NONE = 0, - EHCI_ASYNC_INITIALIZED, - EHCI_ASYNC_INFLIGHT, - EHCI_ASYNC_FINISHED, -}; - -struct EHCIPacket { - EHCIQueue *queue; - QTAILQ_ENTRY(EHCIPacket) next; - - EHCIqtd qtd; /* copy of current QTD (being worked on) */ - uint32_t qtdaddr; /* address QTD read from */ - - USBPacket packet; - QEMUSGList sgl; - int pid; - enum async_state async; - int usb_status; -}; - -struct EHCIQueue { - EHCIState *ehci; - QTAILQ_ENTRY(EHCIQueue) next; - uint32_t seen; - uint64_t ts; - int async; - int transact_ctr; - - /* cached data from guest - needs to be flushed - * when guest removes an entry (doorbell, handshake sequence) - */ - EHCIqh qh; /* copy of current QH (being worked on) */ - uint32_t qhaddr; /* address QH read from */ - uint32_t qtdaddr; /* address QTD read from */ - USBDevice *dev; - QTAILQ_HEAD(pkts_head, EHCIPacket) packets; -}; - -typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; - -struct EHCIState { - USBBus bus; - qemu_irq irq; - MemoryRegion mem; - DMAContext *dma; - MemoryRegion mem_caps; - MemoryRegion mem_opreg; - MemoryRegion mem_ports; - int companion_count; - uint16_t capsbase; - uint16_t opregbase; - - /* properties */ - uint32_t maxframes; - - /* - * EHCI spec version 1.0 Section 2.3 - * Host Controller Operational Registers - */ - uint8_t caps[CAPA_SIZE]; - union { - uint32_t opreg[PORTSC_BEGIN/sizeof(uint32_t)]; - struct { - uint32_t usbcmd; - uint32_t usbsts; - uint32_t usbintr; - uint32_t frindex; - uint32_t ctrldssegment; - uint32_t periodiclistbase; - uint32_t asynclistaddr; - uint32_t notused[9]; - uint32_t configflag; - }; - }; - uint32_t portsc[NB_PORTS]; - - /* - * Internal states, shadow registers, etc - */ - QEMUTimer *frame_timer; - QEMUBH *async_bh; - uint32_t astate; /* Current state in asynchronous schedule */ - uint32_t pstate; /* Current state in periodic schedule */ - USBPort ports[NB_PORTS]; - USBPort *companion_ports[NB_PORTS]; - uint32_t usbsts_pending; - uint32_t usbsts_frindex; - EHCIQueueHead aqueues; - EHCIQueueHead pqueues; - - /* which address to look at next */ - uint32_t a_fetch_addr; - uint32_t p_fetch_addr; - - USBPacket ipacket; - QEMUSGList isgl; - - uint64_t last_run_ns; - uint32_t async_stepdown; - bool int_req_by_async; -}; - -typedef struct EHCIPCIState { - PCIDevice pcidev; - EHCIState ehci; -} EHCIPCIState; - #define SET_LAST_RUN_CLOCK(s) \ (s)->last_run_ns = qemu_get_clock_ns(vm_clock); @@ -2559,8 +2256,6 @@ static const MemoryRegionOps ehci_mmio_port_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int usb_ehci_pci_initfn(PCIDevice *dev); - static USBPortOps ehci_port_ops = { .attach = ehci_attach, .detach = ehci_detach, @@ -2619,7 +2314,7 @@ static void usb_ehci_vm_state_change(void *opaque, int running, RunState state) } } -static const VMStateDescription vmstate_ehci = { +const VMStateDescription vmstate_ehci = { .name = "ehci-core", .version_id = 2, .minimum_version_id = 1, @@ -2655,65 +2350,7 @@ static const VMStateDescription vmstate_ehci = { } }; -static const VMStateDescription vmstate_ehci_pci = { - .name = "ehci", - .version_id = 2, - .minimum_version_id = 1, - .post_load = usb_ehci_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), - VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), - } -}; - -static Property ehci_pci_properties[] = { - DEFINE_PROP_UINT32("maxframes", EHCIPCIState, ehci.maxframes, 128), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ehci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_ehci_pci_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */ - k->revision = 0x10; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_ehci; - dc->props = ehci_pci_properties; -} - -static TypeInfo ehci_info = { - .name = "usb-ehci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIState), - .class_init = ehci_class_init, -}; - -static void ich9_ehci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_ehci_pci_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_ehci; - dc->props = ehci_pci_properties; -} - -static TypeInfo ich9_ehci_info = { - .name = "ich9-usb-ehci1", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIState), - .class_init = ich9_ehci_class_init, -}; - -static void usb_ehci_initfn(EHCIState *s, DeviceState *dev) +void usb_ehci_initfn(EHCIState *s, DeviceState *dev) { int i; @@ -2760,63 +2397,6 @@ static void usb_ehci_initfn(EHCIState *s, DeviceState *dev) &s->mem_ports); } -static int usb_ehci_pci_initfn(PCIDevice *dev) -{ - EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); - EHCIState *s = &i->ehci; - uint8_t *pci_conf = dev->config; - - pci_set_byte(&pci_conf[PCI_CLASS_PROG], 0x20); - - /* capabilities pointer */ - pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x00); - /* pci_set_byte(&pci_conf[PCI_CAPABILITY_LIST], 0x50); */ - - pci_set_byte(&pci_conf[PCI_INTERRUPT_PIN], 4); /* interrupt pin D */ - pci_set_byte(&pci_conf[PCI_MIN_GNT], 0); - pci_set_byte(&pci_conf[PCI_MAX_LAT], 0); - - /* pci_conf[0x50] = 0x01; *//* power management caps */ - - pci_set_byte(&pci_conf[USB_SBRN], USB_RELEASE_2); /* release # (2.1.4) */ - pci_set_byte(&pci_conf[0x61], 0x20); /* frame length adjustment (2.1.5) */ - pci_set_word(&pci_conf[0x62], 0x00); /* port wake up capability (2.1.6) */ - - pci_conf[0x64] = 0x00; - pci_conf[0x65] = 0x00; - pci_conf[0x66] = 0x00; - pci_conf[0x67] = 0x00; - pci_conf[0x68] = 0x01; - pci_conf[0x69] = 0x00; - pci_conf[0x6a] = 0x00; - pci_conf[0x6b] = 0x00; /* USBLEGSUP */ - pci_conf[0x6c] = 0x00; - pci_conf[0x6d] = 0x00; - pci_conf[0x6e] = 0x00; - pci_conf[0x6f] = 0xc0; /* USBLEFCTLSTS */ - - s->caps[0x09] = 0x68; /* EECP */ - - s->irq = dev->irq[3]; - s->dma = pci_dma_context(dev); - - s->capsbase = 0x00; - s->opregbase = 0x20; - - usb_ehci_initfn(s, DEVICE(dev)); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); - - return 0; -} - -static void ehci_register_types(void) -{ - type_register_static(&ehci_info); - type_register_static(&ich9_ehci_info); -} - -type_init(ehci_register_types) - /* * vim: expandtab ts=4 */ diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h new file mode 100644 index 0000000000..0ec675c352 --- /dev/null +++ b/hw/usb/hcd-ehci.h @@ -0,0 +1,320 @@ +/* + * QEMU USB EHCI Emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "hw/hw.h" +#include "qemu-timer.h" +#include "hw/usb.h" +#include "monitor.h" +#include "trace.h" +#include "dma.h" +#include "sysemu.h" + +#ifndef EHCI_DEBUG +#define EHCI_DEBUG 0 +#endif + +#if EHCI_DEBUG +#define DPRINTF printf +#else +#define DPRINTF(...) +#endif + +#define MMIO_SIZE 0x1000 +#define CAPA_SIZE 0x10 + +#define PORTSC 0x0044 +#define PORTSC_BEGIN PORTSC +#define PORTSC_END (PORTSC + 4 * NB_PORTS) + +#define NB_PORTS 6 /* Number of downstream ports */ + +typedef struct EHCIPacket EHCIPacket; +typedef struct EHCIQueue EHCIQueue; +typedef struct EHCIState EHCIState; + +/* EHCI spec version 1.0 Section 3.3 + */ +typedef struct EHCIitd { + uint32_t next; + + uint32_t transact[8]; +#define ITD_XACT_ACTIVE (1 << 31) +#define ITD_XACT_DBERROR (1 << 30) +#define ITD_XACT_BABBLE (1 << 29) +#define ITD_XACT_XACTERR (1 << 28) +#define ITD_XACT_LENGTH_MASK 0x0fff0000 +#define ITD_XACT_LENGTH_SH 16 +#define ITD_XACT_IOC (1 << 15) +#define ITD_XACT_PGSEL_MASK 0x00007000 +#define ITD_XACT_PGSEL_SH 12 +#define ITD_XACT_OFFSET_MASK 0x00000fff + + uint32_t bufptr[7]; +#define ITD_BUFPTR_MASK 0xfffff000 +#define ITD_BUFPTR_SH 12 +#define ITD_BUFPTR_EP_MASK 0x00000f00 +#define ITD_BUFPTR_EP_SH 8 +#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f +#define ITD_BUFPTR_DEVADDR_SH 0 +#define ITD_BUFPTR_DIRECTION (1 << 11) +#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff +#define ITD_BUFPTR_MAXPKT_SH 0 +#define ITD_BUFPTR_MULT_MASK 0x00000003 +#define ITD_BUFPTR_MULT_SH 0 +} EHCIitd; + +/* EHCI spec version 1.0 Section 3.4 + */ +typedef struct EHCIsitd { + uint32_t next; /* Standard next link pointer */ + uint32_t epchar; +#define SITD_EPCHAR_IO (1 << 31) +#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000 +#define SITD_EPCHAR_PORTNUM_SH 24 +#define SITD_EPCHAR_HUBADD_MASK 0x007f0000 +#define SITD_EPCHAR_HUBADDR_SH 16 +#define SITD_EPCHAR_EPNUM_MASK 0x00000f00 +#define SITD_EPCHAR_EPNUM_SH 8 +#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f + + uint32_t uframe; +#define SITD_UFRAME_CMASK_MASK 0x0000ff00 +#define SITD_UFRAME_CMASK_SH 8 +#define SITD_UFRAME_SMASK_MASK 0x000000ff + + uint32_t results; +#define SITD_RESULTS_IOC (1 << 31) +#define SITD_RESULTS_PGSEL (1 << 30) +#define SITD_RESULTS_TBYTES_MASK 0x03ff0000 +#define SITD_RESULTS_TYBYTES_SH 16 +#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00 +#define SITD_RESULTS_CPROGMASK_SH 8 +#define SITD_RESULTS_ACTIVE (1 << 7) +#define SITD_RESULTS_ERR (1 << 6) +#define SITD_RESULTS_DBERR (1 << 5) +#define SITD_RESULTS_BABBLE (1 << 4) +#define SITD_RESULTS_XACTERR (1 << 3) +#define SITD_RESULTS_MISSEDUF (1 << 2) +#define SITD_RESULTS_SPLITXSTATE (1 << 1) + + uint32_t bufptr[2]; +#define SITD_BUFPTR_MASK 0xfffff000 +#define SITD_BUFPTR_CURROFF_MASK 0x00000fff +#define SITD_BUFPTR_TPOS_MASK 0x00000018 +#define SITD_BUFPTR_TPOS_SH 3 +#define SITD_BUFPTR_TCNT_MASK 0x00000007 + + uint32_t backptr; /* Standard next link pointer */ +} EHCIsitd; + +/* EHCI spec version 1.0 Section 3.5 + */ +typedef struct EHCIqtd { + uint32_t next; /* Standard next link pointer */ + uint32_t altnext; /* Standard next link pointer */ + uint32_t token; +#define QTD_TOKEN_DTOGGLE (1 << 31) +#define QTD_TOKEN_TBYTES_MASK 0x7fff0000 +#define QTD_TOKEN_TBYTES_SH 16 +#define QTD_TOKEN_IOC (1 << 15) +#define QTD_TOKEN_CPAGE_MASK 0x00007000 +#define QTD_TOKEN_CPAGE_SH 12 +#define QTD_TOKEN_CERR_MASK 0x00000c00 +#define QTD_TOKEN_CERR_SH 10 +#define QTD_TOKEN_PID_MASK 0x00000300 +#define QTD_TOKEN_PID_SH 8 +#define QTD_TOKEN_ACTIVE (1 << 7) +#define QTD_TOKEN_HALT (1 << 6) +#define QTD_TOKEN_DBERR (1 << 5) +#define QTD_TOKEN_BABBLE (1 << 4) +#define QTD_TOKEN_XACTERR (1 << 3) +#define QTD_TOKEN_MISSEDUF (1 << 2) +#define QTD_TOKEN_SPLITXSTATE (1 << 1) +#define QTD_TOKEN_PING (1 << 0) + + uint32_t bufptr[5]; /* Standard buffer pointer */ +#define QTD_BUFPTR_MASK 0xfffff000 +#define QTD_BUFPTR_SH 12 +} EHCIqtd; + +/* EHCI spec version 1.0 Section 3.6 + */ +typedef struct EHCIqh { + uint32_t next; /* Standard next link pointer */ + + /* endpoint characteristics */ + uint32_t epchar; +#define QH_EPCHAR_RL_MASK 0xf0000000 +#define QH_EPCHAR_RL_SH 28 +#define QH_EPCHAR_C (1 << 27) +#define QH_EPCHAR_MPLEN_MASK 0x07FF0000 +#define QH_EPCHAR_MPLEN_SH 16 +#define QH_EPCHAR_H (1 << 15) +#define QH_EPCHAR_DTC (1 << 14) +#define QH_EPCHAR_EPS_MASK 0x00003000 +#define QH_EPCHAR_EPS_SH 12 +#define EHCI_QH_EPS_FULL 0 +#define EHCI_QH_EPS_LOW 1 +#define EHCI_QH_EPS_HIGH 2 +#define EHCI_QH_EPS_RESERVED 3 + +#define QH_EPCHAR_EP_MASK 0x00000f00 +#define QH_EPCHAR_EP_SH 8 +#define QH_EPCHAR_I (1 << 7) +#define QH_EPCHAR_DEVADDR_MASK 0x0000007f +#define QH_EPCHAR_DEVADDR_SH 0 + + /* endpoint capabilities */ + uint32_t epcap; +#define QH_EPCAP_MULT_MASK 0xc0000000 +#define QH_EPCAP_MULT_SH 30 +#define QH_EPCAP_PORTNUM_MASK 0x3f800000 +#define QH_EPCAP_PORTNUM_SH 23 +#define QH_EPCAP_HUBADDR_MASK 0x007f0000 +#define QH_EPCAP_HUBADDR_SH 16 +#define QH_EPCAP_CMASK_MASK 0x0000ff00 +#define QH_EPCAP_CMASK_SH 8 +#define QH_EPCAP_SMASK_MASK 0x000000ff +#define QH_EPCAP_SMASK_SH 0 + + uint32_t current_qtd; /* Standard next link pointer */ + uint32_t next_qtd; /* Standard next link pointer */ + uint32_t altnext_qtd; +#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e +#define QH_ALTNEXT_NAKCNT_SH 1 + + uint32_t token; /* Same as QTD token */ + uint32_t bufptr[5]; /* Standard buffer pointer */ +#define BUFPTR_CPROGMASK_MASK 0x000000ff +#define BUFPTR_FRAMETAG_MASK 0x0000001f +#define BUFPTR_SBYTES_MASK 0x00000fe0 +#define BUFPTR_SBYTES_SH 5 +} EHCIqh; + +/* EHCI spec version 1.0 Section 3.7 + */ +typedef struct EHCIfstn { + uint32_t next; /* Standard next link pointer */ + uint32_t backptr; /* Standard next link pointer */ +} EHCIfstn; + +enum async_state { + EHCI_ASYNC_NONE = 0, + EHCI_ASYNC_INITIALIZED, + EHCI_ASYNC_INFLIGHT, + EHCI_ASYNC_FINISHED, +}; + +struct EHCIPacket { + EHCIQueue *queue; + QTAILQ_ENTRY(EHCIPacket) next; + + EHCIqtd qtd; /* copy of current QTD (being worked on) */ + uint32_t qtdaddr; /* address QTD read from */ + + USBPacket packet; + QEMUSGList sgl; + int pid; + enum async_state async; + int usb_status; +}; + +struct EHCIQueue { + EHCIState *ehci; + QTAILQ_ENTRY(EHCIQueue) next; + uint32_t seen; + uint64_t ts; + int async; + int transact_ctr; + + /* cached data from guest - needs to be flushed + * when guest removes an entry (doorbell, handshake sequence) + */ + EHCIqh qh; /* copy of current QH (being worked on) */ + uint32_t qhaddr; /* address QH read from */ + uint32_t qtdaddr; /* address QTD read from */ + USBDevice *dev; + QTAILQ_HEAD(pkts_head, EHCIPacket) packets; +}; + +typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; + +struct EHCIState { + USBBus bus; + qemu_irq irq; + MemoryRegion mem; + DMAContext *dma; + MemoryRegion mem_caps; + MemoryRegion mem_opreg; + MemoryRegion mem_ports; + int companion_count; + uint16_t capsbase; + uint16_t opregbase; + + /* properties */ + uint32_t maxframes; + + /* + * EHCI spec version 1.0 Section 2.3 + * Host Controller Operational Registers + */ + uint8_t caps[CAPA_SIZE]; + union { + uint32_t opreg[PORTSC_BEGIN/sizeof(uint32_t)]; + struct { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t notused[9]; + uint32_t configflag; + }; + }; + uint32_t portsc[NB_PORTS]; + + /* + * Internal states, shadow registers, etc + */ + QEMUTimer *frame_timer; + QEMUBH *async_bh; + uint32_t astate; /* Current state in asynchronous schedule */ + uint32_t pstate; /* Current state in periodic schedule */ + USBPort ports[NB_PORTS]; + USBPort *companion_ports[NB_PORTS]; + uint32_t usbsts_pending; + uint32_t usbsts_frindex; + EHCIQueueHead aqueues; + EHCIQueueHead pqueues; + + /* which address to look at next */ + uint32_t a_fetch_addr; + uint32_t p_fetch_addr; + + USBPacket ipacket; + QEMUSGList isgl; + + uint64_t last_run_ns; + uint32_t async_stepdown; + bool int_req_by_async; +}; + +extern const VMStateDescription vmstate_ehci; + +void usb_ehci_initfn(EHCIState *s, DeviceState *dev); -- cgit v1.2.3 From e433785a768391795e88fbbdd75bbd34859666d3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2012 15:08:37 +0100 Subject: usb/ehci: add sysbus variant Signed-off-by: Gerd Hoffmann --- hw/usb/Makefile.objs | 2 +- hw/usb/hcd-ehci-sysbus.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 hw/usb/hcd-ehci-sysbus.c diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 5c7b024103..7669938abd 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,6 +1,6 @@ common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o -common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o +common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-y += libhw.o diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c new file mode 100644 index 0000000000..1584079796 --- /dev/null +++ b/hw/usb/hcd-ehci-sysbus.c @@ -0,0 +1,77 @@ +/* + * QEMU USB EHCI Emulation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "hw/usb/hcd-ehci.h" +#include "hw/sysbus.h" + +typedef struct EHCISysBusState { + SysBusDevice busdev; + EHCIState ehci; +} EHCISysBusState; + +static const VMStateDescription vmstate_ehci_sysbus = { + .name = "ehci-sysbus", + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState), + VMSTATE_END_OF_LIST() + } +}; + +static Property ehci_sysbus_properties[] = { + DEFINE_PROP_UINT32("maxframes", EHCISysBusState, ehci.maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static int usb_ehci_sysbus_initfn(SysBusDevice *dev) +{ + EHCISysBusState *i = FROM_SYSBUS(EHCISysBusState, dev); + EHCIState *s = &i->ehci; + + s->capsbase = 0x100; + s->opregbase = 0x140; + + usb_ehci_initfn(s, DEVICE(dev)); + sysbus_init_irq(dev, &s->irq); + sysbus_init_mmio(dev, &s->mem); + return 0; +} + +static void ehci_sysbus_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = usb_ehci_sysbus_initfn; + dc->vmsd = &vmstate_ehci_sysbus; + dc->props = ehci_sysbus_properties; +} + +TypeInfo ehci_xlnx_type_info = { + .name = "xlnx,ps7-usb", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(EHCISysBusState), + .class_init = ehci_sysbus_class_init, +}; + +static void ehci_sysbus_register_types(void) +{ + type_register_static(&ehci_xlnx_type_info); +} + +type_init(ehci_sysbus_register_types) -- cgit v1.2.3 From 892776ce02e3915b8db9d85935ea144ee5a1a41a Mon Sep 17 00:00:00 2001 From: Peter Crosthwaite Date: Mon, 29 Oct 2012 11:34:38 +1000 Subject: xilinx_zynq: add USB controllers Add the two usb controllers in Zynq. Signed-off-by: Peter Crosthwaite Signed-off-by: Gerd Hoffmann --- hw/xilinx_zynq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/xilinx_zynq.c b/hw/xilinx_zynq.c index 00262350b6..1f12a3d1ad 100644 --- a/hw/xilinx_zynq.c +++ b/hw/xilinx_zynq.c @@ -166,6 +166,9 @@ static void zynq_init(QEMUMachineInitArgs *args) zynq_init_spi_flashes(0xE0007000, pic[81-IRQ_OFFSET], false); zynq_init_spi_flashes(0xE000D000, pic[51-IRQ_OFFSET], true); + sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); + sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[75-IRQ_OFFSET]); + sysbus_create_simple("cadence_uart", 0xE0000000, pic[59-IRQ_OFFSET]); sysbus_create_simple("cadence_uart", 0xE0001000, pic[82-IRQ_OFFSET]); -- cgit v1.2.3 From 2c2e852509de3fecf606e8f2dcbca0ebd1eddc10 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 25 Oct 2012 16:22:57 +0200 Subject: uhci: dynamic type generation Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 196 +++++++++++++++++++----------------------------------- 1 file changed, 70 insertions(+), 126 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index b6b972fa92..925c738e7d 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -88,6 +88,16 @@ enum { typedef struct UHCIState UHCIState; typedef struct UHCIAsync UHCIAsync; typedef struct UHCIQueue UHCIQueue; +typedef struct UHCIInfo UHCIInfo; + +struct UHCIInfo { + const char *name; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + int (*initfn)(PCIDevice *dev); + bool unplug; +}; /* * Pending async transaction. @@ -1293,143 +1303,77 @@ static Property uhci_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void piix3_uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->exit = usb_uhci_exit; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371SB_2; - k->revision = 0x01; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo piix3_uhci_info = { - .name = "piix3-usb-uhci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = piix3_uhci_class_init, -}; - -static void piix4_uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->exit = usb_uhci_exit; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB_2; - k->revision = 0x01; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo piix4_uhci_info = { - .name = "piix4-usb-uhci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = piix4_uhci_class_init, -}; - -static void vt82c686b_uhci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_vt82c686b_initfn; - k->exit = usb_uhci_exit; - k->vendor_id = PCI_VENDOR_ID_VIA; - k->device_id = PCI_DEVICE_ID_VIA_UHCI; - k->revision = 0x01; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo vt82c686b_uhci_info = { - .name = "vt82c686b-usb-uhci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = vt82c686b_uhci_class_init, -}; - -static void ich9_uhci1_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo ich9_uhci1_info = { - .name = "ich9-usb-uhci1", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = ich9_uhci1_class_init, -}; - -static void ich9_uhci2_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_uhci; - dc->props = uhci_properties; -} - -static TypeInfo ich9_uhci2_info = { - .name = "ich9-usb-uhci2", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = ich9_uhci2_class_init, -}; - -static void ich9_uhci3_class_init(ObjectClass *klass, void *data) +static void uhci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_uhci_common_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; + UHCIInfo *info = data; + + k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; + k->exit = info->unplug ? usb_uhci_exit : NULL; + k->vendor_id = info->vendor_id; + k->device_id = info->device_id; + k->revision = info->revision; + k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_uhci; dc->props = uhci_properties; } -static TypeInfo ich9_uhci3_info = { - .name = "ich9-usb-uhci3", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(UHCIState), - .class_init = ich9_uhci3_class_init, +static UHCIInfo uhci_info[] = { + { + .name = "piix3-usb-uhci", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, + .revision = 0x01, + .unplug = true, + },{ + .name = "piix4-usb-uhci", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, + .revision = 0x01, + .unplug = true, + },{ + .name = "vt82c686b-usb-uhci", + .vendor_id = PCI_VENDOR_ID_VIA, + .device_id = PCI_DEVICE_ID_VIA_UHCI, + .revision = 0x01, + .initfn = usb_uhci_vt82c686b_initfn, + .unplug = true, + },{ + .name = "ich9-usb-uhci1", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, + .revision = 0x03, + .unplug = false, + },{ + .name = "ich9-usb-uhci2", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, + .revision = 0x03, + .unplug = false, + },{ + .name = "ich9-usb-uhci3", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, + .revision = 0x03, + .unplug = false, + } }; static void uhci_register_types(void) { - type_register_static(&piix3_uhci_info); - type_register_static(&piix4_uhci_info); - type_register_static(&vt82c686b_uhci_info); - type_register_static(&ich9_uhci1_info); - type_register_static(&ich9_uhci2_info); - type_register_static(&ich9_uhci3_info); + TypeInfo uhci_type_info = { + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(UHCIState), + .class_init = uhci_class_init, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(uhci_info); i++) { + uhci_type_info.name = uhci_info[i].name; + uhci_type_info.class_data = uhci_info + i; + type_register(&uhci_type_info); + } } type_init(uhci_register_types) -- cgit v1.2.3 From 8f3f90b0c7d095ba11b1ad558a0b7c2eeb36c8dc Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 26 Oct 2012 14:56:19 +0200 Subject: uhci: stick irq routing info into UHCIInfo too. Kills the ugly "switch (device_id) { ... }" struct and makes it easier to figure what the differences between the uhci variants are. Need our own DeviceClass struct for that so we can allocate some space to store UHCIInfo. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 925c738e7d..27046b65c8 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -89,16 +89,23 @@ typedef struct UHCIState UHCIState; typedef struct UHCIAsync UHCIAsync; typedef struct UHCIQueue UHCIQueue; typedef struct UHCIInfo UHCIInfo; +typedef struct UHCIPCIDeviceClass UHCIPCIDeviceClass; struct UHCIInfo { const char *name; uint16_t vendor_id; uint16_t device_id; uint8_t revision; + uint8_t irq_pin; int (*initfn)(PCIDevice *dev); bool unplug; }; +struct UHCIPCIDeviceClass { + PCIDeviceClass parent_class; + UHCIInfo info; +}; + /* * Pending async transaction. * 'packet' must be the first field because completion @@ -1218,6 +1225,7 @@ static USBBusOps uhci_bus_ops = { static int usb_uhci_common_initfn(PCIDevice *dev) { PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + UHCIPCIDeviceClass *u = container_of(pc, UHCIPCIDeviceClass, parent_class); UHCIState *s = DO_UPCAST(UHCIState, dev, dev); uint8_t *pci_conf = s->dev.config; int i; @@ -1226,20 +1234,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) /* TODO: reset value should be 0. */ pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - switch (pc->device_id) { - case PCI_DEVICE_ID_INTEL_82801I_UHCI1: - s->irq_pin = 0; /* A */ - break; - case PCI_DEVICE_ID_INTEL_82801I_UHCI2: - s->irq_pin = 1; /* B */ - break; - case PCI_DEVICE_ID_INTEL_82801I_UHCI3: - s->irq_pin = 2; /* C */ - break; - default: - s->irq_pin = 3; /* D */ - break; - } + s->irq_pin = u->info.irq_pin; pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1); if (s->masterbus) { @@ -1307,6 +1302,7 @@ static void uhci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + UHCIPCIDeviceClass *u = container_of(k, UHCIPCIDeviceClass, parent_class); UHCIInfo *info = data; k->init = info->initfn ? info->initfn : usb_uhci_common_initfn; @@ -1317,6 +1313,7 @@ static void uhci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_uhci; dc->props = uhci_properties; + u->info = *info; } static UHCIInfo uhci_info[] = { @@ -1325,18 +1322,21 @@ static UHCIInfo uhci_info[] = { .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, .revision = 0x01, + .irq_pin = 3, .unplug = true, },{ .name = "piix4-usb-uhci", .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, .revision = 0x01, + .irq_pin = 3, .unplug = true, },{ .name = "vt82c686b-usb-uhci", .vendor_id = PCI_VENDOR_ID_VIA, .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, + .irq_pin = 3, .initfn = usb_uhci_vt82c686b_initfn, .unplug = true, },{ @@ -1344,18 +1344,21 @@ static UHCIInfo uhci_info[] = { .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, .revision = 0x03, + .irq_pin = 0, .unplug = false, },{ .name = "ich9-usb-uhci2", .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, .revision = 0x03, + .irq_pin = 1, .unplug = false, },{ .name = "ich9-usb-uhci3", .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, .revision = 0x03, + .irq_pin = 2, .unplug = false, } }; @@ -1365,6 +1368,7 @@ static void uhci_register_types(void) TypeInfo uhci_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(UHCIState), + .class_size = sizeof(UHCIPCIDeviceClass), .class_init = uhci_class_init, }; int i; -- cgit v1.2.3 From 74625ea27c39df7047ebefb11c4a04c3a3513f16 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2012 09:57:28 +0100 Subject: uhci: add ich9 00:1a.* variants Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 27046b65c8..71263fe143 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1340,26 +1340,47 @@ static UHCIInfo uhci_info[] = { .initfn = usb_uhci_vt82c686b_initfn, .unplug = true, },{ - .name = "ich9-usb-uhci1", + .name = "ich9-usb-uhci1", /* 00:1d.0 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, .revision = 0x03, .irq_pin = 0, .unplug = false, },{ - .name = "ich9-usb-uhci2", + .name = "ich9-usb-uhci2", /* 00:1d.1 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, .revision = 0x03, .irq_pin = 1, .unplug = false, },{ - .name = "ich9-usb-uhci3", + .name = "ich9-usb-uhci3", /* 00:1d.2 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, .revision = 0x03, .irq_pin = 2, .unplug = false, + },{ + .name = "ich9-usb-uhci4", /* 00:1a.0 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI4, + .revision = 0x03, + .irq_pin = 0, + .unplug = false, + },{ + .name = "ich9-usb-uhci5", /* 00:1a.1 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI5, + .revision = 0x03, + .irq_pin = 1, + .unplug = false, + },{ + .name = "ich9-usb-uhci6", /* 00:1a.2 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI6, + .revision = 0x03, + .irq_pin = 2, + .unplug = false, } }; -- cgit v1.2.3 From df013187777efc62faeea979cfec2ca4470cc34b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2012 12:53:17 +0100 Subject: usb/ehci-pci: dynamic type generation Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci-pci.c | 66 +++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index daac41d7f8..df137cccc9 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -23,6 +23,13 @@ typedef struct EHCIPCIState { EHCIState ehci; } EHCIPCIState; +typedef struct EHCIPCIInfo { + const char *name; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; +} EHCIPCIInfo; + static int usb_ehci_pci_initfn(PCIDevice *dev) { EHCIPCIState *i = DO_UPCAST(EHCIPCIState, pcidev, dev); @@ -91,48 +98,45 @@ static void ehci_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + EHCIPCIInfo *i = data; k->init = usb_ehci_pci_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801D; /* ich4 */ - k->revision = 0x10; + k->vendor_id = i->vendor_id; + k->device_id = i->device_id; + k->revision = i->revision; k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_ehci; dc->props = ehci_pci_properties; } -static TypeInfo ehci_info = { - .name = "usb-ehci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIState), - .class_init = ehci_class_init, -}; - -static void ich9_ehci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = usb_ehci_pci_initfn; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1; - k->revision = 0x03; - k->class_id = PCI_CLASS_SERIAL_USB; - dc->vmsd = &vmstate_ehci; - dc->props = ehci_pci_properties; -} - -static TypeInfo ich9_ehci_info = { - .name = "ich9-usb-ehci1", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(EHCIState), - .class_init = ich9_ehci_class_init, +static struct EHCIPCIInfo ehci_pci_info[] = { + { + .name = "usb-ehci", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ + .revision = 0x10, + },{ + .name = "ich9-usb-ehci1", + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, + .revision = 0x03, + } }; static void ehci_pci_register_types(void) { - type_register_static(&ehci_info); - type_register_static(&ich9_ehci_info); + TypeInfo ehci_type_info = { + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(EHCIPCIState), + .class_init = ehci_class_init, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(ehci_pci_info); i++) { + ehci_type_info.name = ehci_pci_info[i].name; + ehci_type_info.class_data = ehci_pci_info + i; + type_register(&ehci_type_info); + } } type_init(ehci_pci_register_types) -- cgit v1.2.3 From ba07630c79807147786d3fd033e2762ae84eb132 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2012 13:17:46 +0100 Subject: usb/ehci-pci: add ich9 00:1a.* variant Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci-pci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index df137cccc9..79bc276a44 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -116,10 +116,15 @@ static struct EHCIPCIInfo ehci_pci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ .revision = 0x10, },{ - .name = "ich9-usb-ehci1", + .name = "ich9-usb-ehci1", /* 00:1d.7 */ .vendor_id = PCI_VENDOR_ID_INTEL, .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, .revision = 0x03, + },{ + .name = "ich9-usb-ehci2", /* 00:1a.7 */ + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI2, + .revision = 0x03, } }; -- cgit v1.2.3 From bb4d2b2f0aad4a940db86eb469de0bf91c1c6837 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 30 Oct 2012 13:18:36 +0100 Subject: usb/ehci-pci: add helper to create ich9 usb controllers Signed-off-by: Gerd Hoffmann --- hw/usb.h | 2 ++ hw/usb/hcd-ehci-pci.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/hw/usb.h b/hw/usb.h index 3a6cc8444a..1837bf780f 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -524,5 +524,7 @@ const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); +int ehci_create_ich9_with_companions(PCIBus *bus, int slot); + #endif diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 79bc276a44..d5c7d462d0 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -145,3 +145,56 @@ static void ehci_pci_register_types(void) } type_init(ehci_pci_register_types) + +struct ehci_companions { + const char *name; + int func; + int port; +}; + +static const struct ehci_companions ich9_1d[] = { + { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, + { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, + { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, +}; + +static const struct ehci_companions ich9_1a[] = { + { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, + { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, + { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, +}; + +int ehci_create_ich9_with_companions(PCIBus *bus, int slot) +{ + const struct ehci_companions *comp; + PCIDevice *ehci, *uhci; + BusState *usbbus; + const char *name; + int i; + + switch (slot) { + case 0x1d: + name = "ich9-usb-ehci1"; + comp = ich9_1d; + break; + case 0x1a: + name = "ich9-usb-ehci2"; + comp = ich9_1a; + break; + default: + return -1; + } + + ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name); + qdev_init_nofail(&ehci->qdev); + usbbus = QLIST_FIRST(&ehci->qdev.child_bus); + + for (i = 0; i < 3; i++) { + uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func), + true, comp[i].name); + qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); + qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); + qdev_init_nofail(&uhci->qdev); + } + return 0; +} -- cgit v1.2.3 From faccca000f27bb63fce0c71dc4ca68be1c23a8b0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 12:54:36 +0100 Subject: uhci: Add a uhci_handle_td_error() helper function Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 90 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 71263fe143..a06372b461 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -735,9 +735,52 @@ static void uhci_read_td(UHCIState *s, UHCI_TD *td, uint32_t link) le32_to_cpus(&td->buffer); } +static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr, + int status, uint32_t *int_mask) +{ + uint32_t queue_token = uhci_queue_token(td); + int ret; + + switch (status) { + case USB_RET_NAK: + td->ctrl |= TD_CTRL_NAK; + return TD_RESULT_NEXT_QH; + + case USB_RET_STALL: + td->ctrl |= TD_CTRL_STALL; + trace_usb_uhci_packet_complete_stall(queue_token, td_addr); + ret = TD_RESULT_NEXT_QH; + break; + + case USB_RET_BABBLE: + td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; + /* frame interrupted */ + trace_usb_uhci_packet_complete_babble(queue_token, td_addr); + ret = TD_RESULT_STOP_FRAME; + break; + + case USB_RET_IOERROR: + case USB_RET_NODEV: + default: + td->ctrl |= TD_CTRL_TIMEOUT; + td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT); + trace_usb_uhci_packet_complete_error(queue_token, td_addr); + ret = TD_RESULT_NEXT_QH; + break; + } + + td->ctrl &= ~TD_CTRL_ACTIVE; + s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } + uhci_update_irq(s); + return ret; +} + static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) { - int len = 0, max_len, err, ret; + int len = 0, max_len, ret; uint8_t pid; max_len = ((td->token >> 21) + 1) & 0x7ff; @@ -748,8 +791,9 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; - if (ret < 0) - goto out; + if (ret < 0) { + return uhci_handle_td_error(s, td, async->td_addr, ret, int_mask); + } len = async->packet.result; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); @@ -775,46 +819,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ trace_usb_uhci_packet_complete_success(async->queue->token, async->td_addr); return TD_RESULT_COMPLETE; - -out: - switch(ret) { - case USB_RET_NAK: - td->ctrl |= TD_CTRL_NAK; - return TD_RESULT_NEXT_QH; - - case USB_RET_STALL: - td->ctrl |= TD_CTRL_STALL; - trace_usb_uhci_packet_complete_stall(async->queue->token, - async->td_addr); - err = TD_RESULT_NEXT_QH; - break; - - case USB_RET_BABBLE: - td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; - /* frame interrupted */ - trace_usb_uhci_packet_complete_babble(async->queue->token, - async->td_addr); - err = TD_RESULT_STOP_FRAME; - break; - - case USB_RET_IOERROR: - case USB_RET_NODEV: - default: - td->ctrl |= TD_CTRL_TIMEOUT; - td->ctrl &= ~(3 << TD_CTRL_ERROR_SHIFT); - trace_usb_uhci_packet_complete_error(async->queue->token, - async->td_addr); - err = TD_RESULT_NEXT_QH; - break; - } - - td->ctrl &= ~TD_CTRL_ACTIVE; - s->status |= UHCI_STS_USBERR; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; - } - uhci_update_irq(s); - return err; } static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, -- cgit v1.2.3 From 7f102ebeb5bad7b723a25557234b0feb493f6134 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 12:54:37 +0100 Subject: uhci: Don't crash on device disconnect My recent uhci cleanup series has introduced a regression, where qemu sometimes crashes on a device disconnect. The problem is that the uhci code never checked for a device not / no longer existing, instead it was relying on usb_handle_packet accepting a NULL device. But since we now pass usb_handle_packet q->ep->dev, rather then just a local dev variable, we crash as q->ep == NULL due to the device no longer existing. This patch fixes this. Note that this patch also improves over the old behavior were we would: 1) create a queue for the device 2) create an async for the packet 3) have usb_handle_packet fail 4) destroy the async 5) wait for the queue to be idle for 32 frames 6) destroy the queue Which was rather sub-optimal. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index a06372b461..f4b555addc 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -896,6 +896,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, if (q == NULL) { USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f); USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); + + if (ep == NULL) { + return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV, + int_mask); + } q = uhci_queue_new(s, qh_addr, td, ep); } async = uhci_async_alloc(q, td_addr); -- cgit v1.2.3 From a552a966f16b7b39c5df16fc17e12d02c4fa5954 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:09 +0100 Subject: usb: Add packet combining functions Currently we only do pipelining for output endpoints, since to properly support short-not-ok semantics we can only have one outstanding input packet. Since the ehci and uhci controllers have a limited per td packet size guests will split large input transfers to into multiple packets, and since we don't pipeline these, this comes with a serious performance penalty. This patch adds helper functions to (re-)combine packets which belong to 1 transfer at the guest device-driver level into 1 large transger. This can be used by (redirection) usb-devices to enable pipelining for input endpoints. This patch will combine packets together until a transfer terminating packet is encountered. A terminating packet is a packet which meets one or more of the following conditions: 1) The packet size is *not* a multiple of the endpoint max packet size 2) The packet does *not* have its short-not-ok flag set 3) The packet has its interrupt-on-complete flag set The short-not-ok flag of the combined packet is that of the terminating packet. Multiple combined packets may be submitted to the device, if the combined packets do not have their short-not-ok flag set, enabling true pipelining. If a combined packet does have its short-not-ok flag set the queue will wait with submitting further packets to the device until that packet has completed. Once enabled in the usb-redir and ehci code, this improves the speed (MB/s) of a Linux guest reading from a USB mass storage device by a factor of 1.2 - 1.5. And the main reason why I started working on this, when reading from a pl2303 USB<->serial converter, it combines the previous 4 packets submitted per device-driver level read into 1 big read, reducing the number of packets / sec by a factor 4, and it allows to have multiple reads outstanding. This allows for much better latency tolerance without the pl2303's internal buffer overflowing (which was happening at 115200 bps, without serial flow control). Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb.h | 13 ++++ hw/usb/Makefile.objs | 2 +- hw/usb/combined-packet.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++ hw/usb/core.c | 1 + 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 hw/usb/combined-packet.c diff --git a/hw/usb.h b/hw/usb.h index 1837bf780f..aef07f4a07 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -160,6 +160,7 @@ typedef struct USBBusOps USBBusOps; typedef struct USBPort USBPort; typedef struct USBDevice USBDevice; typedef struct USBPacket USBPacket; +typedef struct USBCombinedPacket USBCombinedPacket; typedef struct USBEndpoint USBEndpoint; typedef struct USBDesc USBDesc; @@ -356,7 +357,15 @@ struct USBPacket { int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBPacketState state; + USBCombinedPacket *combined; QTAILQ_ENTRY(USBPacket) queue; + QTAILQ_ENTRY(USBPacket) combined_entry; +}; + +struct USBCombinedPacket { + USBPacket *first; + QTAILQ_HEAD(packets_head, USBPacket) packets; + QEMUIOVector iov; }; void usb_packet_init(USBPacket *p); @@ -399,6 +408,10 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, uint64_t id); +void usb_ep_combine_input_packets(USBEndpoint *ep); +void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p); +void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p); + void usb_attach(USBPort *port); void usb_detach(USBPort *port); void usb_port_reset(USBPort *port); diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 7669938abd..5a4eeb6d13 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -7,7 +7,7 @@ common-obj-y += libhw.o common-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o common-obj-$(CONFIG_USB_REDIR) += redirect.o -common-obj-y += core.o bus.o desc.o dev-hub.o +common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o common-obj-y += host-$(HOST_USB).o dev-bluetooth.o common-obj-y += dev-hid.o dev-storage.o dev-wacom.o common-obj-y += dev-serial.o dev-network.o dev-audio.o diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c new file mode 100644 index 0000000000..652bf02628 --- /dev/null +++ b/hw/usb/combined-packet.c @@ -0,0 +1,179 @@ +/* + * QEMU USB packet combining code (for input pipelining) + * + * Copyright(c) 2012 Red Hat, Inc. + * + * Red Hat Authors: + * Hans de Goede + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or(at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include "qemu-common.h" +#include "hw/usb.h" +#include "iov.h" +#include "trace.h" + +static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p) +{ + qemu_iovec_concat(&combined->iov, &p->iov, 0, p->iov.size); + QTAILQ_INSERT_TAIL(&combined->packets, p, combined_entry); + p->combined = combined; +} + +static void usb_combined_packet_remove(USBCombinedPacket *combined, + USBPacket *p) +{ + assert(p->combined == combined); + p->combined = NULL; + QTAILQ_REMOVE(&combined->packets, p, combined_entry); +} + +/* Also handles completion of non combined packets for pipelined input eps */ +void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) +{ + USBCombinedPacket *combined = p->combined; + USBEndpoint *ep = p->ep; + USBPacket *next; + enum { completing, complete, leftover }; + int result, state = completing; + bool short_not_ok; + + if (combined == NULL) { + usb_packet_complete_one(dev, p); + goto leave; + } + + assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets)); + + result = combined->first->result; + short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; + + QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { + if (state == completing) { + /* Distribute data over uncombined packets */ + if (result >= p->iov.size) { + p->result = p->iov.size; + } else { + /* Send short or error packet to complete the transfer */ + p->result = result; + state = complete; + } + p->short_not_ok = short_not_ok; + usb_combined_packet_remove(combined, p); + usb_packet_complete_one(dev, p); + result -= p->result; + } else { + /* Remove any leftover packets from the queue */ + state = leftover; + p->result = USB_RET_REMOVE_FROM_QUEUE; + dev->port->ops->complete(dev->port, p); + } + } + /* + * If we had leftover packets the hcd driver will have cancelled them + * and usb_combined_packet_cancel has already freed combined! + */ + if (state != leftover) { + g_free(combined); + } +leave: + /* Check if there are packets in the queue waiting for our completion */ + usb_ep_combine_input_packets(ep); +} + +/* May only be called for combined packets! */ +void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p) +{ + USBCombinedPacket *combined = p->combined; + assert(combined != NULL); + + usb_combined_packet_remove(combined, p); + if (p == combined->first) { + usb_device_cancel_packet(dev, p); + } + if (QTAILQ_EMPTY(&combined->packets)) { + g_free(combined); + } +} + +/* + * Large input transfers can get split into multiple input packets, this + * function recombines them, removing the short_not_ok checks which all but + * the last packet of such splits transfers have, thereby allowing input + * transfer pipelining (which we cannot do on short_not_ok transfers) + */ +void usb_ep_combine_input_packets(USBEndpoint *ep) +{ + USBPacket *p, *u, *next, *prev = NULL, *first = NULL; + USBPort *port = ep->dev->port; + int ret; + + assert(ep->pipeline); + assert(ep->pid == USB_TOKEN_IN); + + QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) { + /* Empty the queue on a halt */ + if (ep->halted) { + p->result = USB_RET_REMOVE_FROM_QUEUE; + port->ops->complete(port, p); + continue; + } + + /* Skip packets already submitted to the device */ + if (p->state == USB_PACKET_ASYNC) { + prev = p; + continue; + } + usb_packet_check_state(p, USB_PACKET_QUEUED); + + /* + * If the previous (combined) packet has the short_not_ok flag set + * stop, as we must not submit packets to the device after a transfer + * ending with short_not_ok packet. + */ + if (prev && prev->short_not_ok) { + break; + } + + if (first) { + if (first->combined == NULL) { + USBCombinedPacket *combined = g_new0(USBCombinedPacket, 1); + + combined->first = first; + QTAILQ_INIT(&combined->packets); + qemu_iovec_init(&combined->iov, 2); + usb_combined_packet_add(combined, first); + } + usb_combined_packet_add(first->combined, p); + } else { + first = p; + } + + /* Is this packet the last one of a (combined) transfer? */ + if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || + next == NULL) { + ret = usb_device_handle_data(ep->dev, first); + assert(ret == USB_RET_ASYNC); + if (first->combined) { + QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) { + usb_packet_set_state(u, USB_PACKET_ASYNC); + } + } else { + usb_packet_set_state(first, USB_PACKET_ASYNC); + } + first = NULL; + prev = p; + } + } +} diff --git a/hw/usb/core.c b/hw/usb/core.c index 632a8efe47..ab37f6f7b8 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -545,6 +545,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, p->parameter = 0; p->short_not_ok = short_not_ok; p->int_req = int_req; + p->combined = NULL; qemu_iovec_reset(&p->iov); usb_packet_set_state(p, USB_PACKET_SETUP); } -- cgit v1.2.3 From 579967bea69bf1b32faee13ff76b19ba641a2618 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:10 +0100 Subject: combined-packet: Add a workaround for Linux usbfs + live migration Older versions (anything but the latest) of Linux usbfs + libusb(x), will submit larger (bulk) transfers split into multiple 16k submissions, which means that rather then all tds getting linked into the queue in one atomic operarion they get linked in a bunch at a time, which could cause problems if: 1) We scan the queue while libusb is in the middle of submitting a split bulk transfer 2) While this bulk transfer is pending we migrate to another host. The problem is that after 2, the new host will rescan the queue and combine the packets in one large transfer, where as 1) has caused the original host to see them as 2 transfers. This patch fixes this by stopping combinging if we detect a 16k transfer with its int_req flag set. This should not adversely effect performance for other cases as: 1) Linux never sets the interrupt flag on packets other then the last 2) Windows does set the in_req flag on each td, but will submit large transfers in 20k tds thus never triggering the check Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/combined-packet.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index 652bf02628..3904e71634 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -117,7 +117,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) { USBPacket *p, *u, *next, *prev = NULL, *first = NULL; USBPort *port = ep->dev->port; - int ret; + int ret, totalsize; assert(ep->pipeline); assert(ep->pid == USB_TOKEN_IN); @@ -161,8 +161,11 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) } /* Is this packet the last one of a (combined) transfer? */ + totalsize = (p->combined) ? p->combined->iov.size : p->iov.size; if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || - next == NULL) { + next == NULL || + /* Work around for Linux usbfs bulk splitting + migration */ + (totalsize == 16348 && p->int_req)) { ret = usb_device_handle_data(ep->dev, first); assert(ret == USB_RET_ASYNC); if (first->combined) { -- cgit v1.2.3 From c19a798133e936a15ee0175ebc973d777c4c62ed Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:11 +0100 Subject: usb-redir: Add support for 32 bits bulk packet length Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- configure | 2 +- hw/usb/redirect.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 9c6ac875a5..fbebf0f448 100755 --- a/configure +++ b/configure @@ -2755,7 +2755,7 @@ fi # check for usbredirparser for usb network redirection support if test "$usb_redir" != "no" ; then - if $pkg_config --atleast-version=0.5 libusbredirparser-0.5 >/dev/null 2>&1 ; then + if $pkg_config --atleast-version=0.5.3 libusbredirparser-0.5 >/dev/null 2>&1 ; then usb_redir="yes" usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null) usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 22f671b3b1..6008620241 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -575,6 +575,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, bulk_packet.endpoint = ep; bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; + bulk_packet.length_high = p->iov.size >> 16; + assert(bulk_packet.length_high == 0 || + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_32bits_bulk_length)); if (ep & USB_DIR_IN) { usbredirparser_send_bulk_packet(dev->parser, p->id, @@ -896,6 +900,7 @@ static void usbredir_create_parser(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_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length); if (runstate_check(RUN_STATE_INMIGRATE)) { flags |= usbredirparser_fl_no_hello; @@ -1452,7 +1457,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, { USBRedirDevice *dev = priv; uint8_t ep = bulk_packet->endpoint; - int len = bulk_packet->length; + int len = (bulk_packet->length_high << 16) | bulk_packet->length; USBPacket *p; DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n", -- cgit v1.2.3 From 1b36c4d826b586de40efd0c2d3c269b14ca8e150 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:12 +0100 Subject: usb-redir: Add support for input pipelining Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 6008620241..448cfabb17 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -29,6 +29,7 @@ #include "qemu-timer.h" #include "monitor.h" #include "sysemu.h" +#include "iov.h" #include #include @@ -312,6 +313,11 @@ static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + if (p->combined) { + usb_combined_packet_cancel(udev, p); + return; + } + packet_id_queue_add(&dev->cancelled, p->id); usbredirparser_send_cancel_data_packet(dev->parser, p->id); usbredirparser_do_write(dev->parser); @@ -331,6 +337,10 @@ static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev, static USBPacket *p; QTAILQ_FOREACH(p, &ep->queue, queue) { + /* Skip combined packets, except for the first */ + if (p->combined && p != p->combined->first) { + continue; + } packet_id_queue_add(&dev->already_in_flight, p->id); } } @@ -565,17 +575,18 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, uint8_t ep) { struct usb_redir_bulk_packet_header bulk_packet; + size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; - 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, 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.length = size; bulk_packet.stream_id = 0; - bulk_packet.length_high = p->iov.size >> 16; + bulk_packet.length_high = size >> 16; assert(bulk_packet.length_high == 0 || usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_32bits_bulk_length)); @@ -584,11 +595,16 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, NULL, 0); } else { - uint8_t buf[p->iov.size]; - usb_packet_copy(p, buf, p->iov.size); - usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); + uint8_t buf[size]; + if (p->combined) { + iov_to_buf(p->combined->iov.iov, p->combined->iov.niov, + 0, buf, size); + } else { + usb_packet_copy(p, buf, size); + } + usbredir_log_data(dev, "bulk data out:", buf, size); usbredirparser_send_bulk_packet(dev->parser, p->id, - &bulk_packet, buf, p->iov.size); + &bulk_packet, buf, size); } usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; @@ -705,6 +721,10 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p) case USB_ENDPOINT_XFER_ISOC: return usbredir_handle_iso_data(dev, p, ep); case USB_ENDPOINT_XFER_BULK: + if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN && + p->ep->pipeline) { + return USB_RET_ADD_TO_QUEUE; + } return usbredir_handle_bulk_data(dev, p, ep); case USB_ENDPOINT_XFER_INT: return usbredir_handle_interrupt_data(dev, p, ep); @@ -715,6 +735,13 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p) } } +static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) +{ + if (ep->pid == USB_TOKEN_IN && ep->pipeline) { + usb_ep_combine_input_packets(ep); + } +} + static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, int config) { @@ -1283,6 +1310,11 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) if (uep->pid == USB_TOKEN_OUT) { uep->pipeline = true; } + if (uep->pid == USB_TOKEN_IN && uep->max_packet_size != 0 && + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_32bits_bulk_length)) { + uep->pipeline = true; + } } static void usbredir_ep_info(void *priv, @@ -1465,11 +1497,17 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, p = usbredir_find_packet_by_id(dev, ep, id); if (p) { + size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; len = usbredir_handle_status(dev, bulk_packet->status, len); if (len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len <= p->iov.size) { - usb_packet_copy(p, data, data_len); + if (data_len <= size) { + if (p->combined) { + iov_from_buf(p->combined->iov.iov, p->combined->iov.niov, + 0, data, data_len); + } else { + usb_packet_copy(p, data, data_len); + } } else { ERROR("bulk got more data then requested (%d > %zd)\n", data_len, p->iov.size); @@ -1477,7 +1515,11 @@ static void usbredir_bulk_packet(void *priv, uint64_t id, } } p->result = len; - usb_packet_complete(&dev->dev, p); + if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { + usb_combined_input_packet_complete(&dev->dev, p); + } else { + usb_packet_complete(&dev->dev, p); + } } free(data); } @@ -1884,6 +1926,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; + uc->flush_ep_queue = usbredir_flush_ep_queue; dc->vmsd = &usbredir_vmstate; dc->props = usbredir_properties; } -- cgit v1.2.3 From 7e03d1781690c9e9ad67a671237dbcc0d159f528 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:13 +0100 Subject: usb-redir: Add an usbredir_setup_usb_eps() helper function Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 448cfabb17..a3df757c43 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1317,17 +1317,35 @@ static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) } } +static void usbredir_setup_usb_eps(USBRedirDevice *dev) +{ + struct USBEndpoint *usb_ep; + int i, pid; + + for (i = 0; i < MAX_ENDPOINTS; i++) { + pid = (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT; + usb_ep = usb_ep_get(&dev->dev, pid, 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; + usbredir_set_pipeline(dev, usb_ep); + } +} + static void usbredir_ep_info(void *priv, struct usb_redir_ep_info_header *ep_info) { USBRedirDevice *dev = priv; - struct USBEndpoint *usb_ep; int i; for (i = 0; i < MAX_ENDPOINTS; i++) { dev->endpoint[i].type = ep_info->type[i]; dev->endpoint[i].interval = ep_info->interval[i]; dev->endpoint[i].interface = ep_info->interface[i]; + if (usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size)) { + dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; + } switch (dev->endpoint[i].type) { case usb_redir_type_invalid: break; @@ -1348,18 +1366,8 @@ static void usbredir_ep_info(void *priv, usbredir_device_disconnect(dev); return; } - 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; - if (usbredirparser_peer_has_cap(dev->parser, - usb_redir_cap_ep_info_max_packet_size)) { - dev->endpoint[i].max_packet_size = - usb_ep->max_packet_size = ep_info->max_packet_size[i]; - } - usbredir_set_pipeline(dev, usb_ep); } + usbredir_setup_usb_eps(dev); } static void usbredir_configuration_status(void *priv, uint64_t id, @@ -1601,8 +1609,6 @@ static void usbredir_pre_save(void *priv) 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: @@ -1622,15 +1628,8 @@ static int usbredir_post_load(void *priv, int version_id) } 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; - usbredir_set_pipeline(dev, usb_ep); - } + usbredir_setup_usb_eps(dev); + return 0; } -- cgit v1.2.3 From 24ac283aa293f9bd8ca4137775506edf33e45eae Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:14 +0100 Subject: usb-redir: Use reject rather the disconnect on bad ep info So that the client gets a notification about us disconnecting the device. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index a3df757c43..5c2d185550 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1353,7 +1353,8 @@ static void usbredir_ep_info(void *priv, case usb_redir_type_interrupt: if (dev->endpoint[i].interval == 0) { ERROR("Received 0 interval for isoc or irq endpoint\n"); - usbredir_device_disconnect(dev); + usbredir_reject_device(dev); + return; } /* Fall through */ case usb_redir_type_control: @@ -1363,7 +1364,7 @@ static void usbredir_ep_info(void *priv, break; default: ERROR("Received invalid endpoint type\n"); - usbredir_device_disconnect(dev); + usbredir_reject_device(dev); return; } } -- cgit v1.2.3 From cdfd3530fafbef441ab6b86da64151f74073e5bd Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 31 Oct 2012 13:47:15 +0100 Subject: usb-redir: Allow to attach USB 2.0 devices to 1.1 host controller This follows the logic of host-linux: If a 2.0 device has no ISO endpoint and no interrupt endpoint with a packet size > 64, we can attach it also to an 1.1 host controller. In case the redir server does not report endpoint sizes, play safe and remove the 1.1 compatibility as well. Moreover, if we detect a conflicting change in the configuration after the device was already attached, it will be disconnected immediately. HdG: Several small cleanups and fixes Signed-off-by: Jan Kiszka Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5c2d185550..8d4d3f41be 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -106,6 +106,7 @@ struct USBRedirDevice { struct usb_redir_interface_info_header interface_info; struct usbredirfilter_rule *filter_rules; int filter_rules_count; + int compatible_speedmask; }; static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); @@ -962,6 +963,7 @@ static void usbredir_do_attach(void *opaque) } if (usb_device_attach(&dev->dev) != 0) { + WARNING("rejecting device due to speed mismatch\n"); usbredir_reject_device(dev); } } @@ -1068,6 +1070,9 @@ static int usbredir_initfn(USBDevice *udev) /* We'll do the attach once we receive the speed from the usb-host */ udev->auto_attach = 0; + /* Will be cleared during setup when we find conflicts */ + dev->compatible_speedmask = USB_SPEED_MASK_FULL; + /* Let the backend know we are ready */ qemu_chr_fe_open(dev->cs); qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read, @@ -1208,6 +1213,7 @@ static void usbredir_device_connect(void *priv, case usb_redir_speed_low: speed = "low speed"; dev->dev.speed = USB_SPEED_LOW; + dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL; break; case usb_redir_speed_full: speed = "full speed"; @@ -1241,7 +1247,7 @@ static void usbredir_device_connect(void *priv, device_connect->device_class); } - dev->dev.speedmask = (1 << dev->dev.speed); + dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; dev->device_info = *device_connect; if (usbredir_check_filter(dev)) { @@ -1281,6 +1287,7 @@ static void usbredir_device_disconnect(void *priv) dev->interface_info.interface_count = NO_INTERFACE_INFO; dev->dev.addr = 0; dev->dev.speed = 0; + dev->compatible_speedmask = USB_SPEED_MASK_FULL; } static void usbredir_interface_info(void *priv, @@ -1302,6 +1309,12 @@ static void usbredir_interface_info(void *priv, } } +static void usbredir_mark_speed_incompatible(USBRedirDevice *dev, int speed) +{ + dev->compatible_speedmask &= ~(1 << speed); + dev->dev.speedmask = (1 << dev->dev.speed) | dev->compatible_speedmask; +} + static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) { if (uep->type != USB_ENDPOINT_XFER_BULK) { @@ -1350,7 +1363,14 @@ static void usbredir_ep_info(void *priv, case usb_redir_type_invalid: break; case usb_redir_type_iso: + usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); + /* Fall through */ case usb_redir_type_interrupt: + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size) || + ep_info->max_packet_size[i] > 64) { + usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); + } if (dev->endpoint[i].interval == 0) { ERROR("Received 0 interval for isoc or irq endpoint\n"); usbredir_reject_device(dev); @@ -1368,6 +1388,14 @@ static void usbredir_ep_info(void *priv, return; } } + /* The new ep info may have caused a speed incompatibility, recheck */ + if (dev->dev.attached && + !(dev->dev.port->speedmask & dev->dev.speedmask)) { + ERROR("Device no longer matches speed after endpoint info change, " + "disconnecting!\n"); + usbredir_reject_device(dev); + return; + } usbredir_setup_usb_eps(dev); } -- cgit v1.2.3 From 95a59dc03988d190bbc72d980a149be3a79592fb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 31 Oct 2012 13:47:16 +0100 Subject: usb-redir: Allow redirecting super speed devices to high speed controllers Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 8d4d3f41be..c5cfe0b313 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1071,7 +1071,7 @@ static int usbredir_initfn(USBDevice *udev) udev->auto_attach = 0; /* Will be cleared during setup when we find conflicts */ - dev->compatible_speedmask = USB_SPEED_MASK_FULL; + dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; /* Let the backend know we are ready */ qemu_chr_fe_open(dev->cs); @@ -1214,10 +1214,12 @@ static void usbredir_device_connect(void *priv, speed = "low speed"; dev->dev.speed = USB_SPEED_LOW; dev->compatible_speedmask &= ~USB_SPEED_MASK_FULL; + dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; break; case usb_redir_speed_full: speed = "full speed"; dev->dev.speed = USB_SPEED_FULL; + dev->compatible_speedmask &= ~USB_SPEED_MASK_HIGH; break; case usb_redir_speed_high: speed = "high speed"; @@ -1287,7 +1289,7 @@ static void usbredir_device_disconnect(void *priv) dev->interface_info.interface_count = NO_INTERFACE_INFO; dev->dev.addr = 0; dev->dev.speed = 0; - dev->compatible_speedmask = USB_SPEED_MASK_FULL; + dev->compatible_speedmask = USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; } static void usbredir_interface_info(void *priv, @@ -1364,6 +1366,7 @@ static void usbredir_ep_info(void *priv, break; case usb_redir_type_iso: usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); + usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); /* Fall through */ case usb_redir_type_interrupt: if (!usbredirparser_peer_has_cap(dev->parser, @@ -1371,6 +1374,11 @@ static void usbredir_ep_info(void *priv, ep_info->max_packet_size[i] > 64) { usbredir_mark_speed_incompatible(dev, USB_SPEED_FULL); } + if (!usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size) || + ep_info->max_packet_size[i] > 1024) { + usbredir_mark_speed_incompatible(dev, USB_SPEED_HIGH); + } if (dev->endpoint[i].interval == 0) { ERROR("Received 0 interval for isoc or irq endpoint\n"); usbredir_reject_device(dev); @@ -1501,6 +1509,17 @@ static void usbredir_control_packet(void *priv, uint64_t id, DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status, len, id); + /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices + * to work redirected to a not superspeed capable hcd */ + if (dev->dev.speed == USB_SPEED_SUPER && + !((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER)) && + control_packet->requesttype == 0x80 && + control_packet->request == 6 && + control_packet->value == 0x100 && control_packet->index == 0 && + data_len >= 18 && data[7] == 9) { + data[7] = 64; + } + p = usbredir_find_packet_by_id(dev, 0, id); if (p) { len = usbredir_handle_status(dev, control_packet->status, len); -- cgit v1.2.3