tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: patch: 3.0 hub support for xhci



Hello,

This patch tries to support USB 3.0 for xhci.

xhci is still at best experimental.
Don't use it on production servers even though it looks work well.


Fixes:

- USB hubs should work.
- The root hub of xhci is now changed to have separated SS and HS ports,
  so that SS and HS hubs in 3.0 hub are hung from SS and HS roothub port
  respectively.
- Fix more for FS and LS device recognition.
- Fix device recognition at boot.
    I changed to force-rescan all ports of xhci root hub recursively.
- Temporarily set IMOD to 160 (25000 irq/sec).
    Intel PantherPoint/LynxPoint/BayTrail need interrupt rate capped,
    otherwise some devices seem to freeze.
    This value is taken from linux xhci.c, but I'm not sure this is optimal.
- Temporarily USBHIST_CALLED in xhci_init() is disabled.
    If USBHIST_CALLED is called before USBHIST_INIT, it panics.
    Something has been changed recently.
- Support WRC PLC CEC port status changes.
- usb_bos_descriptor_t *bdesc is added to usbd_device_handle and
  BOS descr is read in usb_subr.c if SS.
- The name of USB2 Extension descr is renamed to
  usb_devcap_usb2ext_descriptor_t from usb_usb2ext_descriptor_t,
  because this is one of device capability descriptors, which is
  contained in BOS descriptor. (USB 3.0 9.6.2.1)
- Speed definitions of LS and SS in usb_devcap_ss_descriptor_t
  were swapped. (shown in USB 3.0 9.6.2.2 Table 9-13)
- Apply fixed value as initial mps for Control endpoint.

Known bugs:

- HS hub under 3.0 port is disconnected and reconnected every
  several minutes repeatedly.
- Detaching hubs or devices may cause panic or hang up.
    It happens especially when the hub that hangs many devices is
    disconnected.
    I've added usb_transfer_complete with USBD_CANCELLED to
    xhci_device_{ctrl,bulk}_abort(), like device_intr_abort does,
    that might slightly reduce panic.
- Power management is not implemented.
- Some xhci chip recognizes ports as 'connected' but
  'connect/status change' (CSC) bit not set randomly at boot.
    This happens on my xhci expansion card with hub chip.
    uhub ignores ports without CSC bit, so cannot detect hub on this card.
    I'm not sure what is culprit.
- Size of DeviceRemovable in usb_hub_ss_descriptor_t should be expanded
  from 2 to 32 as num of ports on root hub may exceeds 15.
- xhci_do_command may fire KASSERT(sc->sc_command_addr == 0).
    I'm not sure yet what breaks serialization.

Misc bugs:

- Closing pipe does not work correctly,
    e.g. ifconfig axen0 up -> down -> up won't work.
- Most of my 3.0 umass devices does not work due to STALL error.
    They don't understand MODE_SENSE_{6,10} command but stalls.
    To recover stalled ep the driver shall issue reset_endpoint and
    set_dequeue commands, however, xhci_do_command calls cv_timedwait
    that is not allowed to be called from softint context as mentioned
    in softint(9).
    This is because usb_transfer_complete is called from softint context.
    I added experimental recovery code using usb_add_task, but
    I think xhci_handle_event should be changed to be called from
    thread context. (e.g. replacing usb_schedsoftint with usb_add_task,
    however, usb_add_task needs usbd_device_handle as one of args,
    which is difficult to prepare in xhci_intr.)
- xhci.c cannot handle cross-64k transfer yet.
- usbdevs(8) does not report correctly if num of ports > 16.
  Size of udi_ports in struct usb_device_info should be
  expanded from 16 to 256. Needs compat treatment.
- Memory leaks here and there in error and detach paths.
- Slot leaks.
    Devices cannot be added more than 32 times on xhci.
    I added experimental code that disables slot and frees ring memory
    when ctrl endpoint is closed.
- Conexant CX93010 umodem is not recognized (fail with XACT).
- may not work on big endian machines.
- Address of root hub is 0.
- ep->refcnt in usbd_setup_pipe_flags() is not decremented when
  it fails to open pipe.


--
t-hash
--- src/sys/dev/usb/uhub.c.orig	2014-09-09 11:25:38.000000000 +0900
+++ src/sys/dev/usb/uhub.c	2014-11-18 23:50:03.000000000 +0900
@@ -38,12 +38,15 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.126 2014/08/13 06:26:32 skrll Exp $");
 
+#include "opt_usb.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/device.h>
 #include <sys/proc.h>
+#include <sys/sysctl.h>
 
 #include <sys/bus.h>
 
