tech-kern archive

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

patch: 3.0 hub support for xhci



hello,

This patch tries to support 3.0 hubs for xhci.

still xhci is at best experimental.


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.
    uhub_intr() drops latter interrupts while uhub is exploring
    (sc_explorepending=1) for devices of former interrupt.
    I changed to force-rescan all ports of xhci root hub recursively.
- Expand size of DeviceRemovable in usb_hub_ss_descriptor_t
  from 2 to 32 as num of ports on root hub may exceeds 15.
- Temporarily set IMOD to 160 (25000 irq/sec).
    Intel PantherPoint/LynxPoint/BayTrail need interrupt rate capped,
    otherwise some devices seem to stall.
    This value is taken from linux xhci.c, but I'm not sure this is optimal.
    Large IMOD value may impact performance seriously.
    (e.g. xfer rate of my thumb drive falls off 177mb/s -> 80mb/s
     with IMOD=1000.)
- 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.

Known bugs:

- HS hub under 3.0 port is disconnected after several minutes.
- Detaching hubs or devices may cause panic or hang up.
    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.
    Before set_port_feature UHF_PORT_POWER it looks like:
      Bad:
        0x201203<PRC,PIC=0x0,XSPEED=0x4=SS,PP,PLS=0x0,PED,CCS>
      Good:
        0x221203<PRC,CSC,PIC=0x0,XSPEED=0x4=SS,PP,PLS=0x0,PED,CCS>
    I added some delay but it did not effect.
    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.

Misc bugs:

- Closing pipe does not work correctly,
    e.g. ifconfig axen0 up -> down -> up won't work.
    Currently xhci_unconfigure_endpoint is disabled because it panics.
- 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).
    I added experimental recovery code using usb_add_task.
    In xhci_handle_event if stall error is detected, usb_add_task will
    run clear_endpoint_stall task that does the 2 commands and then calls
    usb_transfer_complete after transfer ring is reset to notify
    usbdi layer that issued command fails.
- 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'm not sure when/where I disable it.
    Is it time to disable slot and release ring memory when
    ctrl endpoint is closed?
- Conexant 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.


Thanks,
--
t-hash

