diff --git a/hw/usb/core.c b/hw/usb/core.c index 67ba7d6018fb772a80faf7a4e8b13781220000ba..cf34755bbae58725be9f6e68edb6be6ed14e84cc 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -28,6 +28,26 @@ #include "qemu/iov.h" #include "trace.h" +void usb_pick_speed(USBPort *port) +{ + static const int speeds[] = { + USB_SPEED_SUPER, + USB_SPEED_HIGH, + USB_SPEED_FULL, + USB_SPEED_LOW, + }; + USBDevice *udev = port->dev; + int i; + + for (i = 0; i < ARRAY_SIZE(speeds); i++) { + if ((udev->speedmask & (1 << speeds[i])) && + (port->speedmask & (1 << speeds[i]))) { + udev->speed = speeds[i]; + return; + } + } +} + void usb_attach(USBPort *port) { USBDevice *dev = port->dev; @@ -35,6 +55,7 @@ void usb_attach(USBPort *port) assert(dev != NULL); assert(dev->attached); assert(dev->state == USB_STATE_NOTATTACHED); + usb_pick_speed(port); port->ops->attach(port); dev->state = USB_STATE_ATTACHED; usb_device_handle_attach(dev); diff --git a/hw/usb/desc.c b/hw/usb/desc.c index ab486913637a0f10d48c8803c14edfa4f8384881..b82c397ef9e82bd3ac89228b91200bee4462ab45 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -518,18 +518,6 @@ void usb_desc_init(USBDevice *dev) void usb_desc_attach(USBDevice *dev) { - const USBDesc *desc = usb_device_get_usb_desc(dev); - - assert(desc != NULL); - if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) { - dev->speed = USB_SPEED_SUPER; - } else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { - dev->speed = USB_SPEED_HIGH; - } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) { - dev->speed = USB_SPEED_FULL; - } else { - return; - } usb_desc_setdefaults(dev); } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index a3ae9f260a9da505c476fdf32c55ca721c75da7d..a00a93c3eba6b4e37ac1071a5776de6e088ecb1b 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -27,87 +27,10 @@ * along with this program; if not, see . */ +#include "hw/usb/ehci-regs.h" #include "hw/usb/hcd-ehci.h" #include "trace.h" -/* Capability Registers Base Address - section 2.2 */ -#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 0x000c -#define HCSPPORTROUTE2 0x0010 - -#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 -#define USBCMD_FLS_SH 2 // Frame List Size Shift -#define USBCMD_PSE (1 << 4) // Periodic Schedule Enable -#define USBCMD_ASE (1 << 5) // Asynch Schedule Enable -#define USBCMD_IAAD (1 << 6) // Int Asynch Advance Doorbell -#define USBCMD_LHCR (1 << 7) // Light Host Controller Reset -#define USBCMD_ASPMC (3 << 8) // Async Sched Park Mode Count -#define USBCMD_ASPME (1 << 11) // Async Sched Park Mode Enable -#define USBCMD_ITC (0x7f << 16) // Int Threshold Control -#define USBCMD_ITC_SH 16 // Int Threshold Control Shift - -#define USBSTS 0x0004 -#define USBSTS_RO_MASK 0x0000003f -#define USBSTS_INT (1 << 0) // USB Interrupt -#define USBSTS_ERRINT (1 << 1) // Error Interrupt -#define USBSTS_PCD (1 << 2) // Port Change Detect -#define USBSTS_FLR (1 << 3) // Frame List Rollover -#define USBSTS_HSE (1 << 4) // Host System Error -#define USBSTS_IAA (1 << 5) // Interrupt on Async Advance -#define USBSTS_HALT (1 << 12) // HC Halted -#define USBSTS_REC (1 << 13) // Reclamation -#define USBSTS_PSS (1 << 14) // Periodic Schedule Status -#define USBSTS_ASS (1 << 15) // Asynchronous Schedule Status - -/* - * Interrupt enable bits correspond to the interrupt active bits in USBSTS - * so no need to redefine here. - */ -#define USBINTR 0x0008 -#define USBINTR_MASK 0x0000003f - -#define FRINDEX 0x000c -#define CTRLDSSEGMENT 0x0010 -#define PERIODICLISTBASE 0x0014 -#define ASYNCLISTADDR 0x0018 -#define ASYNCLISTADDR_MASK 0xffffffe0 - -#define CONFIGFLAG 0x0040 - -/* - * Bits that are reserved or are read-only are masked out of values - * written to us by software - */ -#define PORTSC_RO_MASK 0x007001c0 -#define PORTSC_RWC_MASK 0x0000002a -#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable -#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable -#define PORTSC_WKCN_E (1 << 20) // Wake on Connect Enable -#define PORTSC_PTC (15 << 16) // Port Test Control -#define PORTSC_PTC_SH 16 // Port Test Control shift -#define PORTSC_PIC (3 << 14) // Port Indicator Control -#define PORTSC_PIC_SH 14 // Port Indicator Control Shift -#define PORTSC_POWNER (1 << 13) // Port Owner -#define PORTSC_PPOWER (1 << 12) // Port Power -#define PORTSC_LINESTAT (3 << 10) // Port Line Status -#define PORTSC_LINESTAT_SH 10 // Port Line Status Shift -#define PORTSC_PRESET (1 << 8) // Port Reset -#define PORTSC_SUSPEND (1 << 7) // Port Suspend -#define PORTSC_FPRES (1 << 6) // Force Port Resume -#define PORTSC_OCC (1 << 5) // Over Current Change -#define PORTSC_OCA (1 << 4) // Over Current Active -#define PORTSC_PEDC (1 << 3) // Port Enable/Disable Change -#define PORTSC_PED (1 << 2) // Port Enable/Disable -#define PORTSC_CSC (1 << 1) // Connect Status Change -#define PORTSC_CONNECT (1 << 0) // Current Connect Status - #define FRAME_TIMER_FREQ 1000 #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define UFRAME_TIMER_NS (FRAME_TIMER_NS / 8) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 9b1166b2eff6d436977c59b1106a2dd29fe08a9b..c3bf72cc17b52db887a374922300c028b09db7af 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -27,6 +27,7 @@ */ #include "hw/hw.h" #include "hw/usb.h" +#include "hw/usb/uhci-regs.h" #include "hw/pci/pci.h" #include "qemu/timer.h" #include "qemu/iov.h" @@ -37,41 +38,6 @@ //#define DEBUG //#define DEBUG_DUMP_DATA -#define UHCI_CMD_FGR (1 << 4) -#define UHCI_CMD_EGSM (1 << 3) -#define UHCI_CMD_GRESET (1 << 2) -#define UHCI_CMD_HCRESET (1 << 1) -#define UHCI_CMD_RS (1 << 0) - -#define UHCI_STS_HCHALTED (1 << 5) -#define UHCI_STS_HCPERR (1 << 4) -#define UHCI_STS_HSERR (1 << 3) -#define UHCI_STS_RD (1 << 2) -#define UHCI_STS_USBERR (1 << 1) -#define UHCI_STS_USBINT (1 << 0) - -#define TD_CTRL_SPD (1 << 29) -#define TD_CTRL_ERROR_SHIFT 27 -#define TD_CTRL_IOS (1 << 25) -#define TD_CTRL_IOC (1 << 24) -#define TD_CTRL_ACTIVE (1 << 23) -#define TD_CTRL_STALL (1 << 22) -#define TD_CTRL_BABBLE (1 << 20) -#define TD_CTRL_NAK (1 << 19) -#define TD_CTRL_TIMEOUT (1 << 18) - -#define UHCI_PORT_SUSPEND (1 << 12) -#define UHCI_PORT_RESET (1 << 9) -#define UHCI_PORT_LSDA (1 << 8) -#define UHCI_PORT_RD (1 << 6) -#define UHCI_PORT_ENC (1 << 3) -#define UHCI_PORT_EN (1 << 2) -#define UHCI_PORT_CSC (1 << 1) -#define UHCI_PORT_CCS (1 << 0) - -#define UHCI_PORT_READ_ONLY (0x1bb) -#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC) - #define FRAME_TIMER_FREQ 1000 #define FRAME_MAX_LOOPS 256 diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 54dea16009660f228fb1913e8446509b0d6ec42b..7f2af8925f9a0773687706c2238624db8f3b0d56 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -498,6 +498,7 @@ typedef struct XHCIEvRingSeg { enum xhci_flags { XHCI_FLAG_USE_MSI = 1, XHCI_FLAG_USE_MSI_X, + XHCI_FLAG_SS_FIRST, }; static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, @@ -714,10 +715,18 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: - index = uport->index; + if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { + index = uport->index + xhci->numports_3; + } else { + index = uport->index; + } break; case USB_SPEED_SUPER: - index = uport->index + xhci->numports_2; + if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { + index = uport->index; + } else { + index = uport->index + xhci->numports_2; + } break; default: return NULL; @@ -2856,7 +2865,7 @@ static void xhci_port_update(XHCIPort *port, int is_detach) static void xhci_port_reset(XHCIPort *port, bool warm_reset) { - trace_usb_xhci_port_reset(port->portnr); + trace_usb_xhci_port_reset(port->portnr, warm_reset); if (!xhci_port_have_device(port)) { return; @@ -2972,7 +2981,11 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x20425355; /* "USB " */ break; case 0x28: /* Supported Protocol:08 */ - ret = 0x00000001 | (xhci->numports_2<<8); + if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { + ret = (xhci->numports_2<<8) | (xhci->numports_3+1); + } else { + ret = (xhci->numports_2<<8) | 1; + } break; case 0x2c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -2984,7 +2997,11 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x20425355; /* "USB " */ break; case 0x38: /* Supported Protocol:08 */ - ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8); + if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { + ret = (xhci->numports_3<<8) | 1; + } else { + ret = (xhci->numports_3<<8) | (xhci->numports_2+1); + } break; case 0x3c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -3517,8 +3534,13 @@ static void usb_xhci_init(XHCIState *xhci) for (i = 0; i < usbports; i++) { speedmask = 0; if (i < xhci->numports_2) { - port = &xhci->ports[i]; - port->portnr = i + 1; + if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { + port = &xhci->ports[i + xhci->numports_3]; + port->portnr = i + 1 + xhci->numports_3; + } else { + port = &xhci->ports[i]; + port->portnr = i + 1; + } port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_LOW | @@ -3528,8 +3550,13 @@ static void usb_xhci_init(XHCIState *xhci) speedmask |= port->speedmask; } if (i < xhci->numports_3) { - port = &xhci->ports[i + xhci->numports_2]; - port->portnr = i + 1 + xhci->numports_2; + if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { + port = &xhci->ports[i]; + port->portnr = i + 1; + } else { + port = &xhci->ports[i + xhci->numports_2]; + port->portnr = i + 1 + xhci->numports_2; + } port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_SUPER; snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1); @@ -3788,6 +3815,8 @@ static const VMStateDescription vmstate_xhci = { static Property xhci_properties[] = { DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), + DEFINE_PROP_BIT("superspeed-ports-first", + XHCIState, flags, XHCI_FLAG_SS_FIRST, true), DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS), DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 8007d1d1565cff8f52f0a34c9623cbd8a889b9e3..afbf1563f459479eecc05d6cc2b249c9a3f3b67e 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -111,6 +111,7 @@ struct USBHostRequest { unsigned char *buffer; unsigned char *cbuf; unsigned int clen; + bool usb3ep0quirk; QTAILQ_ENTRY(USBHostRequest) next; }; @@ -146,6 +147,10 @@ static void usb_host_attach_kernel(USBHostDevice *s); #define BULK_TIMEOUT 0 /* unlimited */ #define INTR_TIMEOUT 0 /* unlimited */ +#if LIBUSBX_API_VERSION >= 0x01000103 +# define HAVE_STREAMS 1 +#endif + static const char *speed_name[] = { [LIBUSB_SPEED_UNKNOWN] = "?", [LIBUSB_SPEED_LOW] = "1.5", @@ -346,6 +351,13 @@ static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer) r->p->actual_length = xfer->actual_length; if (r->in && xfer->actual_length) { memcpy(r->cbuf, r->buffer + 8, xfer->actual_length); + + /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices + * to work redirected to a not superspeed capable hcd */ + if (r->usb3ep0quirk && xfer->actual_length >= 18 && + r->cbuf[7] == 9) { + r->cbuf[7] = 64; + } } trace_usb_host_req_complete(s->bus_num, s->addr, r->p, r->p->status, r->p->actual_length); @@ -672,11 +684,17 @@ static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p) /* ------------------------------------------------------------------------ */ -static bool usb_host_full_speed_compat(USBHostDevice *s) +static void usb_host_speed_compat(USBHostDevice *s) { + USBDevice *udev = USB_DEVICE(s); struct libusb_config_descriptor *conf; const struct libusb_interface_descriptor *intf; const struct libusb_endpoint_descriptor *endp; +#ifdef HAVE_STREAMS + struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; +#endif + bool compat_high = true; + bool compat_full = true; uint8_t type; int rc, c, i, a, e; @@ -693,10 +711,27 @@ static bool usb_host_full_speed_compat(USBHostDevice *s) type = endp->bmAttributes & 0x3; switch (type) { case 0x01: /* ISO */ - return false; + compat_full = false; + compat_high = false; + break; + case 0x02: /* BULK */ +#ifdef HAVE_STREAMS + rc = libusb_get_ss_endpoint_companion_descriptor + (ctx, endp, &endp_ss_comp); + if (rc == LIBUSB_SUCCESS) { + libusb_free_ss_endpoint_companion_descriptor + (endp_ss_comp); + compat_full = false; + compat_high = false; + } +#endif + break; case 0x03: /* INTERRUPT */ if (endp->wMaxPacketSize > 64) { - return false; + compat_full = false; + } + if (endp->wMaxPacketSize > 1024) { + compat_high = false; } break; } @@ -705,7 +740,17 @@ static bool usb_host_full_speed_compat(USBHostDevice *s) } libusb_free_config_descriptor(conf); } - return true; + + udev->speedmask = (1 << udev->speed); + if (udev->speed == USB_SPEED_SUPER && compat_high) { + udev->speedmask |= USB_SPEED_HIGH; + } + if (udev->speed == USB_SPEED_SUPER && compat_full) { + udev->speedmask |= USB_SPEED_FULL; + } + if (udev->speed == USB_SPEED_HIGH && compat_full) { + udev->speedmask |= USB_SPEED_FULL; + } } static void usb_host_ep_update(USBHostDevice *s) @@ -720,7 +765,7 @@ static void usb_host_ep_update(USBHostDevice *s) struct libusb_config_descriptor *conf; const struct libusb_interface_descriptor *intf; const struct libusb_endpoint_descriptor *endp; -#if LIBUSBX_API_VERSION >= 0x01000103 +#ifdef HAVE_STREAMS struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp; #endif uint8_t devep, type; @@ -768,7 +813,7 @@ static void usb_host_ep_update(USBHostDevice *s) usb_ep_set_type(udev, pid, ep, type); usb_ep_set_ifnum(udev, pid, ep, i); usb_ep_set_halted(udev, pid, ep, 0); -#if LIBUSBX_API_VERSION >= 0x01000103 +#ifdef HAVE_STREAMS if (type == LIBUSB_TRANSFER_TYPE_BULK && libusb_get_ss_endpoint_companion_descriptor(ctx, endp, &endp_ss_comp) == LIBUSB_SUCCESS) { @@ -813,10 +858,7 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev) usb_host_ep_update(s); udev->speed = speed_map[libusb_get_device_speed(dev)]; - udev->speedmask = (1 << udev->speed); - if (udev->speed == USB_SPEED_HIGH && usb_host_full_speed_compat(s)) { - udev->speedmask |= USB_SPEED_MASK_FULL; - } + usb_host_speed_compat(s); if (s->ddesc.iProduct) { libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct, @@ -1162,6 +1204,14 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p, memcpy(r->buffer + 8, r->cbuf, r->clen); } + /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices + * to work redirected to a not superspeed capable hcd */ + if (udev->speed == USB_SPEED_SUPER && + !((udev->port->speedmask & USB_SPEED_MASK_SUPER)) && + request == 0x8006 && value == 0x100 && index == 0) { + r->usb3ep0quirk = true; + } + libusb_fill_control_transfer(r->xfer, s->dh, r->buffer, usb_host_req_complete_ctrl, r, CONTROL_TIMEOUT); @@ -1215,7 +1265,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p) } ep = p->ep->nr | (r->in ? USB_DIR_IN : 0); if (p->stream) { -#if LIBUSBX_API_VERSION >= 0x01000103 +#ifdef HAVE_STREAMS libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream, r->buffer, size, usb_host_req_complete_data, r, @@ -1296,7 +1346,7 @@ static void usb_host_handle_reset(USBDevice *udev) static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps, int nr_eps, int streams) { -#if LIBUSBX_API_VERSION >= 0x01000103 +#ifdef HAVE_STREAMS USBHostDevice *s = USB_HOST_DEVICE(udev); unsigned char endpoints[30]; int i, rc; @@ -1326,7 +1376,7 @@ static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps, static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps, int nr_eps) { -#if LIBUSBX_API_VERSION >= 0x01000103 +#ifdef HAVE_STREAMS USBHostDevice *s = USB_HOST_DEVICE(udev); unsigned char endpoints[30]; int i; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 32a76876c758fa938fb1e5df14ac5b043d92eae7..31328a81572de69cfc53c5a66c87953d3b866da3 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -271,6 +271,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = "apic",\ .property = "version",\ .value = stringify(0x11),\ + },{\ + .driver = "nec-usb-xhci",\ + .property = "superspeed-ports-first",\ + .value = "off",\ } #define PC_COMPAT_1_7 \ diff --git a/include/hw/usb.h b/include/hw/usb.h index 1919bdc09d0b33b32f6812d4884bab088f4a047e..8bcab48d29ffd2f0d44d58095fdc3f862d70d6d2 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -458,6 +458,7 @@ 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_pick_speed(USBPort *port); void usb_attach(USBPort *port); void usb_detach(USBPort *port); void usb_port_reset(USBPort *port); diff --git a/include/hw/usb/ehci-regs.h b/include/hw/usb/ehci-regs.h new file mode 100644 index 0000000000000000000000000000000000000000..616f1b88cc8ef66b946950b7a0532032d7a64a65 --- /dev/null +++ b/include/hw/usb/ehci-regs.h @@ -0,0 +1,82 @@ +#ifndef HW_USB_EHCI_REGS_H +#define HW_USB_EHCI_REGS_H 1 + +/* Capability Registers Base Address - section 2.2 */ +#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 0x000c +#define HCSPPORTROUTE2 0x0010 + +#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 +#define USBCMD_FLS_SH 2 // Frame List Size Shift +#define USBCMD_PSE (1 << 4) // Periodic Schedule Enable +#define USBCMD_ASE (1 << 5) // Asynch Schedule Enable +#define USBCMD_IAAD (1 << 6) // Int Asynch Advance Doorbell +#define USBCMD_LHCR (1 << 7) // Light Host Controller Reset +#define USBCMD_ASPMC (3 << 8) // Async Sched Park Mode Count +#define USBCMD_ASPME (1 << 11) // Async Sched Park Mode Enable +#define USBCMD_ITC (0x7f << 16) // Int Threshold Control +#define USBCMD_ITC_SH 16 // Int Threshold Control Shift + +#define USBSTS 0x0004 +#define USBSTS_RO_MASK 0x0000003f +#define USBSTS_INT (1 << 0) // USB Interrupt +#define USBSTS_ERRINT (1 << 1) // Error Interrupt +#define USBSTS_PCD (1 << 2) // Port Change Detect +#define USBSTS_FLR (1 << 3) // Frame List Rollover +#define USBSTS_HSE (1 << 4) // Host System Error +#define USBSTS_IAA (1 << 5) // Interrupt on Async Advance +#define USBSTS_HALT (1 << 12) // HC Halted +#define USBSTS_REC (1 << 13) // Reclamation +#define USBSTS_PSS (1 << 14) // Periodic Schedule Status +#define USBSTS_ASS (1 << 15) // Asynchronous Schedule Status + +/* + * Interrupt enable bits correspond to the interrupt active bits in USBSTS + * so no need to redefine here. + */ +#define USBINTR 0x0008 +#define USBINTR_MASK 0x0000003f + +#define FRINDEX 0x000c +#define CTRLDSSEGMENT 0x0010 +#define PERIODICLISTBASE 0x0014 +#define ASYNCLISTADDR 0x0018 +#define ASYNCLISTADDR_MASK 0xffffffe0 + +#define CONFIGFLAG 0x0040 + +/* + * Bits that are reserved or are read-only are masked out of values + * written to us by software + */ +#define PORTSC_RO_MASK 0x007001c0 +#define PORTSC_RWC_MASK 0x0000002a +#define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable +#define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable +#define PORTSC_WKCN_E (1 << 20) // Wake on Connect Enable +#define PORTSC_PTC (15 << 16) // Port Test Control +#define PORTSC_PTC_SH 16 // Port Test Control shift +#define PORTSC_PIC (3 << 14) // Port Indicator Control +#define PORTSC_PIC_SH 14 // Port Indicator Control Shift +#define PORTSC_POWNER (1 << 13) // Port Owner +#define PORTSC_PPOWER (1 << 12) // Port Power +#define PORTSC_LINESTAT (3 << 10) // Port Line Status +#define PORTSC_LINESTAT_SH 10 // Port Line Status Shift +#define PORTSC_PRESET (1 << 8) // Port Reset +#define PORTSC_SUSPEND (1 << 7) // Port Suspend +#define PORTSC_FPRES (1 << 6) // Force Port Resume +#define PORTSC_OCC (1 << 5) // Over Current Change +#define PORTSC_OCA (1 << 4) // Over Current Active +#define PORTSC_PEDC (1 << 3) // Port Enable/Disable Change +#define PORTSC_PED (1 << 2) // Port Enable/Disable +#define PORTSC_CSC (1 << 1) // Connect Status Change +#define PORTSC_CONNECT (1 << 0) // Current Connect Status + +#endif /* HW_USB_EHCI_REGS_H */ diff --git a/include/hw/usb/uhci-regs.h b/include/hw/usb/uhci-regs.h new file mode 100644 index 0000000000000000000000000000000000000000..c7315c5e1f17f4c5ef936aee2336259e9d319545 --- /dev/null +++ b/include/hw/usb/uhci-regs.h @@ -0,0 +1,40 @@ +#ifndef HW_USB_UHCI_REGS_H +#define HW_USB_UHCI_REGS_H 1 + +#define UHCI_CMD_FGR (1 << 4) +#define UHCI_CMD_EGSM (1 << 3) +#define UHCI_CMD_GRESET (1 << 2) +#define UHCI_CMD_HCRESET (1 << 1) +#define UHCI_CMD_RS (1 << 0) + +#define UHCI_STS_HCHALTED (1 << 5) +#define UHCI_STS_HCPERR (1 << 4) +#define UHCI_STS_HSERR (1 << 3) +#define UHCI_STS_RD (1 << 2) +#define UHCI_STS_USBERR (1 << 1) +#define UHCI_STS_USBINT (1 << 0) + +#define TD_CTRL_SPD (1 << 29) +#define TD_CTRL_ERROR_SHIFT 27 +#define TD_CTRL_IOS (1 << 25) +#define TD_CTRL_IOC (1 << 24) +#define TD_CTRL_ACTIVE (1 << 23) +#define TD_CTRL_STALL (1 << 22) +#define TD_CTRL_BABBLE (1 << 20) +#define TD_CTRL_NAK (1 << 19) +#define TD_CTRL_TIMEOUT (1 << 18) + +#define UHCI_PORT_SUSPEND (1 << 12) +#define UHCI_PORT_RESET (1 << 9) +#define UHCI_PORT_LSDA (1 << 8) +#define UHCI_PORT_RSVD1 (1 << 7) +#define UHCI_PORT_RD (1 << 6) +#define UHCI_PORT_ENC (1 << 3) +#define UHCI_PORT_EN (1 << 2) +#define UHCI_PORT_CSC (1 << 1) +#define UHCI_PORT_CCS (1 << 0) + +#define UHCI_PORT_READ_ONLY (0x1bb) +#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC) + +#endif /* HW_USB_UHCI_REGS_H */ diff --git a/tests/Makefile b/tests/Makefile index 8f71e0d66427c3f2277324fe1101fff9c36c4479..6b294a7dbc8407380c6b02a88e0255ff2ca570f3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -154,6 +154,8 @@ gcov-files-i386-y += hw/pci-bridge/ioh3420.c check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF) gcov-files-i386-y += hw/usb/hcd-ehci.c gcov-files-i386-y += hw/usb/hcd-uhci.c +gcov-files-i386-y += hw/usb/dev-hid.c +gcov-files-i386-y += hw/usb/dev-storage.c check-qtest-x86_64-y = $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) @@ -319,7 +321,7 @@ tests/ac97-test$(EXESUF): tests/ac97-test.o tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o -tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o +tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y) tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index 7e0907b51413604e764276ceda93e331d1e93b37..c9a0b9134a917fea571d4a5bfbd9d31f0b64b684 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -103,7 +103,7 @@ void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value) void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) { - dev->bus->config_writew(dev->bus, dev->devfn, offset, value); + dev->bus->config_writel(dev->bus, dev->devfn, offset, value); } diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index bc56ba77072a8206fd37b0e0846d9af11f3d803f..bcdf62fc3b7c92967cbd5162b0d33c2120c403a3 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -9,12 +9,149 @@ #include #include +#include #include "libqtest.h" +#include "libqos/pci-pc.h" #include "qemu/osdep.h" +#include "hw/usb/uhci-regs.h" +#include "hw/usb/ehci-regs.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +struct qhc { + QPCIDevice *dev; + void *base; +}; + +static QPCIBus *pcibus; +static struct qhc uhci1; +static struct qhc uhci2; +static struct qhc uhci3; +static struct qhc ehci1; + +/* helpers */ + +static void pci_init_one(struct qhc *hc, uint32_t devfn, int bar) +{ + hc->dev = qpci_device_find(pcibus, devfn); + g_assert(hc->dev != NULL); + qpci_device_enable(hc->dev); + hc->base = qpci_iomap(hc->dev, bar); + g_assert(hc->base != NULL); +} + +#if 0 +static void uhci_port_update(struct qhc *hc, int port, + uint16_t set, uint16_t clear) { + void *addr = hc->base + 0x10 + 2 * port; + uint16_t value; + + value = qpci_io_readw(hc->dev, addr); + value |= set; + value &= ~clear; + qpci_io_writew(hc->dev, addr, value); +} +#endif + +static void uhci_port_test(struct qhc *hc, int port, uint16_t expect) +{ + void *addr = hc->base + 0x10 + 2 * port; + uint16_t value = qpci_io_readw(hc->dev, addr); + uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1); + +#if 0 + fprintf(stderr, "%s: %d, have 0x%04x, want 0x%04x\n", + __func__, port, value & mask, expect & mask); +#endif + g_assert((value & mask) == (expect & mask)); +} + +static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) +{ + void *addr = hc->base + 0x64 + 4 * port; + uint32_t value = qpci_io_readl(hc->dev, addr); + uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC); + +#if 0 + fprintf(stderr, "%s: %d, have 0x%08x, want 0x%08x\n", + __func__, port, value & mask, expect & mask); +#endif + g_assert((value & mask) == (expect & mask)); +} + +/* tests */ + +static void pci_init(void) +{ + if (pcibus) { + return; + } + pcibus = qpci_init_pc(); + g_assert(pcibus != NULL); + + pci_init_one(&uhci1, QPCI_DEVFN(0x1d, 0), 4); + pci_init_one(&uhci2, QPCI_DEVFN(0x1d, 1), 4); + pci_init_one(&uhci3, QPCI_DEVFN(0x1d, 2), 4); + pci_init_one(&ehci1, QPCI_DEVFN(0x1d, 7), 0); +} + +static void pci_uhci_port_1(void) +{ + g_assert(pcibus != NULL); + + uhci_port_test(&uhci1, 0, UHCI_PORT_CCS); /* usb-tablet */ + uhci_port_test(&uhci1, 1, UHCI_PORT_CCS); /* usb-storage */ + uhci_port_test(&uhci2, 0, 0); + uhci_port_test(&uhci2, 1, 0); + uhci_port_test(&uhci3, 0, 0); + uhci_port_test(&uhci3, 1, 0); +} + +static void pci_ehci_port_1(void) +{ + int i; + + g_assert(pcibus != NULL); + + for (i = 0; i < 6; i++) { + ehci_port_test(&ehci1, i, PORTSC_POWNER | PORTSC_PPOWER); + } +} + +static void pci_ehci_config(void) +{ + /* hands over all ports from companion uhci to ehci */ + qpci_io_writew(ehci1.dev, ehci1.base + 0x60, 1); +} + +static void pci_uhci_port_2(void) +{ + g_assert(pcibus != NULL); + + uhci_port_test(&uhci1, 0, 0); /* usb-tablet, @ehci */ + uhci_port_test(&uhci1, 1, 0); /* usb-storage, @ehci */ + uhci_port_test(&uhci2, 0, 0); + uhci_port_test(&uhci2, 1, 0); + uhci_port_test(&uhci3, 0, 0); + uhci_port_test(&uhci3, 1, 0); +} + +static void pci_ehci_port_2(void) +{ + static uint32_t expect[] = { + PORTSC_PPOWER | PORTSC_CONNECT, /* usb-tablet */ + PORTSC_PPOWER | PORTSC_CONNECT, /* usb-storage */ + PORTSC_PPOWER, + PORTSC_PPOWER, + PORTSC_PPOWER, + PORTSC_PPOWER, + }; + int i; + + g_assert(pcibus != NULL); + + for (i = 0; i < 6; i++) { + ehci_port_test(&ehci1, i, expect[i]); + } } int main(int argc, char **argv) @@ -22,7 +159,12 @@ int main(int argc, char **argv) int ret; g_test_init(&argc, &argv, NULL); - qtest_add_func("/ehci/pci/nop", pci_nop); + qtest_add_func("/ehci/pci/init", pci_init); + qtest_add_func("/ehci/pci/uhci-port-1", pci_uhci_port_1); + qtest_add_func("/ehci/pci/ehci-port-1", pci_ehci_port_1); + qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config); + qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2); + qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2); qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7," "multifunction=on,id=ich9-ehci-1 " @@ -31,7 +173,10 @@ int main(int argc, char **argv) "-device ich9-usb-uhci2,bus=pcie.0,addr=1d.1," "multifunction=on,masterbus=ich9-ehci-1.0,firstport=2 " "-device ich9-usb-uhci3,bus=pcie.0,addr=1d.2," - "multifunction=on,masterbus=ich9-ehci-1.0,firstport=4"); + "multifunction=on,masterbus=ich9-ehci-1.0,firstport=4 " + "-drive if=none,id=usbcdrom,media=cdrom " + "-device usb-tablet,bus=ich9-ehci-1.0,port=1,usb_version=1 " + "-device usb-storage,bus=ich9-ehci-1.0,port=2,drive=usbcdrom "); ret = g_test_run(); qtest_end(); diff --git a/trace-events b/trace-events index 616a2d6bbe8a694e444fb85ce693c2bc2aa90df3..ffe6e620317a9a2f6153f1833787da5bf590aba3 100644 --- a/trace-events +++ b/trace-events @@ -371,7 +371,7 @@ 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_reset(uint32_t port, bool warm) "port %d, warm %d" usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d" usb_xhci_port_notify(uint32_t port, uint32_t pls) "port %d, bits %x" usb_xhci_slot_enable(uint32_t slotid) "slotid %d"