@@ -52,14 +55,42 @@ __KERNEL_RCSID(0, "$NetBSD: uhub.c,v 1.1
 #include <dev/usb/usbdi_util.h>
 #include <dev/usb/usbdivar.h>
 
-#ifdef UHUB_DEBUG
-#define DPRINTF(x)	if (uhubdebug) printf x
-#define DPRINTFN(n,x)	if (uhubdebug>(n)) printf x
-int	uhubdebug = 0;
-#else
+#if !(defined(USB_DEBUG) && defined(UHUB_DEBUG))
+#define uhubdebug 0
 #define DPRINTF(x)
 #define DPRINTFN(n,x)
-#endif
+#else
+static int uhubdebug = 0;
+#define DPRINTF(x)	if (uhubdebug) printf x
+#define DPRINTFN(n,x)	if (uhubdebug>(n)) printf x
+
+SYSCTL_SETUP(sysctl_hw_uhub_setup, "sysctl hw.uhub setup")
+{
+	int err;
+	const struct sysctlnode *rnode;
+	const struct sysctlnode *cnode;
+
+	err = sysctl_createv(clog, 0, NULL, &rnode,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "uhub",
+	    SYSCTL_DESCR("uhub global controls"),
+	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
+
+	if (err)
+		goto fail;
+
+	/* control debugging printfs */
+	err = sysctl_createv(clog, 0, &rnode, &cnode,
+	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
+	    "debug", SYSCTL_DESCR("Enable debugging output"),
+	    NULL, 0, &uhubdebug, sizeof(uhubdebug), CTL_CREATE, CTL_EOL);
+	if (err)
+		goto fail;
+
+	return;
+fail:
+	aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
+}
+#endif /* !(UHUB_DEBUG && USB_DEBUG) */
 
 struct uhub_softc {
 	device_t		sc_dev;		/* base device */
@@ -73,11 +104,13 @@ struct uhub_softc {
 	size_t			sc_statuslen;
 	int			sc_explorepending;
 	int		sc_isehciroothub; /* see comment in uhub_intr() */
+	int		sc_isxhciroothub;
 
 	u_char			sc_running;
 };
 
-#define UHUB_IS_HIGH_SPEED(sc) ((sc)->sc_proto != UDPROTO_FSHUB)
+#define UHUB_IS_HIGH_SPEED(sc) \
+    ((sc)->sc_proto == UDPROTO_HSHUBSTT || (sc)->sc_proto == UDPROTO_HSHUBMTT)
 #define UHUB_IS_SINGLE_TT(sc) ((sc)->sc_proto == UDPROTO_HSHUBSTT)
 
 #define PORTSTAT_ISSET(sc, port) \
@@ -112,6 +145,97 @@ CFATTACH_DECL2_NEW(uroothub, sizeof(stru
  */
 int uhub_ubermatch = 0;
 
+static usbd_status
+usbd_get_hub_desc(usbd_device_handle dev, usb_hub_descriptor_t *hd, int speed)
+{
+	usb_device_request_t req;
+	usbd_status err;
+	int nports;
+
+	/* don't issue UDESC_HUB to SS hub, or it would stall */
+	if (dev->speed == USB_SPEED_SUPER) {
+#if 1
+		union {
+			usb_hub_ss_descriptor_t hd_ss;
+			uint8_t hd_buf[USB_HUB_SS_DESCRIPTOR_SIZE+32-2];
+		} uhd_ss;
+#define hssd uhd_ss.hd_ss
+#else
+		usb_hub_ss_descriptor_t hssd;
+#endif
+		int rmvlen;
+
+		/* XXX: should add member hubssdesc to usbd_hub ? */
+#if 1
+		memset(&uhd_ss, 0, sizeof(uhd_ss));
+#else
+		memset(&hssd, 0, sizeof(hssd));
+#endif
+		req.bmRequestType = UT_READ_CLASS_DEVICE;
+		req.bRequest = UR_GET_DESCRIPTOR;
+		USETW2(req.wValue, UDESC_SS_HUB, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, USB_HUB_SS_DESCRIPTOR_SIZE);
+		DPRINTFN(1,("%s: getting sshub descriptor\n", __func__));
+		err = usbd_do_request(dev, &req, &hssd);
+		nports = hssd.bNbrPorts;
+		rmvlen = (nports + 7) / 8;
+		if (!err && dev->depth == 0 && nports > UHD_SS_NPORTS_MAX) {
+			USETW(req.wLength,
+			    USB_HUB_SS_DESCRIPTOR_SIZE +
+			    (rmvlen > 2 ? rmvlen : 2) - 2);
+			err = usbd_do_request(dev, &req, &hssd);
+		}
+		if (dev->depth != 0 && nports > UHD_SS_NPORTS_MAX) {
+			DPRINTF(("%s: num of ports %d exceeds maxports %d\n",
+			    __func__,
+			    nports, UHD_SS_NPORTS_MAX));
+			nports = hd->bNbrPorts = UHD_SS_NPORTS_MAX;
+		}
+		hd->bDescLength = USB_HUB_DESCRIPTOR_SIZE +
+		    (rmvlen > 1 ? rmvlen : 1) - 1;
+		memcpy(hd->DeviceRemovable, hssd.DeviceRemovable, rmvlen);
+		hd->bDescriptorType		= hssd.bDescriptorType;
+		hd->bNbrPorts			= hssd.bNbrPorts;
+		hd->wHubCharacteristics[0]	= hssd.wHubCharacteristics[0];
+		hd->wHubCharacteristics[1]	= hssd.wHubCharacteristics[1];
+		hd->bPwrOn2PwrGood		= hssd.bPwrOn2PwrGood;
+		hd->bHubContrCurrent		= hssd.bHubContrCurrent;
+#if 1
+#undef hssd
+#endif
+	} else {
+		req.bmRequestType = UT_READ_CLASS_DEVICE;
+		req.bRequest = UR_GET_DESCRIPTOR;
+		USETW2(req.wValue, UDESC_HUB, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
+		DPRINTFN(1,("%s: getting hub descriptor\n", __func__));
+		err = usbd_do_request(dev, &req, hd);
+		nports = hd->bNbrPorts;
+		if (!err && nports > 7) {
+			USETW(req.wLength,
+			    USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
+			err = usbd_do_request(dev, &req, hd);
+		}
+	}
+
+	return err;
+}
+
+static usbd_status
+usbd_set_hub_depth(usbd_device_handle dev, int depth)
+{
+	usb_device_request_t req;
+
+	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+	req.bRequest = UR_SET_HUB_DEPTH;
+	USETW(req.wValue, depth);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, 0);
+	return usbd_do_request(dev, &req, 0);
+}
+
 int
 uhub_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -142,7 +266,6 @@ uhub_attach(device_t parent, device_t se
 	char *devinfop;
 	usbd_status err;
 	struct usbd_hub *hub = NULL;
-	usb_device_request_t req;
 	usb_hub_descriptor_t hubdesc;
 	int p, port, nports, nremov, pwrdly;
 	usbd_interface_handle iface;
@@ -182,18 +305,9 @@ uhub_attach(device_t parent, device_t se
 	}
 
 	/* Get hub descriptor. */
-	req.bmRequestType = UT_READ_CLASS_DEVICE;
-	req.bRequest = UR_GET_DESCRIPTOR;
-	USETW2(req.wValue, UDESC_HUB, 0);
-	USETW(req.wIndex, 0);
-	USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE);
-	DPRINTFN(1,("%s: getting hub descriptor\n", __func__));
-	err = usbd_do_request(dev, &req, &hubdesc);
+	memset(&hubdesc, 0, sizeof(hubdesc));
+	err = usbd_get_hub_desc(dev, &hubdesc, dev->speed);
 	nports = hubdesc.bNbrPorts;
-	if (!err && nports > 7) {
-		USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE + (nports+1) / 8);
-		err = usbd_do_request(dev, &req, &hubdesc);
-	}
 	if (err) {
 		DPRINTF(("%s: getting hub descriptor failed, error=%s\n",
 		    device_xname(sc->sc_dev), usbd_errstr(err)));
@@ -221,6 +335,15 @@ uhub_attach(device_t parent, device_t se
 	hub->explore = uhub_explore;
 	hub->hubdesc = hubdesc;
 
+	if (dev->speed == USB_SPEED_SUPER && dev->depth != 0) {
+		aprint_debug_dev(self, "setting hub depth %u\n", dev->depth-1);
+		err = usbd_set_hub_depth(dev, dev->depth - 1);
+		if (err) {
+			aprint_error_dev(self, "can't set depth\n");
+			goto bad;
+		}
+	}
+
 	/* Set up interrupt pipe. */
 	err = usbd_device2interface_handle(dev, 0, &iface);
 	if (err) {
@@ -253,6 +376,8 @@ uhub_attach(device_t parent, device_t se
 		goto bad;
 	if (device_is_a(device_parent(device_parent(sc->sc_dev)), "ehci"))
 		sc->sc_isehciroothub = 1;
+	if (device_is_a(device_parent(device_parent(sc->sc_dev)), "xhci"))
+		sc->sc_isxhciroothub = 1;
 
 	/* force initial scan */
 	memset(sc->sc_status, 0xff, sc->sc_statuslen);
@@ -280,9 +405,10 @@ uhub_attach(device_t parent, device_t se
 	 * These are the events on the bus when a hub is attached:
 	 *  Get device and config descriptors (see attach code)
 	 *  Get hub descriptor (see above)
+	 *  Set hub depth (if super speed)
 	 *  For all ports
 	 *     turn on power
-	 *     wait for power to become stable
+	 *  wait for power to become stable
 	 * (all below happens in explore code)
 	 *  For all ports
 	 *     clear C_PORT_CONNECTION
@@ -338,7 +464,8 @@ uhub_attach(device_t parent, device_t se
 		if (err)
 			aprint_error_dev(self, "port %d power on failed, %s\n",
 			    port, usbd_errstr(err));
-		DPRINTF(("usb_init_port: turn on port %d power\n", port));
+		DPRINTF(("%s: turn on port %d power\n",
+		    device_xname(sc->sc_dev), port));
 	}
 
 	/* Wait for stable power if we are not a root hub */
@@ -376,7 +503,8 @@ uhub_explore(usbd_device_handle dev)
 	int port;
 	int change, status, reconnect;
 
-	DPRINTFN(10, ("uhub_explore dev=%p addr=%d\n", dev, dev->address));
+	DPRINTFN(10, ("uhub_explore dev=%p addr=%d speed=%u at %s\n",
+	    dev, dev->address, dev->speed, device_xname(sc->sc_dev)));
 
 	if (!sc->sc_running)
 		return (USBD_NOT_STARTED);
@@ -396,6 +524,8 @@ uhub_explore(usbd_device_handle dev)
 			/* just acknowledge */
 			status = UGETW(hs.wHubStatus);
 			change = UGETW(hs.wHubChange);
+			DPRINTF(("%s: hub s/c=%x/%x\n",
+			       device_xname(sc->sc_dev), status, change));
 			if (change & UHS_LOCAL_POWER)
 				usbd_clear_hub_feature(dev,
 						       UHF_C_HUB_LOCAL_POWER);
@@ -424,10 +554,8 @@ uhub_explore(usbd_device_handle dev)
 			}
 			status = UGETW(up->status.wPortStatus);
 			change = UGETW(up->status.wPortChange);
-#if 0
-			printf("%s port %d: s/c=%x/%x\n",
-			       device_xname(sc->sc_dev), port, status, change);
-#endif
+			DPRINTF(("%s port %d: s/c=%x/%x\n",
+			   device_xname(sc->sc_dev), port, status, change));
 		}
 		if (!change && !reconnect) {
 			/* No status change, just do recursive explore. */
@@ -460,6 +588,19 @@ uhub_explore(usbd_device_handle dev)
 					    port);
 			}
 		}
+#if 1
+		if (change & UPS_C_PORT_RESET)
+			usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET);
+#endif
+		if (change & UPS_C_BH_PORT_RESET)
+			usbd_clear_port_feature(dev, port,
+			    UHF_C_BH_PORT_RESET);
+		if (change & UPS_C_PORT_LINK_STATE)
+			usbd_clear_port_feature(dev, port,
+			    UHF_C_PORT_LINK_STATE);
+		if (change & UPS_C_PORT_CONFIG_ERROR)
+			usbd_clear_port_feature(dev, port,
+			    UHF_C_PORT_CONFIG_ERROR);
 
 		/* XXX handle overcurrent and resume events! */
 
@@ -500,7 +641,16 @@ uhub_explore(usbd_device_handle dev)
 
 		/* Connected */
 
-		if (!(status & UPS_PORT_POWER))
+		DPRINTF(("%s: dev->speed=%u dev->depth=%u\n",
+		    device_xname(sc->sc_dev), dev->speed, dev->depth));
+		if ((!(status & UPS_PORT_POWER) &&
+		    /* check POWER for root/non-root hub if non-SS */
+		    ((dev->depth == 0 && !(status & UPS_SUPER_SPEED)) ||
+		     (dev->depth != 0 && dev->speed != USB_SPEED_SUPER))) ||
+		    /* check POWER_SS for root/non-root hub if SS */
+		    (!(status & UPS_PORT_POWER_SS) &&
+		    ((dev->depth == 0 && (status & UPS_SUPER_SPEED)) ||
+		     (dev->depth != 0 && dev->speed == USB_SPEED_SUPER))))
 			aprint_normal_dev(sc->sc_dev,
 			    "strange, connected port %d has no power\n", port);
 
@@ -522,6 +672,8 @@ uhub_explore(usbd_device_handle dev)
 		}
 		status = UGETW(up->status.wPortStatus);
 		change = UGETW(up->status.wPortChange);
+		DPRINTF(("%s port %d reset: s/c=%x/%x\n",
+		       device_xname(sc->sc_dev), port, status, change));
 		if (!(status & UPS_CURRENT_CONNECT_STATUS)) {
 			/* Nothing connected, just ignore it. */
 #ifdef DIAGNOSTIC
@@ -540,8 +692,9 @@ uhub_explore(usbd_device_handle dev)
 		}
 
 		/* Figure out device speed */
-#if 0
-		if (status & UPS_SUPER_SPEED)
+#if 1
+		if ((status & UPS_SUPER_SPEED) ||
+		    (dev->speed == USB_SPEED_SUPER && dev->depth != 0))
 			speed = USB_SPEED_SUPER;
 		else
 #endif
@@ -551,6 +704,7 @@ uhub_explore(usbd_device_handle dev)
 			speed = USB_SPEED_LOW;
 		else
 			speed = USB_SPEED_FULL;
+		DPRINTF(("%s: speed %u\n", device_xname(sc->sc_dev), speed));
 		/* Get device info and set its address. */
 		err = usbd_new_device(sc->sc_dev, dev->bus,
 			  dev->depth + 1, speed, port, up);
@@ -578,7 +732,7 @@ uhub_explore(usbd_device_handle dev)
 		}
 	}
 	/* enable status change notifications again */
-	if (!sc->sc_isehciroothub)
+	if (!sc->sc_isehciroothub && !sc->sc_isxhciroothub)
 		memset(sc->sc_status, 0, sc->sc_statuslen);
 	sc->sc_explorepending = 0;
 	return (USBD_NORMAL_COMPLETION);
@@ -726,4 +880,12 @@ uhub_intr(usbd_xfer_handle xfer, usbd_pr
 	if (status == USBD_NORMAL_COMPLETION && sc->sc_explorepending &&
 	    sc->sc_isehciroothub)
 		usb_needs_explore(sc->sc_hub);
+	/* 
+	 * XXX force re-scan all ports of xhci root hub
+	 */
+	if (status == USBD_NORMAL_COMPLETION && sc->sc_explorepending &&
+	    sc->sc_isxhciroothub) {
+		memset(sc->sc_status, 0xff, sc->sc_statuslen);
+		usb_needs_explore(sc->sc_hub);
+	}
 }
--- src/sys/dev/usb/usb.h.orig	2014-11-09 08:28:09.000000000 +0900
+++ src/sys/dev/usb/usb.h	2014-11-16 12:48:52.000000000 +0900
@@ -260,6 +263,7 @@ typedef struct {
 #define UC_REMOTE_WAKEUP	0x20
 	uByte		bMaxPower; /* max current in 2 mA units */
 #define UC_POWER_FACTOR 2
+#define UC_POWER_FACTOR_SS 8
 } UPACKED usb_config_descriptor_t;
 #define USB_CONFIG_DESCRIPTOR_SIZE 9
 
@@ -330,6 +334,7 @@ typedef struct {
 } UPACKED usb_endpoint_ss_comp_descriptor_t;
 #define USB_ENDPOINT_SS_COMP_DESCRIPTOR_SIZE 6
 
+/* USB 3.0 9.6.2, Table 9-12 */
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
@@ -338,10 +343,12 @@ typedef struct {
 } UPACKED usb_bos_descriptor_t;
 #define USB_BOS_DESCRIPTOR_SIZE 5
 
+/* common members of device capability descriptors */
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
 	uByte		bDevCapabilityType;
+/* Table 9-14 */
 #define USB_DEVCAP_RESERVED			0x00
 #define USB_DEVCAP_WUSB				0x01
 #define USB_DEVCAP_USB2EXT			0x02
@@ -357,17 +364,19 @@ typedef struct {
 #define USB_DEVCAP_WUSB_EXT			0x0c
 	/* data ... */
 } UPACKED usb_device_capability_descriptor_t;
-#define USB_DEVICE_CAPABILITY_DESCRIPTOR_SIZE 3 /* variable length */
+#define USB_DEVICE_CAPABILITY_DESCRIPTOR_SIZE 3 /* at least */
 
+/* 9.6.2.1 */
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
 	uByte		bDevCapabilityType;
 	uDWord		bmAttributes;
 #define USB_DEVCAP_USB2EXT_LPM __BIT(1)
-} UPACKED usb_usb2ext_descriptor_t;
+} UPACKED usb_devcap_usb2ext_descriptor_t;
 #define USB_DEVCAP_USB2EXT_DESCRIPTOR_SIZE 7
 
+/* 9.6.2.2 */
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
@@ -375,16 +384,17 @@ typedef struct {
 	uByte		bmAttributes;
 #define USB_DEVCAP_SS_LTM __BIT(1)
 	uWord		wSpeedsSupported;
-#define USB_DEVCAP_SS_SPEED_SS __BIT(0)
+#define USB_DEVCAP_SS_SPEED_LS __BIT(0)
 #define USB_DEVCAP_SS_SPEED_FS __BIT(1)
 #define USB_DEVCAP_SS_SPEED_HS __BIT(2)
-#define USB_DEVCAP_SS_SPEED_LS __BIT(3)
+#define USB_DEVCAP_SS_SPEED_SS __BIT(3)
 	uByte		bFunctionalitySupport;
 	uByte		bU1DevExitLat;
 	uWord		wU2DevExitLat;
 } UPACKED usb_devcap_ss_descriptor_t;
 #define USB_DEVCAP_SS_DESCRIPTOR_SIZE 10
 
+/* 9.6.2.4 */
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
@@ -749,7 +759,9 @@ typedef struct {
 #endif
 
 #define USB_MIN_POWER		100 /* mA */
+#define USB_MIN_POWER_SS	150 /* mA */
 #define USB_MAX_POWER		500 /* mA */
+#define USB_MAX_POWER_SS	900 /* mA */
 
 #define USB_BUS_RESET_DELAY	100 /* ms XXX?*/
 
--- src/sys/dev/usb/usb_subr.c.orig	2014-09-22 02:57:10.000000000 +0900
+++ src/sys/dev/usb/usb_subr.c	2014-11-14 19:15:27.000000000 +0900
@@ -531,6 +531,7 @@ usbd_status
 usbd_set_config_index(usbd_device_handle dev, int index, int msg)
 {
 	usb_config_descriptor_t cd, *cdp;
+	usb_bos_descriptor_t bd, *bdp = NULL;
 	usbd_status err;
 	int i, ifcidx, nifc, len, selfpowered, power;
 
@@ -597,6 +598,42 @@ usbd_set_config_index(usbd_device_handle
 		goto bad;
 	}
 
+	if (dev->speed == USB_SPEED_SUPER) {
+		/* get short bos desc */
+		err = usbd_get_bos_desc(dev, index, &bd);
+		if (err) {
+			DPRINTF(("usbd_set_config_index: get_bos_desc=%d\n",
+			    err));
+			goto bad;
+		}
+		len = UGETW(bd.wTotalLength);
+		bdp = malloc(len, M_USB, M_NOWAIT);
+		if (bdp == NULL) {
+			err = USBD_NOMEM;
+			goto bad;
+		}
+
+		/* Get the full desc */
+		for (i = 0; i < 3; i++) {
+			err = usbd_get_desc(dev, UDESC_BOS, index, len, bdp);
+			if (!err)
+				break;
+			usbd_delay_ms(dev, 200);
+		}
+		if (err) {
+			DPRINTF(("usbd_set_config_index: get_bos_desc=%d\n",
+			    err));
+			goto bad;
+		}
+		if (bdp->bDescriptorType != UDESC_BOS) {
+			DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n",
+				     bdp->bDescriptorType));
+			err = USBD_INVAL;
+			goto bad;
+		}
+	}
+	dev->bdesc = bdp;
+
 	/*
 	 * Figure out if the device is self or bus powered.
 	 */