--- src/sys/dev/usb/uhub.c.orig	2014-08-14 07:02:24.000000000 +0900
+++ src/sys/dev/usb/uhub.c	2014-10-24 19:38:58.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,80 @@ 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;
+
+	if (dev->speed == USB_SPEED_SUPER) {
+		usb_hub_ss_descriptor_t hssd;
+		int rmvlen;
+
+		memset(&hssd, 0, sizeof(hssd));
+		req.bmRequestType = UT_READ_CLASS_DEVICE;
+		req.bRequest = UR_GET_DESCRIPTOR;
+		USETW2(req.wValue, UDESC_SSHUB, 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;
+	} 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 +249,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 +288,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 +318,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 +359,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 +388,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
@@ -460,6 +569,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 +622,12 @@ uhub_explore(usbd_device_handle dev)
 
 		/* Connected */
 
-		if (!(status & UPS_PORT_POWER))
+		if ((!(status & UPS_PORT_POWER) &&
+		    ((dev->depth == 0 && !(status & UPS_SUPER_SPEED)) ||
+		     (dev->depth != 0 && dev->speed != USB_SPEED_SUPER))) ||
+		    (!(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);
 
@@ -540,8 +667,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
@@ -578,7 +706,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 +854,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-09-13 22:14:45.000000000 +0900
+++ src/sys/dev/usb/usb.h	2014-10-24 18:28:15.000000000 +0900
@@ -172,6 +172,7 @@ typedef struct {
 #define  UDESC_CS_ENDPOINT	0x25
 #define  UDESC_HUB		0x29
 #define  UDESC_SSHUB		0x2a
+#define  UDESC_ENDPOINT_SS_COMP 0x30
 #define UR_SET_DESCRIPTOR	0x07
 #define UR_GET_CONFIG		0x08
 #define UR_SET_CONFIG		0x09
@@ -296,6 +297,16 @@ typedef struct {
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
+	uByte		bMaxBurst;
+	uByte		bmAttributes;
+	/* The fields below are only valid for periodic endpoints */
+	uWord		wBytesPerInterval;
+} UPACKED usb_endpoint_ss_comp_descriptor_t;
+#define USB_ENDPOINT_SS_COMP_DESCRIPTOR_SIZE 6
+
+typedef struct {
+	uByte		bLength;
+	uByte		bDescriptorType;
 	uWord		bString[126];
 } UPACKED usb_string_descriptor_t;
 #define USB_MAX_STRING_LEN 128
@@ -308,6 +319,7 @@ typedef struct {
 #define UR_RESET_TT		0x09
 #define UR_GET_TT_STATE		0x0a
 #define UR_STOP_TT		0x0b
+#define UR_SET_HUB_DEPTH	0x0c
 
 /*
  * Hub features from USB 2.0 spec, table 11-17 and updated by the
@@ -331,6 +343,14 @@ typedef struct {
 #define UHF_PORT_TEST		21
 #define UHF_PORT_INDICATOR	22
 #define UHF_C_PORT_L1		23
+#define UHF_PORT_U1_TIMEOUT	23
+#define UHF_PORT_U2_TIMEOUT	24
+#define UHF_C_PORT_LINK_STATE	25
+#define UHF_C_PORT_CONFIG_ERROR	26
+#define UHF_PORT_REMOTE_WAKE_MASK	27
+#define UHF_BH_PORT_RESET	28
+#define UHF_C_BH_PORT_RESET	29
+#define UHF_FORCE_LINKPM_ACCEPT	30
 
 typedef struct {
 	uByte		bDescLength;
@@ -365,6 +385,20 @@ typedef struct {
 typedef struct {
 	uByte		bLength;
 	uByte		bDescriptorType;
+	uByte		bNbrPorts;
+#define UHD_SS_NPORTS_MAX	15
+	uWord		wHubCharacteristics;
+	uByte		bPwrOn2PwrGood;	/* delay in 2 ms units */
+	uByte		bHubContrCurrent;
+	uByte		bHubHdrDecLat;
+	uWord		wHubDelay;	/* forward delay in nanosec */
+	uByte		DeviceRemovable[32]; /* max 255 ports */
+} UPACKED usb_hub_ss_descriptor_t;
+#define USB_HUB_SS_DESCRIPTOR_SIZE 12
+
+typedef struct {
+	uByte		bLength;
+	uByte		bDescriptorType;
 	uWord		bcdUSB;
 	uByte		bDeviceClass;
 	uByte		bDeviceSubClass;
@@ -420,10 +454,11 @@ typedef struct {
 #define UPS_RESET			0x0010
 #define UPS_PORT_L1			0x0020
 #define UPS_PORT_POWER			0x0100
+#define UPS_PORT_POWER_SS		0x0200
 #define UPS_FULL_SPEED			0x0000	/* for completeness */
 #define UPS_LOW_SPEED			0x0200
 #define UPS_HIGH_SPEED			0x0400
-#define UPS_SUPER_SPEED			0x0600
+#define UPS_SUPER_SPEED			0x0800
 #define UPS_PORT_TEST			0x0800
 #define UPS_PORT_INDICATOR		0x1000
 	uWord		wPortChange;
@@ -433,6 +468,9 @@ typedef struct {
 #define UPS_C_OVERCURRENT_INDICATOR	0x0008
 #define UPS_C_PORT_RESET		0x0010
 #define UPS_C_PORT_L1			0x0020
+#define UPS_C_BH_PORT_RESET		0x0020
+#define UPS_C_PORT_LINK_STATE		0x0040
+#define UPS_C_PORT_CONFIG_ERROR		0x0080
 } UPACKED usb_port_status_t;
 
 /* Device class codes */
--- src/sys/dev/usb/usb_subr.c.orig	2014-09-22 02:57:10.000000000 +0900
+++ src/sys/dev/usb/usb_subr.c	2014-10-24 17:41:39.000000000 +0900
@@ -1126,7 +1126,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 +1415,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/xhci.c.orig	2014-10-03 21:18:24.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2014-10-30 19:33:15.000000000 +0900
@@ -51,6 +57,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 +67,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 +99,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()
@@ -136,8 +141,7 @@ static usbd_status xhci_set_dequeue(usbd
 
 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_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_address_device(struct xhci_softc * const,
@@ -392,7 +396,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:
@@ -613,10 +617,10 @@ xhci_init(struct xhci_softc *sc)
 	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);
@@ -749,6 +753,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 +870,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 +965,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 +1099,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 +1118,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) |
@@ -1139,8 +1342,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 +1379,58 @@ 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 0 /* XXX experimental */
+static void
+xhci_clear_endpoint_stall_task(void *addr)
+{
+	usbd_xfer_handle const xfer = addr;
+	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, "slot %u dci %u",
+	 ((struct xhci_slot * const)(xfer->pipe->device->hci_private))->xs_idx,
+	 xhci_ep_get_dci(xfer->pipe->endpoint->edesc), 0, 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, "slot %u dci %u",
+	 ((struct xhci_slot * const)(xfer->pipe->device->hci_private))->xs_idx,
+	 xhci_ep_get_dci(xfer->pipe->endpoint->edesc), 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 +1443,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);
@@ -1248,7 +1490,23 @@ 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 (xfer->pipe == NULL ||
+		    xfer->pipe->device == NULL ||
+		    xfer->pipe->device->bus == NULL ||
+		    SIMPLEQ_EMPTY(&xfer->pipe->queue))
+			break;
 
 		if ((trb_3 & XHCI_TRB_3_ED_BIT) != 0) {
 			DPRINTFN(14, "transfer event data: "
@@ -1272,8 +1530,18 @@ xhci_handle_event(struct xhci_softc * co
 		    XHCI_TRB_ERROR_STALL) {
 			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 0 /* XXX experimental */
+			/*
+			 * asynchronously reset endpoint and
+			 * drop tr->is_halted flag and
+			 * call usb_transfer_complete with err.
+			 */
+			xfer->status = err;
+			xhci_clear_endpoint_stall_async(xfer);
+			break;
+#endif
 		} else {
 			err = USBD_IOERROR;
 		}
@@ -1472,7 +1740,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();
@@ -1515,28 +1784,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 != NULL) {
-		for (int p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) {
+		if (hub->powersrc == NULL)
+			break;
+		dep = hub->depth;
+		if (dep == 0)
+			break;
+		rhport = hub->powersrc->portno;
+		if (dep > USB_HUB_MAX_DEPTH)
+			continue;
+
+		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;
-	}
-	if (speed == USB_SPEED_SUPER) {
-		rhport += sc->sc_ss_port_start - 1;
-	} else {
-		rhport += sc->sc_hs_port_start - 1;
+		dev->myhsport = NULL;
 	}
-	DPRINTFN(4, "rhport %d", rhport, 0, 0, 0);
 
 	dev->speed = speed;
 	dev->langid = USBD_NOLANG;
@@ -1565,7 +1856,7 @@ xhci_new_device(device_t parent, usbd_bu
 		err = xhci_enable_slot(sc, &slot);
 		if (err)
 			return err;
-		err = xhci_init_slot(sc, slot, depth, speed, port, rhport);
+		err = xhci_init_slot(dev, slot, route, rhport);
 		if (err)
 			return err;
 		xs = &sc->sc_slots[slot];
@@ -1582,6 +1873,7 @@ xhci_new_device(device_t parent, usbd_bu
 		KASSERT(bus->devices[dev->address] == NULL);
 		bus->devices[dev->address] = dev;
 
+		/* need reset_port and retry? */
 		err = usbd_get_initial_ddesc(dev, dd);
 		if (err)
 			return err;
@@ -1638,7 +1930,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)
@@ -1911,37 +2204,31 @@ 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;
 		mps = 64;
 		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:
@@ -1978,6 +2265,8 @@ xhci_init_slot(struct xhci_softc * const
 		}
 	}
 
+	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 +2275,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));
@@ -2111,6 +2393,18 @@ static const usb_hub_descriptor_t xhci_h
 	{""},
 };
 
+static const usb_hub_ss_descriptor_t xhci_hubssd = {
+	.bLength		= USB_HUB_SS_DESCRIPTOR_SIZE,
+	.bDescriptorType	= UDESC_SSHUB,
+	.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 */
 
 static usbd_status
@@ -2290,11 +2584,11 @@ xhci_root_ctrl_start(usbd_xfer_handle xf
 	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 +2613,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 +2638,41 @@ 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_SSHUB) {
+			usb_hub_ss_descriptor_t hubssd;
+	
+			hubssd = xhci_hubssd;
+			hubssd.bNbrPorts = sc->sc_maxports;
+			/* XXX not defined in SS hub */
+			USETW(hubssd.wHubCharacteristics, UHD_PWR_NO_SWITCH);
+			hubssd.bPwrOn2PwrGood = 200;
+			for (i = 0, l = hubssd.bNbrPorts;
+			    l > 0; i++, l -= 8)
+				/* XXX can't find out? */
+				hubssd.DeviceRemovable[i++] = 0;
+			hubssd.bLength = USB_HUB_SS_DESCRIPTOR_SIZE + i;
+			l = min(len, hubssd.bLength);
+			hubssd.bLength = l;
+			totlen = l;
+			memcpy(buf, &hubssd, l);
+		} 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 +2692,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 +2705,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 +2717,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 +2740,16 @@ 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;
 	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;
@@ -2702,6 +3042,10 @@ 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
@@ -2822,6 +3166,10 @@ 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



Home | Main Index | Thread Index | Old Index