@@ -689,6 +726,10 @@ usbd_set_config_index(usbd_device_handle
 
  bad:
 	free(cdp, M_USB);
+	if (bdp != NULL) {
+		free(bdp, M_USB);
+		dev->bdesc = NULL;
+	}
 	return (err);
 }
 
@@ -1126,7 +1167,7 @@ usbd_new_device(device_t parent, usbd_bu
 				goto found;
 			}
 		}
-		panic("usbd_new_device: cannot find HS port\n");
+		panic("usbd_new_device: cannot find HS port");
 	found:
 		DPRINTFN(1,("usbd_new_device: high speed port %d\n", p));
 	} else {
@@ -1415,6 +1456,13 @@ usbd_fill_deviceinfo(usbd_device_handle 
 					err = USB_PORT_ENABLED;
 				else if (s & UPS_SUSPEND)
 					err = USB_PORT_SUSPENDED;
+				/*
+				 * UPS_PORT_POWER_SS is available only
+				 * if SS, otherwise it means UPS_LOW_SPEED.
+				 */
+				else if ((s & UPS_PORT_POWER_SS) &&
+				    dev->speed == USB_SPEED_SUPER)
+					err = USB_PORT_POWERED;
 				else if (s & UPS_PORT_POWER)
 					err = USB_PORT_POWERED;
 				else
--- src/sys/dev/usb/usbdi_util.c.orig	2014-09-13 22:14:45.000000000 +0900
+++ src/sys/dev/usb/usbdi_util.c	2014-11-13 07:56:59.000000000 +0900
@@ -105,6 +105,33 @@ usbd_get_config_desc_full(usbd_device_ha
 }
 
 usbd_status
+usbd_get_bos_desc(usbd_device_handle dev, int confidx,
+		     usb_bos_descriptor_t *d)
+{
+	usbd_status err;
+
+	DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx));
+	err = usbd_get_desc(dev, UDESC_BOS, confidx,
+			    USB_BOS_DESCRIPTOR_SIZE, d);
+	if (err)
+		return (err);
+	if (d->bDescriptorType != UDESC_BOS) {
+		DPRINTFN(-1,("usbd_get_bos_desc: confidx=%d, bad desc "
+			     "len=%d type=%d\n",
+			     confidx, d->bLength, d->bDescriptorType));
+		return (USBD_INVAL);
+	}
+	return (USBD_NORMAL_COMPLETION);
+}
+
+usbd_status
+usbd_get_bos_desc_full(usbd_device_handle dev, int conf, void *d, int size)
+{
+	DPRINTFN(3,("usbd_get_bos_desc_full: conf=%d\n", conf));
+	return (usbd_get_desc(dev, UDESC_BOS, conf, size, d));
+}
+
+usbd_status
 usbd_get_device_desc(usbd_device_handle dev, usb_device_descriptor_t *d)
 {
 	DPRINTFN(3,("usbd_get_device_desc:\n"));
--- src/sys/dev/usb/usbdi_util.h.orig	2013-09-28 09:25:34.000000000 +0900
+++ src/sys/dev/usb/usbdi_util.h	2014-11-13 07:59:24.000000000 +0900
@@ -40,6 +40,9 @@ usbd_status	usbd_get_desc(usbd_device_ha
 usbd_status	usbd_get_config_desc(usbd_device_handle, int,
 				     usb_config_descriptor_t *);
 usbd_status	usbd_get_config_desc_full(usbd_device_handle, int, void *, int);
+usbd_status	usbd_get_bos_desc(usbd_device_handle, int,
+				     usb_bos_descriptor_t *);
+usbd_status	usbd_get_bos_desc_full(usbd_device_handle, int, void *, int);
 usbd_status	usbd_get_device_desc(usbd_device_handle dev,
 				     usb_device_descriptor_t *d);
 usbd_status	usbd_set_address(usbd_device_handle dev, int addr);
--- src/sys/dev/usb/usbdivar.h.orig	2014-09-06 20:15:44.000000000 +0900
+++ src/sys/dev/usb/usbdivar.h	2014-11-13 08:09:30.000000000 +0900
@@ -191,6 +191,7 @@ struct usbd_device {
 	struct usbd_interface  *ifaces;        /* array of all interfaces */
 	usb_device_descriptor_t ddesc;         /* device descriptor */
 	usb_config_descriptor_t *cdesc;	       /* full config descr */
+	usb_bos_descriptor_t	*bdesc;	       /* full BOS descr */
 	const struct usbd_quirks     *quirks;  /* device quirks, always set */
 	struct usbd_hub	       *hub;           /* only if this is a hub */
 	int			subdevlen;     /* array length of following */
--- src/sys/dev/usb/xhci.c.orig	2014-11-18 19:58:02.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2014-11-18 23:45:22.000000000 +0900
@@ -51,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.2
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
 #include <dev/usb/usbhist.h>
 #include <dev/usb/usb_mem.h>
 #include <dev/usb/usb_quirks.h>
@@ -60,8 +69,7 @@ __KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.2
 #include <dev/usb/usbroothub_subr.h>
 
 
-#ifdef USB_DEBUG
-#ifndef XHCI_DEBUG
+#if !(defined(USB_DEBUG) && defined(XHCI_DEBUG))
 #define xhcidebug 0
 #else
 static int xhcidebug = 0;
@@ -93,8 +101,7 @@ fail:
 	aprint_error("%s: sysctl_createv failed (err = %d)\n", __func__, err);
 }
 
-#endif /* XHCI_DEBUG */
-#endif /* USB_DEBUG */
+#endif /* !(XHCI_DEBUG && USB_DEBUG) */
 
 #define DPRINTFN(N,FMT,A,B,C,D) USBHIST_LOGN(xhcidebug,N,FMT,A,B,C,D)
 #define XHCIHIST_FUNC() USBHIST_FUNC()
@@ -105,6 +112,19 @@ fail:
 
 #define XHCI_ICI_INPUT_CONTROL 0
 
+#define IS_VALID_XFER(X)				\
+	((X) != NULL &&					\
+	 (X)->pipe != NULL &&				\
+	 (X)->pipe->device != NULL &&			\
+	 (X)->pipe->device->bus != NULL &&		\
+	 (X)->pipe->device->bus->devices[(X)->pipe->device->address] != NULL &&\
+	 !SIMPLEQ_EMPTY(&(X)->pipe->queue))
+
+#define usb_transfer_complete(X) do {			\
+		if (IS_VALID_XFER(X))			\
+			usb_transfer_complete(X);	\
+	} while(0)
+
 struct xhci_pipe {
 	struct usbd_pipe xp_pipe;
 };
@@ -130,16 +150,18 @@ static usbd_status xhci_new_device(devic
 static usbd_status xhci_configure_endpoint(usbd_pipe_handle);
 static usbd_status xhci_unconfigure_endpoint(usbd_pipe_handle);
 static usbd_status xhci_reset_endpoint(usbd_pipe_handle);
-//static usbd_status xhci_stop_endpoint(usbd_pipe_handle);
+static usbd_status xhci_stop_endpoint(usbd_pipe_handle);
 
 static usbd_status xhci_set_dequeue(usbd_pipe_handle);
 
 static usbd_status xhci_do_command(struct xhci_softc * const,
     struct xhci_trb * const, int);
-static usbd_status xhci_init_slot(struct xhci_softc * const, uint32_t,
-    int, int, int, int);
+static usbd_status xhci_do_command_locked(struct xhci_softc * const,
+    struct xhci_trb * const, int);
+static usbd_status xhci_init_slot(usbd_device_handle, uint32_t, int, int);
 static usbd_status xhci_enable_slot(struct xhci_softc * const,
     uint8_t * const);
+static usbd_status xhci_disable_slot(struct xhci_softc * const, uint8_t);
 static usbd_status xhci_address_device(struct xhci_softc * const,
     uint64_t, uint8_t, bool);
 static usbd_status xhci_update_ep0_mps(struct xhci_softc * const,
@@ -392,7 +414,7 @@ xhci_db_write_4(const struct xhci_softc 
 static inline uint8_t
 xhci_ep_get_type(usb_endpoint_descriptor_t * const ed)
 {
-	u_int eptype;
+	u_int eptype = 0;
 
 	switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
 	case UE_CONTROL:
@@ -606,17 +628,17 @@ int
 xhci_init(struct xhci_softc *sc)
 {
 	bus_size_t bsz;
-	uint32_t cap, hcs1, hcs2, hcc, dboff, rtsoff;
+	uint32_t cap, hcs1, hcs2, hcs3, hcc, dboff, rtsoff;
 	uint32_t ecp, ecr;
 	uint32_t usbcmd, usbsts, pagesize, config;
 	int i;
 	uint16_t hciversion;
 	uint8_t caplength;
 
-	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+	/* XXX If USBHIST_CALLED is called before USBHIST_INIT, it panics. */
+	//XHCIHIST_FUNC(); XHCIHIST_CALLED();
 
-	/* XXX Low/Full/High speeds for now */
-	sc->sc_bus.usbrev = USBREV_2_0;
+	sc->sc_bus.usbrev = USBREV_3_0;
 
 	cap = xhci_read_4(sc, XHCI_CAPLENGTH);
 	caplength = XHCI_CAP_CAPLENGTH(cap);
@@ -642,13 +664,24 @@ xhci_init(struct xhci_softc *sc)
 	sc->sc_maxintrs = XHCI_HCS1_MAXINTRS(hcs1);
 	sc->sc_maxports = XHCI_HCS1_MAXPORTS(hcs1);
 	hcs2 = xhci_cap_read_4(sc, XHCI_HCSPARAMS2);
-	(void)xhci_cap_read_4(sc, XHCI_HCSPARAMS3);
+	hcs3 = xhci_cap_read_4(sc, XHCI_HCSPARAMS3);
+	sc->sc_rh_u1del = XHCI_HCS3_U1_DEL(hcs3);
+	sc->sc_rh_u2del = XHCI_HCS3_U2_DEL(hcs3);
 	hcc = xhci_cap_read_4(sc, XHCI_HCCPARAMS);
 
 	sc->sc_ac64 = XHCI_HCC_AC64(hcc);
 	sc->sc_ctxsz = XHCI_HCC_CSZ(hcc) ? 64 : 32;
 	aprint_debug_dev(sc->sc_dev, "ac64 %d ctxsz %d\n", sc->sc_ac64,
 	    sc->sc_ctxsz);
+	char sbuf[128];
+	snprintb(sbuf, sizeof sbuf, "\177\020"
+	    "f\020\020XECP\0f\014\4MAXPSA\0"
+	    "b\7NSS\0b\6LTC\0b\5LHRC\0b\4PIND\0b\3PPC\0"
+	    "b\2CZC\0b\1BNC\0b\0AC64\0\0",
+	    hcc);
+	aprint_debug_dev(sc->sc_dev, "hcc=%s\n", sbuf);
+	aprint_debug_dev(sc->sc_dev, "u1del %u u2del %u\n",
+	    sc->sc_rh_u1del, sc->sc_rh_u2del);
 
 	aprint_debug_dev(sc->sc_dev, "xECP %x\n", XHCI_HCC_XECP(hcc) * 4);
 	ecp = XHCI_HCC_XECP(hcc) * 4;
@@ -749,6 +782,7 @@ xhci_init(struct xhci_softc *sc)
 	aprint_debug_dev(sc->sc_dev, "sc_pgsz 0x%08x\n", (uint32_t)sc->sc_pgsz);
 	aprint_debug_dev(sc->sc_dev, "sc_maxslots 0x%08x\n",
 	    (uint32_t)sc->sc_maxslots);
+	aprint_debug_dev(sc->sc_dev, "sc_maxports %d\n", sc->sc_maxports);
 
 	usbd_status err;
 
@@ -865,7 +899,15 @@ xhci_init(struct xhci_softc *sc)
 #endif
 
 	xhci_rt_write_4(sc, XHCI_IMAN(0), XHCI_IMAN_INTR_ENA);
-	xhci_rt_write_4(sc, XHCI_IMOD(0), 0);
+	/* Intel PantherPoint/LynxPoint/BayTrail need interrupt rate capped */
+#if 0
+	if (!(sc->sc_quirks & XHCI_QUIRK_INTEL_IMOD)) {
+		xhci_rt_write_4(sc, XHCI_IMOD(0), XHCI_IMOD_DEFAULT);
+	}
+#else
+	/* XXX add quirk later */
+	xhci_rt_write_4(sc, XHCI_IMOD(0), 160);
+#endif
 
 	xhci_op_write_4(sc, XHCI_USBCMD, XHCI_CMD_INTE|XHCI_CMD_RS); /* Go! */
 	aprint_debug_dev(sc->sc_dev, "USBCMD %08"PRIx32"\n",
@@ -952,6 +994,129 @@ xhci_intr1(struct xhci_softc * const sc)
 	return 1;
 }
 
+/*
+ * 3 port speed types used in USB stack
+ *
+ * usbdi speed
+ *	definition: USB_SPEED_* in usb.h
+ *	They are used in usbd_device_handle in USB stack.
+ *	ioctl interface uses these values too.
+ * port_status speed
+ *	definition: UPS_*_SPEED in usb.h
+ *	They are used in usb_port_status_t.
+ *	Some 3.0 values overlap with 2.0 values.
+ *	(e.g. 0x200 means UPS_POER_POWER_SS in SS and
+ *	            means UPS_LOW_SPEED in HS.)
+ *	port status sent from hub also uses these values.
+ *	(but I've never seen UPS_SUPER_SPEED in port_status from hub.)
+ * xspeed:
+ *	definition: Protocol Speed ID (PSI) (xHCI 3.1 7.2.1)
+ *	They are used in only slot context and PORTSC reg of xhci.
+ *	The difference between usbdi speed and them are that
+ *	FS and LS values are swapped.
+ */
+
+static int
+xhci_speed2xspeed(int speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:	return 2;
+	case USB_SPEED_FULL:	return 1;
+	case USB_SPEED_HIGH:	return 3;
+	case USB_SPEED_SUPER:	return 4;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static void
+xhci_setup_sctx(usbd_device_handle dev, uint32_t *cp)
+{
+	usb_device_descriptor_t * const dd = &dev->ddesc;
+	int speed = dev->speed;
+	int tthubslot, ttportnum;
+	bool ishub;
+	bool usemtt;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	/* 6.2.2 */
+	/*
+	 * tthubslot:
+	 *   This is the slot ID of parent HS hub
+	 *   if LS/FS device is connected && connected through HS hub.
+	 *   This is 0 if device is not LS/FS device ||
+	 *   attached to root hub.
+	 * ttportnum:
+	 *   This is the downstream facing port of parent HS hub
+	 *   if LS/FS device is connected.
+	 *   This is 0 if device is not LS/FS device ||
+	 *   attached to root hub.
+	 */
+	if (dev->myhsport != NULL &&
+	    (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL)) {
+		ttportnum = dev->myhsport->portno;
+		/* XXX addr == slot ? */
+		tthubslot = dev->myhsport->parent->address;
+	} else {
+		ttportnum = 0;
+		tthubslot = 0;
+	}
+	DPRINTFN(4, "myhsport %p ttportnum=%d tthubslot=%d",
+	    dev->myhsport, ttportnum, tthubslot, 0);
+
+	/* ishub is valid after reading UDESC_DEVICE */
+	ishub = (dd->bDeviceClass == UDCLASS_HUB);
+
+	/* dev->hub is valid after reading UDESC_HUB */
+	if (ishub && dev->hub) {
+		usb_hub_descriptor_t *hd = &dev->hub->hubdesc;
+
+		cp[1] |= htole32(XHCI_SCTX_1_NUM_PORTS_SET(hd->bNbrPorts));
+		cp[2] |= htole32(XHCI_SCTX_2_TT_THINK_TIME_SET(
+		    __SHIFTOUT(UGETW(hd->wHubCharacteristics), UHD_TT_THINK)));
+		DPRINTFN(4, "nports=%d ttt=%d",
+		    hd->bNbrPorts, XHCI_SCTX_2_TT_THINK_TIME_GET(cp[2]), 0, 0);
+	}
+
+#define IS_TTHUB(dd) \
+    ((dd)->bDeviceProtocol == UDPROTO_HSHUBSTT || \
+     (dd)->bDeviceProtocol == UDPROTO_HSHUBMTT)
+
+	/*
+	 * MTT flag is set if
+	 * 1. this is HS hub && MTT is enabled
+	 *  or
+	 * 2. this is not hub && this is LS or FS device &&
+	 *    MTT of parent HS hub (and its parent, too) is enabled
+	 */
+	if (ishub && speed == USB_SPEED_HIGH && IS_TTHUB(dd))
+		usemtt = true;
+	else if (!ishub &&
+	     (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) &&
+	     dev->myhsport != NULL &&
+	     IS_TTHUB(&dev->myhsport->parent->ddesc))
+		usemtt = true;
+	else
+		usemtt = false;
+	DPRINTFN(4, "class %u proto %u ishub %d usemtt %d",
+	    dd->bDeviceClass, dd->bDeviceProtocol, ishub, usemtt);
+
+	cp[0] |= htole32(
+	    XHCI_SCTX_0_SPEED_SET(xhci_speed2xspeed(speed)) |
+	    XHCI_SCTX_0_HUB_SET(ishub ? 1 : 0) |
+	    XHCI_SCTX_0_MTT_SET(usemtt ? 1 : 0)
+	    );
+	cp[1] |= htole32(0);
+	cp[2] |= htole32(
+	    XHCI_SCTX_2_IRQ_TARGET_SET(0) |
+	    XHCI_SCTX_2_TT_HUB_SID_SET(tthubslot) |
+	    XHCI_SCTX_2_TT_PORT_NUM_SET(ttportnum)
+	    );
+	cp[3] |= htole32(0);
+}
+
 static usbd_status
 xhci_configure_endpoint(usbd_pipe_handle pipe)
 {
@@ -963,10 +1128,14 @@ xhci_configure_endpoint(usbd_pipe_handle
 	struct xhci_trb trb;
 	usbd_status err;
 	uint32_t *cp;
+	uint32_t mps = UGETW(ed->wMaxPacketSize);
+	uint32_t maxb = 0;
+	int speed = pipe->device->speed;
+	uint32_t ival = ed->bInterval;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "dci %u epaddr 0x%02x attr 0x%02x",
-	    dci, ed->bEndpointAddress, ed->bmAttributes, 0);
+	DPRINTFN(4, "slot %u dci %u epaddr 0x%02x attr 0x%02x",
+	    xs->xs_idx, dci, ed->bEndpointAddress, ed->bmAttributes);
 
 	/* XXX ensure input context is available? */
 
@@ -978,33 +1147,96 @@ xhci_configure_endpoint(usbd_pipe_handle
 
 	/* set up input slot context */
 	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
-	cp[0] = htole32(XHCI_SCTX_0_CTX_NUM_SET(dci));
-	cp[1] = htole32(0);
-	cp[2] = htole32(0);
-	cp[3] = htole32(0);
+	xhci_setup_sctx(pipe->device, cp);
+	cp[0] |= htole32(XHCI_SCTX_0_CTX_NUM_SET(dci));
 
 	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(dci));
-	if (xfertype == UE_INTERRUPT) {
 	cp[0] = htole32(
-	    XHCI_EPCTX_0_IVAL_SET(3) /* XXX */
+	    XHCI_EPCTX_0_EPSTATE_SET(0) |
+	    XHCI_EPCTX_0_MAXP_STREAMS_SET(0) |
+	    XHCI_EPCTX_0_LSA_SET(0)
 	    );
 	cp[1] = htole32(
-	    XHCI_EPCTX_1_CERR_SET(3) |
-	    XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(pipe->endpoint->edesc)) |
-	    XHCI_EPCTX_1_MAXB_SET(0) |
-	    XHCI_EPCTX_1_MAXP_SIZE_SET(8) /* XXX */
-	    );
-	cp[4] = htole32(
-		XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)
-		);
-	} else {
-	cp[0] = htole32(0);
-	cp[1] = htole32(
-	    XHCI_EPCTX_1_CERR_SET(3) |
-	    XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(pipe->endpoint->edesc)) |
-	    XHCI_EPCTX_1_MAXB_SET(0) |
-	    XHCI_EPCTX_1_MAXP_SIZE_SET(512) /* XXX */
+	    XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(ed)) |
+	    XHCI_EPCTX_1_MAXB_SET(0)
 	    );
+	if (xfertype != UE_ISOCHRONOUS)
+		cp[1] |= htole32(XHCI_EPCTX_1_CERR_SET(3));
+
+	if (speed == USB_SPEED_SUPER) {
+		const usb_endpoint_ss_comp_descriptor_t * esscd;
+
+		/* XXX */
+		esscd = (const usb_endpoint_ss_comp_descriptor_t *)
+		    usb_find_desc(pipe->device,
+		    UDESC_ENDPOINT_SS_COMP, USBD_CDCSUBTYPE_ANY);
+		if (esscd != NULL) {
+			maxb = esscd->bMaxBurst;
+			cp[1] |= htole32(XHCI_EPCTX_1_MAXB_SET(maxb));
+			DPRINTFN(4, "setting SS MaxBurst %u", maxb, 0, 0, 0);
+		}
+	}
+	if (speed == USB_SPEED_HIGH &&
+	    (xfertype == UE_ISOCHRONOUS || xfertype == UE_INTERRUPT)) {
+		maxb = UE_GET_TRANS(UGETW(ed->wMaxPacketSize));
+		cp[1] |= htole32(XHCI_EPCTX_1_MAXB_SET(maxb));
+		DPRINTFN(4, "setting HS MaxBurst %u", maxb, 0, 0, 0);
+	}
+
+	switch (xfertype) {
+	case UE_INTERRUPT:
+		/* 6.2.3.6  */
+		if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) {
+			ival = ival > 10 ? 10 : ival;
+			ival = ival < 3 ? 3 : ival;
+		} else {
+			ival = ival > 15 ? 15 : ival;
+		}
+		if (speed == USB_SPEED_SUPER) {
+			if (maxb > 0)
+				mps = 1024;
+		} else {
+			mps = mps ? mps : 8;
+		}
+		cp[0] |= htole32(XHCI_EPCTX_0_IVAL_SET(ival));
+		cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps));
+		cp[4] = htole32(
+		    XHCI_EPCTX_4_AVG_TRB_LEN_SET(8) /* XXX */
+		    );
+		break;
+	case UE_CONTROL:
+		if (speed == USB_SPEED_SUPER)
+			mps = 512;
+		else
+			mps = mps ? mps : 8;
+		cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps));
+		cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)); /* XXX */
+		break;
+#ifdef notyet
+	case UE_ISOCHRONOUS:
+		if (speed == USB_SPEED_FULL) {
+			ival = ival > 18 ? 18 : ival;
+			ival = ival < 3 ? 3 : ival;
+		} else {
+			ival = ival > 15 ? 15 : ival;
+		}
+		if (speed == USB_SPEED_SUPER) {
+			mps = 1024;
+		} else {
+			mps = mps ? mps : 1024;
+		}
+		cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps));
+		cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(1024)); /* XXX */
+		break;
+#endif
+	default:
+		if (speed == USB_SPEED_SUPER)
+			mps = 1024;
+		else
+			mps = mps ? mps : 512;
+		cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps));
+		cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(1024)); /* XXX */
+		break;
 	}
 	*(uint64_t *)(&cp[2]) = htole64(
 	    xhci_ring_trbp(&xs->xs_ep[dci].xe_tr, 0) |
@@ -1034,16 +1266,82 @@ xhci_configure_endpoint(usbd_pipe_handle
 static usbd_status
 xhci_unconfigure_endpoint(usbd_pipe_handle pipe)
 {
-#ifdef USB_DEBUG
+	struct xhci_softc * const sc = pipe->device->bus->hci_private;
 	struct xhci_slot * const xs = pipe->device->hci_private;
-#endif
+	usb_endpoint_descriptor_t * const ed = pipe->endpoint->edesc;
+	const u_int dci = xhci_ep_get_dci(ed);
+	struct xhci_trb trb;
+	usbd_status err;
+	uint32_t *cp;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "slot %u", xs->xs_idx, 0, 0, 0);
 
-	return USBD_NORMAL_COMPLETION;
+	if (sc->sc_dying)
+		return USBD_IOERROR;
+
+	if (xs == NULL)
+		/* xs is uninitialized before xhci_init_slot */
+		return USBD_IOERROR;
+
+	DPRINTFN(1, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
+
+	/* XXX RACE */
+	if (cpu_intr_p() || cpu_softintr_p()) {
+#ifdef DIAGNOSTIC
+		DPRINTFN(1, "WARNING: called from intr ctx, giving up",
+		    0, 0, 0, 0);
+		return USBD_IOERROR;
+#else
+		DPRINTFN(4, "WARNING: called from intr ctx", 0, 0, 0, 0);
+#endif
+	}
+
+	/* XXX RACE */
+	if (!mutex_owned(&sc->sc_lock)) {
+#ifdef DIAGNOSTIC
+		DPRINTFN(1, "WARNING: not locked, giving up", 0, 0, 0, 0);
+		return USBD_IOERROR;
+#else
+		DPRINTFN(4, "WARNING: not locked", 0, 0, 0, 0);
+#endif
+	}
+
+	if (dci == XHCI_DCI_EP_CONTROL) {
+		DPRINTFN(4, "closing ep0", 0, 0, 0, 0);
+		xhci_disable_slot(sc, xs->xs_idx);
+		return USBD_NORMAL_COMPLETION;
+	}
+
+	err = xhci_stop_endpoint(pipe);
+
+	memset(xhci_slot_get_icv(sc, xs, 0), 0, sc->sc_pgsz);
+
+	cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL);
+	cp[0] = htole32(XHCI_INCTX_0_DROP_MASK(dci));
+	cp[1] = htole32(0);
+
+	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
+	cp[0] = htole32(XHCI_SCTX_0_CTX_NUM_SET(dci));
+
+	/* sync input contexts before they are read from memory */
+	usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE);
+
+	/*
+	 * don't set DC bit 1, otherwise all endpoints but ep0
+	 * would be deconfigured.
+	 */
+	trb.trb_0 = xhci_slot_get_icp(sc, xs, 0);
+	trb.trb_2 = 0;
+	trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
+	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP);
+
+	err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
+
+	return err;
 }
 
+/* 4.6.8, 6.4.3.7 */
 static usbd_status
 xhci_reset_endpoint(usbd_pipe_handle pipe)
 {
@@ -1054,7 +1352,7 @@ xhci_reset_endpoint(usbd_pipe_handle pip
 	usbd_status err;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "dci %u", dci, 0, 0, 0);
+	DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
 	trb.trb_0 = 0;
 	trb.trb_2 = 0;
@@ -1067,7 +1365,6 @@ xhci_reset_endpoint(usbd_pipe_handle pip
 	return err;
 }
 
-#if 0
 static usbd_status
 xhci_stop_endpoint(usbd_pipe_handle pipe)
 {
@@ -1078,7 +1375,7 @@ xhci_stop_endpoint(usbd_pipe_handle pipe
 	const u_int dci = xhci_ep_get_dci(pipe->endpoint->edesc);
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "dci %u", dci, 0, 0, 0);
+	DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
 	trb.trb_0 = 0;
 	trb.trb_2 = 0;
@@ -1086,11 +1383,10 @@ xhci_stop_endpoint(usbd_pipe_handle pipe
 	    XHCI_TRB_3_EP_SET(dci) |
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP);
 
-	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
 
 	return err;
 }
-#endif
 
 static usbd_status
 xhci_set_dequeue(usbd_pipe_handle pipe)
@@ -1139,8 +1435,7 @@ xhci_open(usbd_pipe_handle pipe)
 		return USBD_IOERROR;
 
 	/* Root Hub */
-	if (dev->depth == 0 && dev->powersrc->portno == 0 &&
-	    dev->speed != USB_SPEED_SUPER) {
+	if (dev->depth == 0 && dev->powersrc->portno == 0) {
 		switch (ed->bEndpointAddress) {
 		case USB_CONTROL_ENDPOINT:
 			pipe->methods = &xhci_root_ctrl_methods;
@@ -1177,11 +1472,54 @@ xhci_open(usbd_pipe_handle pipe)
 	}
 
 	if (ed->bEndpointAddress != USB_CONTROL_ENDPOINT)
-		xhci_configure_endpoint(pipe);
+		return xhci_configure_endpoint(pipe);
 
 	return USBD_NORMAL_COMPLETION;
 }
 
+#if 1 /* XXX experimental */
+static void
+xhci_clear_endpoint_stall_task(void *cookie)
+{
+	usbd_xfer_handle const xfer = cookie;
+	struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+	struct xhci_slot * const xs = xfer->pipe->device->hci_private;
+	const u_int dci = xhci_ep_get_dci(xfer->pipe->endpoint->edesc);
+	struct xhci_ring * const tr = &xs->xs_ep[dci].xe_tr;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+	DPRINTFN(4, "xfer %p slot %u dci %u", xfer, xs->xs_idx, dci, 0);
+
+	xhci_reset_endpoint(xfer->pipe);
+	xhci_set_dequeue(xfer->pipe);
+
+	mutex_enter(&sc->sc_lock);
+	tr->is_halted = false;
+	usb_transfer_complete(xfer);
+	mutex_exit(&sc->sc_lock);
+	DPRINTFN(4, "ends", 0, 0, 0, 0);
+}
+
+static void
+xhci_clear_endpoint_stall_async(usbd_xfer_handle xfer)
+{
+	struct xhci_softc * const sc = xfer->pipe->device->bus->hci_private;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+	DPRINTFN(4, "xfer %p", xfer, 0, 0, 0);
+
+	if (sc->sc_dying) {
+		return;
+	}
+
+	usb_init_task(&xfer->pipe->async_task, xhci_clear_endpoint_stall_task,
+	    xfer, USB_TASKQ_MPSAFE);
+	usb_add_task(xfer->pipe->device, &xfer->pipe->async_task,
+	    USB_TASKQ_HC);
+	DPRINTFN(4, "ends", 0, 0, 0, 0);
+}
+#endif /* XXX experimental */
+
 static void
 xhci_rhpsc(struct xhci_softc * const sc, u_int port)
 {
@@ -1194,13 +1532,6 @@ xhci_rhpsc(struct xhci_softc * const sc,
 	if (xfer == NULL)
 		return;
 
-	if (!(port >= sc->sc_hs_port_start &&
-	    port < sc->sc_hs_port_start + sc->sc_hs_port_count))
-		return;
-
-	port -= sc->sc_hs_port_start;
-	port += 1;
-	DPRINTFN(4, "hs port %u status change", port, 0, 0, 0);
 
 	p = KERNADDR(&xfer->dmabuf, 0);
 	memset(p, 0, xfer->length);
@@ -1219,6 +1550,8 @@ xhci_handle_event(struct xhci_softc * co
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 
+	KASSERT(mutex_owned(&sc->sc_lock));
+
 	trb_0 = le64toh(trb->trb_0);
 	trb_2 = le32toh(trb->trb_2);
 	trb_3 = le32toh(trb->trb_3);
@@ -1248,7 +1581,20 @@ xhci_handle_event(struct xhci_softc * co
 			xx = (void *)(uintptr_t)(trb_0 & ~0x3);
 		}
 		xfer = &xx->xx_xfer;
+		/*
+		 * stop_endpoint may cause ERR_STOPPED_LENGTH_INVALID,
+		 * in which case this condition happens.
+		 */ 
+		if (xfer == NULL) {
+			DPRINTFN(1, "xfer done: xfer is NULL", 0, 0, 0, 0);
+			break;
+		}
+		/* XXX xfer needs more validation */
+
 		DPRINTFN(14, "xfer %p", xfer, 0, 0, 0);
+		/* may happen when detaching */
+		if (!IS_VALID_XFER(xfer))
+			break;
 
 		if ((trb_3 & XHCI_TRB_3_ED_BIT) != 0) {
 			DPRINTFN(14, "transfer event data: "
@@ -1260,20 +1606,30 @@ xhci_handle_event(struct xhci_softc * co
 			}
 		}
 
-		if (XHCI_TRB_2_ERROR_GET(trb_2) ==
-		    XHCI_TRB_ERROR_SUCCESS) {
+		uint8_t trberr = XHCI_TRB_2_ERROR_GET(trb_2);
+		if (trberr == XHCI_TRB_ERROR_SUCCESS ||
+		    trberr == XHCI_TRB_ERROR_SHORT_PKT) {
 			xfer->actlen = xfer->length - XHCI_TRB_2_REM_GET(trb_2);
 			err = USBD_NORMAL_COMPLETION;
-		} else if (XHCI_TRB_2_ERROR_GET(trb_2) ==
-		    XHCI_TRB_ERROR_SHORT_PKT) {
-			xfer->actlen = xfer->length - XHCI_TRB_2_REM_GET(trb_2);
-			err = USBD_NORMAL_COMPLETION;
-		} else if (XHCI_TRB_2_ERROR_GET(trb_2) ==
-		    XHCI_TRB_ERROR_STALL) {
+		} else if (trberr == XHCI_TRB_ERROR_STALL ||
+			   trberr == XHCI_TRB_ERROR_BABBLE) {
 			err = USBD_STALLED;
 			xr->is_halted = true;
-			DPRINTFN(1, "ev: xfer done: err %u slot %u dci %u",
+			DPRINTFN(1,
+			    "evh: xfer done: ERR %u slot %u dci %u",
 			    XHCI_TRB_2_ERROR_GET(trb_2), slot, dci, 0);
+#if 1 /* XXX experimental */
+			/*
+			 * This tries to kick task to reset endpoint,
+			 * drop tr->is_halted flag, and call
+			 * usb_transfer_complete with err asynchronously
+			 * in usb task thread.
+			 * usb_transfer_complete will be call from thread ctx.
+			 */
+			xfer->status = err;
+			xhci_clear_endpoint_stall_async(xfer);
+			break;
+#endif
 		} else {
 			err = USBD_IOERROR;
 		}
@@ -1472,7 +1828,8 @@ xhci_new_device(device_t parent, usbd_bu
 	int rhport = 0;
 	struct xhci_slot *xs;
 	uint32_t *cp;
-	uint8_t slot;
+	uint32_t route = 0;
+	uint8_t slot = 0;
 	uint8_t addr;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
@@ -1493,11 +1850,21 @@ xhci_new_device(device_t parent, usbd_bu
 	dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT;
 	dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
 	dev->def_ep_desc.bmAttributes = UE_CONTROL;
-	/* XXX */
-	if (speed == USB_SPEED_LOW)
-		USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET);
-	else
+	/* 4.3,  4.8.2.1 */
+	switch (speed) {
+	case USB_SPEED_SUPER:
+		USETW(dev->def_ep_desc.wMaxPacketSize, 512);
+		break;
+	case USB_SPEED_FULL:
+		/* XXX using 64 as initial mps of ep0 in FS */
+	case USB_SPEED_HIGH:
 		USETW(dev->def_ep_desc.wMaxPacketSize, 64);
+		break;
+	case USB_SPEED_LOW:
+	default:
+		USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET);
+		break;
+	}
 	dev->def_ep_desc.bInterval = 0;
 
 	/* doesn't matter, just don't let it uninitialized */
@@ -1515,28 +1882,50 @@ xhci_new_device(device_t parent, usbd_bu
 	up->device = dev;
 
 	/* Locate root hub port */
-	for (adev = dev, hub = dev;
-	    hub != NULL;
-	    adev = hub, hub = hub->myhub) {
-		DPRINTFN(4, "hub %p", hub, 0, 0, 0);
-	}
-	DPRINTFN(4, "hub %p", hub, 0, 0, 0);
+	for (hub = dev; hub != NULL; hub = hub->myhub) {
+		uint32_t dep;
+
+		DPRINTFN(4, "hub %p depth %d upport %p upportno %d",
+		    hub, hub->depth, hub->powersrc,
+		    hub->powersrc ? hub->powersrc->portno : 0);
+
+		if (hub->powersrc == NULL)
+			break;
+		dep = hub->depth;
+		if (dep == 0)
+			break;
+		rhport = hub->powersrc->portno;
+		if (dep > USB_HUB_MAX_DEPTH)
+			continue;
 
-	if (hub != NULL) {
-		for (int p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
+		route |=
+		    (rhport > UHD_SS_NPORTS_MAX ? UHD_SS_NPORTS_MAX : rhport)
+		    << ((dep - 1) * 4);
+	}
+	route = route >> 4;
+
+
+	DPRINTFN(4, "rhport %d Route %05x hub %p", rhport, route, hub, 0);
+
+	/* Locate port on upstream high speed hub */
+	for (adev = dev, hub = up->parent;
+	     hub != NULL && hub->speed != USB_SPEED_HIGH;
+	     adev = hub, hub = hub->myhub)
+		;
+	if (hub) {
+		int p;
+		for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
 			if (hub->hub->ports[p].device == adev) {
-				rhport = p;
+				dev->myhsport = &hub->hub->ports[p];
+				goto found;
 			}
 		}
+		panic("xhci_new_device: cannot find HS port");
+	found:
+		DPRINTFN(4, "high speed port %d", p, 0, 0, 0);
 	} else {
-		rhport = port;
+		dev->myhsport = NULL;
 	}
-	if (speed == USB_SPEED_SUPER) {
-		rhport += sc->sc_ss_port_start - 1;
-	} else {
-		rhport += sc->sc_hs_port_start - 1;
-	}
-	DPRINTFN(4, "rhport %d", rhport, 0, 0, 0);
 
 	dev->speed = speed;
 	dev->langid = USBD_NOLANG;
@@ -1546,8 +1935,7 @@ xhci_new_device(device_t parent, usbd_bu
 	err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL,
 	    &dev->default_pipe);
 	if (err) {
-		usbd_remove_device(dev, up);
-		return (err);
+		goto bad;
 	}
 
 	dd = &dev->ddesc;
@@ -1557,17 +1945,17 @@ xhci_new_device(device_t parent, usbd_bu
 		bus->devices[dev->address] = dev;
 		err = usbd_get_initial_ddesc(dev, dd);
 		if (err)
-			return err;
+			goto bad;
 		err = usbd_reload_device_desc(dev);
 		if (err)
-			return err;
+			goto bad;
 	} else {
 		err = xhci_enable_slot(sc, &slot);
 		if (err)
-			return err;
-		err = xhci_init_slot(sc, slot, depth, speed, port, rhport);
+			goto bad;
+		err = xhci_init_slot(dev, slot, route, rhport);
 		if (err)
-			return err;
+			goto bad;
 		xs = &sc->sc_slots[slot];
 		dev->hci_private = xs;
 		cp = xhci_slot_get_dcv(sc, xs, XHCI_DCI_SLOT);
@@ -1582,9 +1970,10 @@ xhci_new_device(device_t parent, usbd_bu
 		KASSERT(bus->devices[dev->address] == NULL);
 		bus->devices[dev->address] = dev;
 
+		/* need retry? */
 		err = usbd_get_initial_ddesc(dev, dd);
 		if (err)
-			return err;
+			goto bad;
 		/* 4.8.2.1 */
 		if (speed == USB_SPEED_SUPER)
 			USETW(dev->def_ep_desc.wMaxPacketSize,
@@ -1597,11 +1986,13 @@ xhci_new_device(device_t parent, usbd_bu
 		    UGETW(dev->def_ep_desc.wMaxPacketSize));
 		err = usbd_reload_device_desc(dev);
 		if (err)
-			return err;
+			goto bad;
 
+#if 0
 		usbd_kill_pipe(dev->default_pipe);
 		err = usbd_setup_pipe(dev, 0, &dev->def_ep,
 		    USBD_DEFAULT_INTERVAL, &dev->default_pipe);
+#endif
 	}
 
 	DPRINTFN(1, "adding unit addr=%d, rev=%02x,",
@@ -1624,11 +2015,14 @@ xhci_new_device(device_t parent, usbd_bu
 
 	err = usbd_probe_and_attach(parent, dev, port, dev->address);
 	if (err) {
-		usbd_remove_device(dev, up);
-		return (err);
+		goto bad;
 	}
 
 	return USBD_NORMAL_COMPLETION;
+
+ bad:
+	usbd_remove_device(dev, up);
+	return err;
 }
 
 static usbd_status
@@ -1638,7 +2032,8 @@ xhci_ring_init(struct xhci_softc * const
 	usbd_status err;
 	size_t size = ntrb * XHCI_TRB_SIZE;
 
-	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+	/* XXX initialization order */
+	//XHCIHIST_FUNC(); XHCIHIST_CALLED();
 
 	err = usb_allocmem(&sc->sc_bus, size, align, &xr->xr_dma);
 	if (err)
@@ -1769,8 +2164,8 @@ xhci_ring_put(struct xhci_softc * const 
 }
 
 static usbd_status
-xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb,
-    int timeout)
+xhci_do_command1(struct xhci_softc * const sc, struct xhci_trb * const trb,
+    int timeout, int locked)
 {
 	struct xhci_ring * const cr = &sc->sc_cr;
 	usbd_status err;
@@ -1779,8 +2174,10 @@ xhci_do_command(struct xhci_softc * cons
 	DPRINTFN(12, "input: 0x%016"PRIx64" 0x%08"PRIx32" 0x%08"PRIx32,
 	    trb->trb_0, trb->trb_2, trb->trb_3, 0);
 
-	mutex_enter(&sc->sc_lock);
+	if (!locked)
+		mutex_enter(&sc->sc_lock);
 
+	/* this might fire when cv_timedwait unlocks sc_lock */
 	KASSERT(sc->sc_command_addr == 0);
 	sc->sc_command_addr = xhci_ring_trbp(cr, cr->xr_ep);
 
@@ -1818,11 +2215,26 @@ xhci_do_command(struct xhci_softc * cons
 
 timedout:
 	sc->sc_command_addr = 0;
-	mutex_exit(&sc->sc_lock);
+	if (!locked)
+		mutex_exit(&sc->sc_lock);
 	return err;
 }
 
 static usbd_status
+xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb,
+    int timeout)
+{
+	return xhci_do_command1(sc, trb, timeout, 0);
+}
+
+static usbd_status
+xhci_do_command_locked(struct xhci_softc * const sc,
+    struct xhci_trb * const trb, int timeout)
+{
+	return xhci_do_command1(sc, trb, timeout, 1);
+}
+
+static usbd_status
 xhci_enable_slot(struct xhci_softc * const sc, uint8_t * const slotp)
 {
 	struct xhci_trb trb;
@@ -1845,6 +2257,38 @@ xhci_enable_slot(struct xhci_softc * con
 }
 
 static usbd_status
+xhci_disable_slot(struct xhci_softc * const sc, uint8_t slot)
+{
+	struct xhci_trb trb;
+	struct xhci_slot *xs;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	if (sc->sc_dying)
+		return USBD_IOERROR;
+
+	xs = &sc->sc_slots[slot];
+	if (xs != NULL) {
+		for (int i = 0; i < 32; i++) {
+			if (i == XHCI_DCI_SLOT)
+				continue;
+			xhci_ring_free(sc, &xs->xs_ep[i].xe_tr);
+			memset(&xs->xs_ep[i], 0, sizeof(xs->xs_ep[i]));
+		}
+		usb_freemem(&sc->sc_bus, &xs->xs_ic_dma);
+		usb_freemem(&sc->sc_bus, &xs->xs_dc_dma);
+	}
+
+	trb.trb_0 = 0;
+	trb.trb_2 = 0;
+	trb.trb_3 = htole32(
+		XHCI_TRB_3_SLOT_SET(slot) |
+		XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DISABLE_SLOT));
+
+	return xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
+}
+
+static usbd_status
 xhci_address_device(struct xhci_softc * const sc,
     uint64_t icp, uint8_t slot_id, bool bsr)
 {
@@ -1911,37 +2355,39 @@ xhci_set_dcba(struct xhci_softc * const 
 }
 
 static usbd_status
-xhci_init_slot(struct xhci_softc * const sc, uint32_t slot, int depth,
-    int speed, int port, int rhport)
+xhci_init_slot(usbd_device_handle dev, uint32_t slot, int route, int rhport)
 {
+	struct xhci_softc * const sc = dev->bus->hci_private;
+	int speed = dev->speed;
 	struct xhci_slot *xs;
 	usbd_status err;
 	u_int dci;
 	uint32_t *cp;
 	uint32_t mps;
-	uint32_t xspeed;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "slot %u depth %d speed %d",
-	    slot, depth, speed, 0);
-	DPRINTFN(4, " port %d rhport %d",
-	    port, rhport, 0, 0);
+	DPRINTFN(4, "slot %u speed %d rhport %d route %05x",
+	    slot, speed, route, rhport);
 
 	switch (speed) {
 	case USB_SPEED_LOW:
-		xspeed = 2;
 		mps = USB_MAX_IPACKET;
 		break;
 	case USB_SPEED_FULL:
-		xspeed = 1;
+#if 0
+		mps = 8;
+#else
+		/* XXX according to xHCI spec 1.1 sec 4.3 (7),
+		 * mps in FS should be started with 8.
+		 * (and updated later in xhci_update_ep0_mps)
+		 */
 		mps = 64;
+#endif
 		break;
 	case USB_SPEED_HIGH:
-		xspeed = 3;
 		mps = USB_2_MAX_CTRL_PACKET;
 		break;
 	case USB_SPEED_SUPER:
-		xspeed = 4;
 		mps = USB_3_MAX_CTRL_PACKET;
 		break;
 	default:
@@ -1962,7 +2408,7 @@ xhci_init_slot(struct xhci_softc * const
 	err = usb_allocmem(&sc->sc_bus, sc->sc_pgsz, sc->sc_pgsz,
 	    &xs->xs_ic_dma);
 	if (err)
-		return err;
+		goto bad1;
 	memset(KERNADDR(&xs->xs_ic_dma, 0), 0, sc->sc_pgsz);
 
 	for (dci = 0; dci < 32; dci++) {
@@ -1974,10 +2420,12 @@ xhci_init_slot(struct xhci_softc * const
 		    XHCI_TRANSFER_RING_TRBS, XHCI_TRB_ALIGN);
 		if (err) {
 			DPRINTFN(0, "ring init failure", 0, 0, 0, 0);
-			return err;
+			goto bad2;
 		}
 	}
 
+	memset(xhci_slot_get_icv(sc, xs, 0), 0, sc->sc_pgsz);
+
 	/* set up initial input control context */
 	cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL);
 	cp[0] = htole32(0);
@@ -1986,17 +2434,10 @@ xhci_init_slot(struct xhci_softc * const
 
 	/* set up input slot context */
 	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
-	cp[0] = htole32(
-		XHCI_SCTX_0_CTX_NUM_SET(1) |
-		XHCI_SCTX_0_SPEED_SET(xspeed)
-		);
-	cp[1] = htole32(
-		XHCI_SCTX_1_RH_PORT_SET(rhport)
-		);
-	cp[2] = htole32(
-		XHCI_SCTX_2_IRQ_TARGET_SET(0)
-		);
-	cp[3] = htole32(0);
+	xhci_setup_sctx(dev, cp);
+	cp[0] |= htole32(XHCI_SCTX_0_CTX_NUM_SET(1));
+	cp[0] |= htole32(XHCI_SCTX_0_ROUTE_SET(route));
+	cp[1] |= htole32(XHCI_SCTX_1_RH_PORT_SET(rhport));
 
 	/* set up input EP0 context */
 	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_EP_CONTROL));
@@ -2028,6 +2469,17 @@ xhci_init_slot(struct xhci_softc * const
 	hexdump("output context", xhci_slot_get_dcv(sc, xs, 0),
 	    sc->sc_ctxsz * 2);
 
+	if (!err)
+		return err;
+
+ bad2:
+	for (int i = 1; i < dci; i++) {
+		xhci_ring_free(sc, &xs->xs_ep[i].xe_tr);
+		memset(&xs->xs_ep[i], 0, sizeof(xs->xs_ep[i]));
+	}
+	usb_freemem(&sc->sc_bus, &xs->xs_ic_dma);
+ bad1:
+	usb_freemem(&sc->sc_bus, &xs->xs_dc_dma);
 	return err;
 }
 
@@ -2042,73 +2494,125 @@ xhci_noop(usbd_pipe_handle pipe)
 /* root hub descriptors */
 
 static const usb_device_descriptor_t xhci_devd = {
-	USB_DEVICE_DESCRIPTOR_SIZE,
-	UDESC_DEVICE,		/* type */
-	{0x00, 0x02},		/* USB version */
-	UDCLASS_HUB,		/* class */
-	UDSUBCLASS_HUB,		/* subclass */
-	UDPROTO_HSHUBSTT,	/* protocol */
-	64,			/* max packet */
-	{0},{0},{0x00,0x01},	/* device id */
-	1,2,0,			/* string indexes */
-	1			/* # of configurations */
+	.bLength		= USB_DEVICE_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_DEVICE,		/* type */
+	.bcdUSB			= {0x00, 0x02},		/* USB version */
+	.bDeviceClass		= UDCLASS_HUB,		/* class */
+	.bDeviceSubClass	= UDSUBCLASS_HUB,	/* subclass */
+	.bDeviceProtocol	= UDPROTO_HSHUBSTT,	/* protocol */
+	.bMaxPacketSize		= 64,			/* max packet */
+	.idVendor		= {0},
+	.idProduct		= {0},
+	.bcdDevice		= {0x00, 0x01},		/* device id */
+	.iManufacturer		= 1,			/* string indexes */
+	.iProduct		= 2,
+	.iSerialNumber		= 0,
+	.bNumConfigurations	= 1		/* # of configurations */
 };
 
 static const usb_device_qualifier_t xhci_odevd = {
-	USB_DEVICE_DESCRIPTOR_SIZE,
-	UDESC_DEVICE_QUALIFIER,	/* type */
-	{0x00, 0x02},		/* USB version */
-	UDCLASS_HUB,		/* class */
-	UDSUBCLASS_HUB,		/* subclass */
-	UDPROTO_FSHUB,		/* protocol */
-	64,                     /* max packet */
-	1,                      /* # of configurations */
-	0
+	.bLength		= USB_DEVICE_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_DEVICE_QUALIFIER,	/* type */
+	.bcdUSB			= {0x00, 0x02},		/* USB version */
+	.bDeviceClass		= UDCLASS_HUB,		/* class */
+	.bDeviceSubClass	= UDSUBCLASS_HUB,	/* subclass */
+	.bDeviceProtocol	= UDPROTO_FSHUB,	/* protocol */
+	.bMaxPacketSize0	= 64,			/* max packet */
+	.bNumConfigurations	= 1,		/* # of configurations */
+	.bReserved		= 0
 };
 
 static const usb_config_descriptor_t xhci_confd = {
-	USB_CONFIG_DESCRIPTOR_SIZE,
-	UDESC_CONFIG,
-	{USB_CONFIG_DESCRIPTOR_SIZE +
-	 USB_INTERFACE_DESCRIPTOR_SIZE +
-	 USB_ENDPOINT_DESCRIPTOR_SIZE},
-	1,
-	1,
-	0,
-	UC_ATTR_MBO | UC_SELF_POWERED,
-	0                      /* max power */
+	.bLength		= USB_CONFIG_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_CONFIG,
+	.wTotalLength		= {USB_CONFIG_DESCRIPTOR_SIZE +
+				 USB_INTERFACE_DESCRIPTOR_SIZE +
+				 USB_ENDPOINT_DESCRIPTOR_SIZE},
+	.bNumInterface		= 1,
+	.bConfigurationValue	= 1,
+	.iConfiguration		= 0,
+	.bmAttributes		= UC_ATTR_MBO | UC_SELF_POWERED,
+	.bMaxPower		= 0	/* max current in 2 mA units */
+};
+
+static const usb_bos_descriptor_t xhci_bosd = {
+	.bLength		= USB_BOS_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_BOS,
+	.wTotalLength		= {USB_BOS_DESCRIPTOR_SIZE +
+				 USB_DEVCAP_USB2EXT_DESCRIPTOR_SIZE +
+				 USB_DEVCAP_SS_DESCRIPTOR_SIZE +
+				 USB_DEVCAP_CONTAINER_ID_DESCRIPTOR_SIZE},
+	.bNumDeviceCaps		= 3
+};
+
+static const usb_devcap_usb2ext_descriptor_t xhci_devcap_usb2extd = {
+	.bLength		= USB_DEVCAP_USB2EXT_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_BOS,
+	.bDevCapabilityType	= USB_DEVCAP_USB2EXT,
+	.bmAttributes		= {""}
+};
+
+static const usb_devcap_ss_descriptor_t xhci_devcap_ssd = {
+	.bLength		= USB_DEVCAP_SS_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_BOS,
+	.bDevCapabilityType	= USB_DEVCAP_SUPER_SPEED,
+	.bmAttributes		= 0,
+	.wSpeedsSupported	= {""},
+	.bFunctionalitySupport	= 0,
+	.bU1DevExitLat		= 0,
+	.wU2DevExitLat		= {""}
+};
+
+static const usb_devcap_container_id_descriptor_t xhci_devcap_containerid_d = {
+	.bLength		= USB_DEVCAP_CONTAINER_ID_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_BOS,
+	.bDevCapabilityType	= USB_DEVCAP_CONTAINER_ID,
+	.bReserved		= 0,
+//	.ContainerID[16]
 };
 
 static const usb_interface_descriptor_t xhci_ifcd = {
-	USB_INTERFACE_DESCRIPTOR_SIZE,
-	UDESC_INTERFACE,
-	0,
-	0,
-	1,
-	UICLASS_HUB,
-	UISUBCLASS_HUB,
-	UIPROTO_HSHUBSTT,
-	0
+	.bLength		= USB_INTERFACE_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_INTERFACE,
+	.bInterfaceNumber	= 0,
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 1,
+	.bInterfaceClass	= UICLASS_HUB,
+	.bInterfaceSubClass	= UISUBCLASS_HUB,
+	.bInterfaceProtocol	= UIPROTO_HSHUBSTT,
+	.iInterface		= 0
 };
 
 static const usb_endpoint_descriptor_t xhci_endpd = {
-	USB_ENDPOINT_DESCRIPTOR_SIZE,
-	UDESC_ENDPOINT,
-	UE_DIR_IN | XHCI_INTR_ENDPT,
-	UE_INTERRUPT,
-	{8, 0},                 /* max packet */
-	12
+	.bLength		= USB_ENDPOINT_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_ENDPOINT,
+	.bEndpointAddress	= UE_DIR_IN | XHCI_INTR_ENDPT,
+	.bmAttributes		= UE_INTERRUPT,
+	.wMaxPacketSize		= {64, 0},	/* max packet */
+	.bInterval		= 12
 };
 
 static const usb_hub_descriptor_t xhci_hubd = {
-	USB_HUB_DESCRIPTOR_SIZE,
-	UDESC_HUB,
-	0,
-	{0,0},
-	0,
-	0,
-	{""},
-	{""},
+	.bDescLength		= USB_HUB_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_HUB,
+	.bNbrPorts		= 0,
+	.wHubCharacteristics	= {0, 0},
+	.bPwrOn2PwrGood		= 0,		/* delay in 2 ms units */
+	.bHubContrCurrent	= 0,
+	.DeviceRemovable	= {""},		/* max 255 ports */
+	.PortPowerCtrlMask	= {""},		/* deprecated */ 
+};
+
+static const usb_hub_ss_descriptor_t xhci_hubssd = {
+	.bLength		= USB_HUB_SS_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_SS_HUB,
+	.bNbrPorts		= 0,
+	.wHubCharacteristics	= {0, 0},
+	.bPwrOn2PwrGood		= 0,		/* delay in 2 ms units */
+	.bHubContrCurrent	= 0,
+	.bHubHdrDecLat		= 0,
+	.wHubDelay		= {0, 0},
+	.DeviceRemovable	= {""},		/* max 255 ports */
 };
 
 /* root hub control */
@@ -2219,6 +2723,55 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 			totlen += l;
 			memcpy(buf, &xhci_endpd, min(l, sizeof(xhci_endpd)));
 			break;
+		case UDESC_BOS:
+			if ((value & 0xff) != 0) {
+				err = USBD_IOERROR;
+				goto ret;
+			}
+			totlen = l = min(len, USB_BOS_DESCRIPTOR_SIZE);
+			memcpy(buf, &xhci_bosd, min(l, sizeof(xhci_bosd)));
+
+			buf = (char *)buf + l;
+			len -= l;
+			l = min(len, USB_DEVCAP_USB2EXT_DESCRIPTOR_SIZE);
+			totlen += l;
+			memcpy(buf, &xhci_devcap_usb2extd,
+			    min(l, sizeof(xhci_devcap_usb2extd)));
+			USETDW(((usb_devcap_usb2ext_descriptor_t *)buf)
+			    ->bmAttributes, USB_DEVCAP_USB2EXT_LPM);
+
+			buf = (char *)buf + l;
+			len -= l;
+			l = min(len, USB_DEVCAP_SS_DESCRIPTOR_SIZE);
+			totlen += l;
+			memcpy(buf, &xhci_devcap_ssd,
+			    min(l, sizeof(xhci_devcap_ssd)));
+			{
+				usb_devcap_ss_descriptor_t *dcssd = buf;
+
+				dcssd->bmAttributes = USB_DEVCAP_SS_LTM;
+				dcssd->bFunctionalitySupport =
+				    USB_DEVCAP_SS_SPEED_SS |
+				    USB_DEVCAP_SS_SPEED_FS |
+				    USB_DEVCAP_SS_SPEED_HS |
+				    USB_DEVCAP_SS_SPEED_LS;
+				USETW(dcssd->wSpeedsSupported,
+				    dcssd->bFunctionalitySupport);
+				dcssd->bU1DevExitLat = sc->sc_rh_u1del;
+				if (dcssd->bU1DevExitLat > 10)
+					dcssd->bU1DevExitLat = 10;
+				USETW(dcssd->wU2DevExitLat, sc->sc_rh_u1del);
+				if (UGETW(dcssd->wU2DevExitLat) > 2047)
+					USETW(dcssd->wU2DevExitLat, 2047);
+			}
+
+			buf = (char *)buf + l;
+			len -= l;
+			l = min(len, USB_DEVCAP_CONTAINER_ID_DESCRIPTOR_SIZE);
+			totlen += l;
+			memcpy(buf, &xhci_devcap_containerid_d,
+			    min(l, sizeof(xhci_devcap_containerid_d)));
+			break;
 		case UDESC_STRING:
 #define sd ((usb_string_descriptor_t *)buf)
 			switch (value & 0xff) {
@@ -2287,14 +2840,15 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 	/* Hub requests */
 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
 		break;
+	/* Hub Port request */
 	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
 		DPRINTFN(4, "UR_CLEAR_PORT_FEATURE port=%d feature=%d",
 			     index, value, 0, 0);
-		if (index < 1 || index > sc->sc_hs_port_count) {
+		if (index < 1 || index > sc->sc_maxports) {
 			err = USBD_IOERROR;
 			goto ret;
 		}
-		port = XHCI_PORTSC(sc->sc_hs_port_start - 1 + index);
+		port = XHCI_PORTSC(index);
 		v = xhci_op_read_4(sc, port);
 		DPRINTFN(4, "portsc=0x%08x", v, 0, 0, 0);
 		v &= ~XHCI_PS_CLEAR;
@@ -2319,9 +2873,18 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 		case UHF_C_PORT_OVER_CURRENT:
 			err = USBD_IOERROR;
 			goto ret;
+		case UHF_C_BH_PORT_RESET:
+			xhci_op_write_4(sc, port, v | XHCI_PS_WRC);
+			break;
 		case UHF_C_PORT_RESET:
 			xhci_op_write_4(sc, port, v | XHCI_PS_PRC);
 			break;
+		case UHF_C_PORT_LINK_STATE:
+			xhci_op_write_4(sc, port, v | XHCI_PS_PLC);
+			break;
+		case UHF_C_PORT_CONFIG_ERROR:
+			xhci_op_write_4(sc, port, v | XHCI_PS_CEC);
+			break;
 		default:
 			err = USBD_IOERROR;
 			goto ret;
@@ -2335,16 +2898,58 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 			err = USBD_IOERROR;
 			goto ret;
 		}
-		hubd = xhci_hubd;
-		hubd.bNbrPorts = sc->sc_hs_port_count;
-		USETW(hubd.wHubCharacteristics, UHD_PWR_NO_SWITCH);
-		hubd.bPwrOn2PwrGood = 200;
-		for (i = 0, l = sc->sc_maxports; l > 0; i++, l -= 8)
-			hubd.DeviceRemovable[i++] = 0; /* XXX can't find out? */
-		hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
-		l = min(len, hubd.bDescLength);
-		totlen = l;
-		memcpy(buf, &hubd, l);
+		if ((value >> 8) == UDESC_SS_HUB) {
+#if 1
+			union {
+				usb_hub_ss_descriptor_t hd_ss;
+				uint8_t
+				    hd_buf[USB_HUB_SS_DESCRIPTOR_SIZE+32-2];
+			} uhd_ss;
+#define hssd uhd_ss.hd_ss
+#else
+			usb_hub_ss_descriptor_t hssd;
+#endif
+
+#if 1
+			memset(&hssd, 0, sizeof(uhd_ss));
+#else
+			memset(&hssd, 0, sizeof(hssd));
+#endif
+			hssd = xhci_hubssd;
+			hssd.bNbrPorts = sc->sc_maxports;
+			/* XXX not defined in SS hub */
+			USETW(hssd.wHubCharacteristics, UHD_PWR_NO_SWITCH);
+			hssd.bPwrOn2PwrGood = 200;
+			for (i = 0, l = hssd.bNbrPorts;
+			    l > 0; i++, l -= 8)
+				/* XXX can't find out? */
+				hssd.DeviceRemovable[i++] = 0;
+			hssd.bLength = USB_HUB_SS_DESCRIPTOR_SIZE + i;
+			l = min(len, hssd.bLength);
+			hssd.bLength = l;
+			totlen = l;
+			memcpy(buf, &hssd, l);
+#if 1
+#undef hssd
+#endif
+		} else if ((value >> 8) == UDESC_HUB) {
+			hubd = xhci_hubd;
+			hubd.bNbrPorts = sc->sc_maxports;
+			USETW(hubd.wHubCharacteristics, UHD_PWR_NO_SWITCH);
+			hubd.bPwrOn2PwrGood = 200;
+			for (i = 0, l = hubd.bNbrPorts;
+			    l > 0; i++, l -= 8)
+				/* XXX can't find out? */
+				hubd.DeviceRemovable[i++] = 0;
+			hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i;
+			l = min(len, hubd.bDescLength);
+			hubd.bDescLength = l;
+			totlen = l;
+			memcpy(buf, &hubd, l);
+		} else {
+			err = USBD_IOERROR;
+			goto ret;
+		}
 		break;
 	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
 		if (len != 4) {
@@ -2364,10 +2969,9 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 			err = USBD_IOERROR;
 			goto ret;
 		}
-		v = xhci_op_read_4(sc, XHCI_PORTSC(sc->sc_hs_port_start - 1 +
-		    index));
-		DPRINTFN(4, "READ_CLASS_OTHER GET_STATUS PORTSC %d (%d) %08x",
-		    index, sc->sc_hs_port_start - 1 + index, v, 0);
+		v = xhci_op_read_4(sc, XHCI_PORTSC(index));
+		DPRINTFN(4, "getrhportsc %d %08x", index, v, 0, 0);
+		/* xspeed -> port_status value */
 		switch (XHCI_PS_SPEED_GET(v)) {
 		case 1:
 			i = UPS_FULL_SPEED;
@@ -2378,6 +2982,9 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 		case 3:
 			i = UPS_HIGH_SPEED;
 			break;
+		case 4:
+			i = UPS_SUPER_SPEED;
+			break;
 		default:
 			i = 0;
 			break;
@@ -2387,13 +2994,21 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 		if (v & XHCI_PS_OCA)	i |= UPS_OVERCURRENT_INDICATOR;
 		//if (v & XHCI_PS_SUSP)	i |= UPS_SUSPEND;
 		if (v & XHCI_PS_PR)	i |= UPS_RESET;
-		if (v & XHCI_PS_PP)	i |= UPS_PORT_POWER;
+		if (v & XHCI_PS_PP) {
+			if (i & UPS_SUPER_SPEED)
+					i |= UPS_PORT_POWER_SS;
+			else
+					i |= UPS_PORT_POWER;
+		}
 		USETW(ps.wPortStatus, i);
 		i = 0;
 		if (v & XHCI_PS_CSC)    i |= UPS_C_CONNECT_STATUS;
 		if (v & XHCI_PS_PEC)    i |= UPS_C_PORT_ENABLED;
 		if (v & XHCI_PS_OCC)    i |= UPS_C_OVERCURRENT_INDICATOR;
 		if (v & XHCI_PS_PRC)	i |= UPS_C_PORT_RESET;
+		if (v & XHCI_PS_WRC)	i |= UPS_C_BH_PORT_RESET;
+		if (v & XHCI_PS_PLC)	i |= UPS_C_PORT_LINK_STATE;
+		if (v & XHCI_PS_CEC)	i |= UPS_C_PORT_CONFIG_ERROR;
 		USETW(ps.wPortChange, i);
 		l = min(len, sizeof ps);
 		memcpy(buf, &ps, l);
@@ -2402,14 +3017,17 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 	case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
 		err = USBD_IOERROR;
 		goto ret;
+	case C(UR_SET_HUB_DEPTH, UT_WRITE_CLASS_DEVICE):
+		break;
 	case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
 		break;
+	/* Hub Port request */
 	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
-		if (index < 1 || index > sc->sc_hs_port_count) {
+		if (index < 1 || index > sc->sc_maxports) {
 			err = USBD_IOERROR;
 			goto ret;
 		}
-		port = XHCI_PORTSC(sc->sc_hs_port_start - 1 + index);
+		port = XHCI_PORTSC(index);
 		v = xhci_op_read_4(sc, port);
 		DPRINTFN(4, "portsc=0x%08x", v, 0, 0, 0);
 		v &= ~XHCI_PS_CLEAR;
@@ -2443,6 +3061,24 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 		case UHF_C_PORT_RESET:
 			xhci_op_write_4(sc, port, v | XHCI_PS_PRC);
 			break;
+		case UHF_PORT_U1_TIMEOUT:
+			if (XHCI_PS_SPEED_GET(v) != 4) {
+				err = USBD_IOERROR;
+				goto ret;
+			}
+			v &= ~XHCI_PM3_U1TO_SET(0xff);
+			v |= XHCI_PM3_U1TO_SET(value);
+			xhci_op_write_4(sc, port, v);
+			break;
+		case UHF_PORT_U2_TIMEOUT:
+			if (XHCI_PS_SPEED_GET(v) != 4) {
+				err = USBD_IOERROR;
+				goto ret;
+			}
+			v &= ~XHCI_PM3_U2TO_SET(0xff);
+			v |= XHCI_PM3_U2TO_SET(value);
+			xhci_op_write_4(sc, port, v);
+			break;
 		default:
 			err = USBD_IOERROR;
 			goto ret;
@@ -2702,12 +3338,18 @@ static void
 xhci_device_ctrl_abort(usbd_xfer_handle xfer)
 {
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	/* XXX */
+	xfer->status = USBD_CANCELLED;
+	usb_transfer_complete(xfer);
 }
 
 static void
 xhci_device_ctrl_close(usbd_pipe_handle pipe)
 {
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	(void)xhci_unconfigure_endpoint(pipe);
 }
 
 /* ----------------- */
@@ -2822,12 +3464,18 @@ static void
 xhci_device_bulk_abort(usbd_xfer_handle xfer)
 {
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	/* XXX */
+	xfer->status = USBD_CANCELLED;
+	usb_transfer_complete(xfer);
 }
 
 static void
 xhci_device_bulk_close(usbd_pipe_handle pipe)
 {
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	(void)xhci_unconfigure_endpoint(pipe);
 }
 
 /* --------------- */
--- src/sys/dev/usb/xhcireg.h.orig	2014-11-18 21:02:04.000000000 +0900
+++ src/sys/dev/usb/xhcireg.h	2014-11-18 19:54:27.000000000 +0900
@@ -38,6 +38,11 @@
 #define	 PCI_USBREV_3_0		0x30	/* USB 3.0 */
 #define	PCI_XHCI_FLADJ		0x61	/* RW frame length adjust */
 
+#define	PCI_XHCI_INTEL_XUSB2PR	0xD0    /* Intel USB2 Port Routing */
+#define	PCI_XHCI_INTEL_USB2PRM	0xD4    /* Intel USB2 Port Routing Mask */
+#define	PCI_XHCI_INTEL_USB3_PSSEN 0xD8  /* Intel USB3 Port SuperSpeed Enable */
+#define	PCI_XHCI_INTEL_USB3PRM	0xDC    /* Intel USB3 Port Routing Mask */
+
 /* XHCI capability registers */
 #define XHCI_CAPLENGTH		0x00	/* RO capability */
 #define	XHCI_CAP_CAPLENGTH(x)	((x) & 0xFF)
@@ -168,6 +173,7 @@
 #define	XHCI_IMOD_ICNT_GET(x)	(((x) >> 16) & 0xFFFF)	/* 250ns unit */
 #define	XHCI_IMOD_ICNT_SET(x)	(((x) & 0xFFFF) << 16)	/* 250ns unit */
 #define	XHCI_IMOD_DEFAULT	0x000001F4U	/* 8000 IRQ/second */
+#define	XHCI_IMOD_DEFAULT_LP	0x000003E8U	/* 4000 IRQ/sec for LynxPoint */
 #define	XHCI_ERSTSZ(n)		(0x0028 + (0x20 * (n)))	/* XHCI event ring segment table size */
 #define	XHCI_ERSTS_GET(x)	((x) & 0xFFFF)
 #define	XHCI_ERSTS_SET(x)	((x) & 0xFFFF)
@@ -188,18 +194,20 @@
 /* XHCI legacy support */
 #define	XHCI_XECP_ID(x)		((x) & 0xFF)
 #define	XHCI_XECP_NEXT(x)	(((x) >> 8) & 0xFF)
-#if 0
 #define	XHCI_XECP_BIOS_SEM	0x0002
 #define	XHCI_XECP_OS_SEM	0x0003
-#endif
 
-/* XHCI capability ID's */
-#define	XHCI_ID_USB_LEGACY	0x0001
-#define	XHCI_ID_PROTOCOLS	0x0002
-#define	XHCI_ID_POWER_MGMT	0x0003
-#define	XHCI_ID_VIRTUALIZATION	0x0004
-#define	XHCI_ID_MSG_IRQ		0x0005
-#define	XHCI_ID_USB_LOCAL_MEM	0x0006
+/* XHCI extended capability ID's */
+#define	XHCI_ID_USB_LEGACY	0x0001	/* USB Legacy Support */
+#define	 XHCI_XECP_USBLESUP	0x0000	/* Legacy Support Capability Reg */
+#define	 XHCI_XECP_USBLEGCTLSTS	0x0004	/* Legacy Support Ctrl & Status Reg */
+#define	XHCI_ID_PROTOCOLS	0x0002	/* Supported Protocol */
+#define	XHCI_ID_POWER_MGMT	0x0003	/* Extended Power Management */
+#define	XHCI_ID_VIRTUALIZATION	0x0004	/* I/O Virtualization */
+#define	XHCI_ID_MSG_IRQ		0x0005	/* Message Interrupt */
+#define	XHCI_ID_USB_LOCAL_MEM	0x0006	/* Local Memory */
+#define	XHCI_ID_USB_DEBUG	0x000A	/* USB Debug Capability */
+#define	XHCI_ID_XMSG_IRQ	0x0011	/* Extended Message Interrupt */
 
 #define XHCI_PAGE_SIZE(sc) ((sc)->sc_pgsz)
 
@@ -243,6 +251,7 @@ struct xhci_trb {
 #define XHCI_TRB_3_TC_BIT               (1U << 1)       /* command ring only */
 #define XHCI_TRB_3_ENT_BIT              (1U << 1)       /* transfer ring only */
 #define XHCI_TRB_3_ISP_BIT              (1U << 2)
+//#define XHCI_TRB_3_ED_BIT               (1U << 2)
 #define XHCI_TRB_3_NSNOOP_BIT           (1U << 3)
 #define XHCI_TRB_3_CHAIN_BIT            (1U << 4)
 #define XHCI_TRB_3_IOC_BIT              (1U << 5)
@@ -266,6 +275,8 @@ struct xhci_trb {
 #define XHCI_TRB_3_FRID_SET(x)          (((x) & 0x7FF) << 20)
 #define XHCI_TRB_3_ISO_SIA_BIT          (1U << 31)
 #define XHCI_TRB_3_SUSP_EP_BIT          (1U << 23)
+#define XHCI_TRB_3_VFID_GET(x)          (((x) >> 16) & 0xFF)
+#define XHCI_TRB_3_VFID_SET(x)          (((x) & 0xFF) << 16)
 #define XHCI_TRB_3_SLOT_GET(x)          (((x) >> 24) & 0xFF)
 #define XHCI_TRB_3_SLOT_SET(x)          (((x) & 0xFF) << 24)
 
@@ -375,10 +386,20 @@ struct xhci_trb {
 #define XHCI_SCTX_3_DEV_ADDR_GET(x)             ((x) & 0xFF)
 #define XHCI_SCTX_3_SLOT_STATE_SET(x)           (((x) & 0x1F) << 27)
 #define XHCI_SCTX_3_SLOT_STATE_GET(x)           (((x) >> 27) & 0x1F)
+#define XHCI_SLOTSTATE_DISABLED			0 /* disabled or enabled */
+#define XHCI_SLOTSTATE_ENABLED			0
+#define XHCI_SLOTSTATE_DEFAULT			1
+#define XHCI_SLOTSTATE_ADDRESSED		2
+#define XHCI_SLOTSTATE_CONFIGURED		3
 
 
 #define XHCI_EPCTX_0_EPSTATE_SET(x)             ((x) & 0x7)
 #define XHCI_EPCTX_0_EPSTATE_GET(x)             ((x) & 0x7)
+#define XHCI_EPSTATE_DISABLED			0
+#define XHCI_EPSTATE_RUNNING			1
+#define XHCI_EPSTATE_HALTED			2
+#define XHCI_EPSTATE_STOPPED			3
+#define XHCI_EPSTATE_ERROR			4
 #define XHCI_EPCTX_0_MULT_SET(x)                (((x) & 0x3) << 8)
 #define XHCI_EPCTX_0_MULT_GET(x)                (((x) >> 8) & 0x3)
 #define XHCI_EPCTX_0_MAXP_STREAMS_SET(x)        (((x) & 0x1F) << 10)
--- src/sys/dev/usb/xhcivar.h.orig	2014-03-11 02:58:40.000000000 +0900
+++ src/sys/dev/usb/xhcivar.h	2014-11-12 16:28:07.000000000 +0900
@@ -86,6 +86,8 @@ struct xhci_softc {
 	int sc_maxintrs;
 	int sc_maxports;
 	int sc_maxspbuf;
+	uint16_t sc_rh_u1del;
+	uint16_t sc_rh_u2del;
 
 	/* XXX suboptimal */
 	int sc_hs_port_start;
@@ -114,6 +116,9 @@ struct xhci_softc {
 
 	uint8_t sc_addr;
 	uint8_t sc_conf;
+
+	int sc_quirks;
+#define XHCI_QUIRK_FORCE_INTR __BIT(0) /* force interrupt reading */
 };
 
 int	xhci_init(struct xhci_softc *);


Home | Main Index | Thread Index | Old Index