Module Name:    src
Committed By:   skrll
Date:           Sun Jun  5 08:05:27 UTC 2016

Modified Files:
        src/sys/dev/usb: xhci.c

Log Message:
>From t-hash
    + Gather up setting slot and device context into xhci_setup_ctx(),
      and split out setting route string and TT hub params.
    + Apply htole{32,64} to contexts at once as possible.
    + Use xhci_setup_ctx in xhci_set_address and xhci_configure_endpoint.


To generate a diff of this commit:
cvs rdiff -u -r1.50 -r1.51 src/sys/dev/usb/xhci.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/xhci.c
diff -u src/sys/dev/usb/xhci.c:1.50 src/sys/dev/usb/xhci.c:1.51
--- src/sys/dev/usb/xhci.c:1.50	Sun Jun  5 07:55:28 2016
+++ src/sys/dev/usb/xhci.c	Sun Jun  5 08:05:27 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: xhci.c,v 1.50 2016/06/05 07:55:28 skrll Exp $	*/
+/*	$NetBSD: xhci.c,v 1.51 2016/06/05 08:05:27 skrll Exp $	*/
 
 /*
  * Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.50 2016/06/05 07:55:28 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.51 2016/06/05 08:05:27 skrll Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -150,7 +150,7 @@ static usbd_status xhci_do_command_locke
     struct xhci_trb * const, int);
 static usbd_status xhci_init_slot(struct usbd_device *, uint32_t);
 static void xhci_free_slot(struct xhci_softc *, struct xhci_slot *, int, int);
-static usbd_status xhci_set_address(struct usbd_device *, uint32_t, uint32_t, int, bool);
+static usbd_status xhci_set_address(struct usbd_device *, uint32_t, bool);
 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);
@@ -163,6 +163,12 @@ static usbd_status xhci_ring_init(struct
     struct xhci_ring * const, size_t, size_t);
 static void xhci_ring_free(struct xhci_softc * const, struct xhci_ring * const);
 
+static void xhci_setup_ctx(struct usbd_pipe *);
+static void xhci_setup_route(struct usbd_pipe *, uint32_t *);
+static void xhci_setup_tthub(struct usbd_pipe *, uint32_t *);
+static void xhci_setup_maxburst(struct usbd_pipe *, uint32_t *);
+static uint32_t xhci_bival2ival(uint32_t, uint32_t);
+
 static void xhci_noop(struct usbd_pipe *);
 
 static usbd_status xhci_root_intr_transfer(struct usbd_xfer *);
@@ -561,7 +567,6 @@ xhci_detach(struct xhci_softc *sc, int f
 
 	mutex_destroy(&sc->sc_lock);
 	mutex_destroy(&sc->sc_intr_lock);
-	cv_destroy(&sc->sc_softwake_cv);
 
 	pool_cache_destroy(sc->sc_xferpool);
 
@@ -1018,7 +1023,6 @@ xhci_init(struct xhci_softc *sc)
 	cv_init(&sc->sc_command_cv, "xhcicmd");
 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB);
 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_USB);
-	cv_init(&sc->sc_softwake_cv, "xhciab");
 
 	/* Set up the bus struct. */
 	sc->sc_bus.ub_methods = &xhci_bus_methods;
@@ -1214,267 +1218,6 @@ xhci_xspeed2psspeed(int xspeed)
 	}
 }
 
-/* construct slot context */
-static void
-xhci_setup_sctx(struct usbd_device *dev, uint32_t *cp)
-{
-	usb_device_descriptor_t * const dd = &dev->ud_ddesc;
-	int speed = dev->ud_speed;
-	int tthubslot, ttportnum;
-	bool ishub;
-	bool usemtt;
-
-	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-
-	/*
-	 * 6.2.2, Table 57-60, 6.2.2.1, 6.2.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 ||
-	 *   parent hub is not HS hub ||
-	 *   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 ||
-	 *   parent hub is not HS hub ||
-	 *   attached to root hub.
-	 */
-	if (dev->ud_myhsport != NULL &&
-	    dev->ud_myhub != NULL && dev->ud_myhub->ud_depth != 0 &&
-	    (dev->ud_myhub != NULL &&
-	     dev->ud_myhub->ud_speed == USB_SPEED_HIGH) &&
-	    (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL)) {
-		ttportnum = dev->ud_myhsport->up_portno;
-		tthubslot = dev->ud_myhsport->up_parent->ud_addr;
-	} else {
-		ttportnum = 0;
-		tthubslot = 0;
-	}
-	DPRINTFN(4, "myhsport %p ttportnum=%d tthubslot=%d",
-	    dev->ud_myhsport, ttportnum, tthubslot, 0);
-
-	/* ishub is valid after reading UDESC_DEVICE */
-	ishub = (dd->bDeviceClass == UDCLASS_HUB);
-
-	/* dev->ud_hub is valid after reading UDESC_HUB */
-	if (ishub && dev->ud_hub) {
-		usb_hub_descriptor_t *hd = &dev->ud_hub->uh_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->ud_myhub != NULL && dev->ud_myhub->ud_depth != 0 &&
-	     (dev->ud_myhub != NULL &&
-	      dev->ud_myhub->ud_speed == USB_SPEED_HIGH) &&
-	     dev->ud_myhsport != NULL &&
-	     IS_TTHUB(&dev->ud_myhsport->up_parent->ud_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 uint32_t
-xhci_get_maxburst(struct usbd_pipe *pipe)
-{
-	usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
-	usbd_desc_iter_t iter;
-	const usb_cdc_descriptor_t *cdcd;
-	const usb_endpoint_ss_comp_descriptor_t * esscd = NULL;
-	uint32_t maxb = 0;
-	uint8_t ep;
-
-	cdcd = (const usb_cdc_descriptor_t *)usb_find_desc(
-	    pipe->up_dev, UDESC_INTERFACE, USBD_CDCSUBTYPE_ANY);
-	usb_desc_iter_init(pipe->up_dev, &iter);
-	iter.cur = (const void *)cdcd;
-
-	/* find endpoint_ss_comp desc for ep of this pipe */
-	for (ep = 0;;) {
-		cdcd = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter);
-		if (cdcd == NULL)
-			break;
-		if (ep == 0 && cdcd->bDescriptorType == UDESC_ENDPOINT) {
-			ep = ((const usb_endpoint_descriptor_t *)cdcd)->
-			    bEndpointAddress;
-			if (UE_GET_ADDR(ep) ==
-			    UE_GET_ADDR(ed->bEndpointAddress)) {
-				cdcd = (const usb_cdc_descriptor_t *)
-				    usb_desc_iter_next(&iter);
-				break;
-			}
-			ep = 0;
-		}
-	}
-	if (cdcd != NULL && cdcd->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
-		esscd = (const usb_endpoint_ss_comp_descriptor_t *)cdcd;
-		maxb = esscd->bMaxBurst;
-	}
-
-	return maxb;
-}
-
-/*
- * Convert endpoint bInterval value to endpoint context interval value
- * for Interrupt pipe.
- * xHCI 6.2.3.6 Table 65, USB 2.0 9.6.6
- */
-static uint32_t
-xhci_bival2ival(uint32_t ival, int speed)
-{
-	if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) {
-		int i;
-
-		/*
-		 * round ival down to "the nearest base 2 multiple of
-		 * bInterval * 8".
-		 * bInterval is at most 255 as its type is uByte.
-		 * 255(ms) = 2040(x 125us) < 2^11, so start with 10.
-		 */
-		for (i = 10; i > 0; i--) {
-			if ((ival * 8) >= (1 << i))
-				break;
-		}
-		ival = i;
-	} else {
-		/* Interval = bInterval-1 for SS/HS */
-		ival--;
-	}
-
-	return ival;
-}
-
-/*
- * 4.8.2, 6.2.3.2
- * construct common endpoint parameters
- */
-static void
-xhci_setup_endp_ctx(struct usbd_pipe *pipe, uint32_t *cp)
-{
-	struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
-	usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
-	const u_int dci = xhci_ep_get_dci(ed);
-	const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
-	uint32_t mps = UGETW(ed->wMaxPacketSize);
-	uint32_t maxb = 0;
-	int speed = pipe->up_dev->ud_speed;
-	uint32_t ival = ed->bInterval;
-
-	cp[0] = htole32(
-	    XHCI_EPCTX_0_EPSTATE_SET(0) |
-	    XHCI_EPCTX_0_MULT_SET(0) |	/* always 0 except SS iscoh */
-	    XHCI_EPCTX_0_MAXP_STREAMS_SET(0) |
-	    XHCI_EPCTX_0_LSA_SET(0) |
-	    XHCI_EPCTX_0_MAX_ESIT_PAYLOAD_HI_SET(0)
-	    );
-	cp[1] = htole32(
-	    XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(ed)) |
-	    XHCI_EPCTX_1_HID_SET(0) |
-	    XHCI_EPCTX_1_MAXB_SET(0)
-	    );
-	if (xfertype != UE_ISOCHRONOUS)
-		cp[1] |= htole32(XHCI_EPCTX_1_CERR_SET(3));
-
-	/* 6.2.3.4,  4.8.2.4 */
-	if (USB_IS_SS(speed)) {
-		/* UBS 3.1  9.6.6 */
-		cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(mps));
-		/* UBS 3.1  9.6.7 */
-		maxb = xhci_get_maxburst(pipe);
-		cp[1] |= htole32(XHCI_EPCTX_1_MAXB_SET(maxb));
-	} else {
-		/* UBS 2.0  9.6.6 */
-		cp[1] |= htole32(XHCI_EPCTX_1_MAXP_SIZE_SET(UE_GET_SIZE(mps)));
-
-		/* 6.2.3.4 */
-		if (speed == USB_SPEED_HIGH &&
-		   (xfertype == UE_ISOCHRONOUS || xfertype == UE_INTERRUPT)) {
-			maxb = UE_GET_TRANS(mps);
-		} else {
-			/* LS/FS or HS CTRL or HS BULK */
-			maxb = 0;
-		}
-		cp[1] |= htole32(XHCI_EPCTX_1_MAXB_SET(maxb));
-	}
-
-	if (xfertype == UE_CONTROL)
-		cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)); /* 6.2.3 */
-	else if (USB_IS_SS(speed))
-		cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(mps));
-	else
-		cp[4] = htole32(XHCI_EPCTX_4_AVG_TRB_LEN_SET(UE_GET_SIZE(mps)));
-
-	switch (xfertype) {
-	case UE_CONTROL:
-		break;
-	case UE_BULK:
-		/* XXX Set MaxPStreams, HID, and LSA if streams enabled */
-		break;
-	case UE_INTERRUPT:
-		if (pipe->up_interval != USBD_DEFAULT_INTERVAL)
-			ival = pipe->up_interval;
-
-		ival = xhci_bival2ival(ival, speed);
-		cp[0] |= htole32(XHCI_EPCTX_0_IVAL_SET(ival));
-		break;
-	case UE_ISOCHRONOUS:
-		if (pipe->up_interval != USBD_DEFAULT_INTERVAL)
-			ival = pipe->up_interval;
-
-		/* xHCI 6.2.3.6 Table 65, USB 2.0 9.6.6 */
-		if (speed == USB_SPEED_FULL)
-			ival += 3; /* 1ms -> 125us */
-		ival--;
-		cp[0] |= htole32(XHCI_EPCTX_0_IVAL_SET(ival));
-
-		if (USB_IS_SS(speed)) {
-			/* XXX if LEC = 1, set ESIT instead */
-			cp[0] |= htole32(XHCI_EPCTX_0_MULT_SET(0));
-		}
-		break;
-	default:
-		break;
-	}
-	*(uint64_t *)(&cp[2]) = htole64(
-	    xhci_ring_trbp(&xs->xs_ep[dci].xe_tr, 0) |
-	    XHCI_EPCTX_2_DCS_SET(1));
-}
-
 /*
  * Construct input contexts and issue TRB
  */
@@ -1486,32 +1229,21 @@ xhci_configure_endpoint(struct usbd_pipe
 	const u_int dci = xhci_ep_get_dci(pipe->up_endpoint->ue_edesc);
 	struct xhci_trb trb;
 	usbd_status err;
-	uint32_t *cp;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 	DPRINTFN(4, "slot %u dci %u epaddr 0x%02x attr 0x%02x",
 	    xs->xs_idx, dci, pipe->up_endpoint->ue_edesc->bEndpointAddress,
 	    pipe->up_endpoint->ue_edesc->bmAttributes);
 
+	KASSERT(!mutex_owned(&sc->sc_lock));
+
 	/* XXX ensure input context is available? */
 
 	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(0);
-	cp[1] = htole32(XHCI_INCTX_1_ADD_MASK(dci));
-
-	/* set up input slot context */
-	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
-	xhci_setup_sctx(pipe->up_dev, cp);
-	cp[0] |= htole32(XHCI_SCTX_0_CTX_NUM_SET(dci));
-
-	/* set up input endpoint context */
-	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(dci));
-	xhci_setup_endp_ctx(pipe, cp);
+	/* set up context */
+	xhci_setup_ctx(pipe);
 
-	/* sync input contexts before they are read from memory */
-	usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE);
 	hexdump("input control context", xhci_slot_get_icv(sc, xs, 0),
 	    sc->sc_ctxsz * 1);
 	hexdump("input endpoint context", xhci_slot_get_icv(sc, xs,
@@ -2224,31 +1956,38 @@ xhci_new_device(device_t parent, struct 
 	struct usbd_device *dev;
 	usbd_status err;
 	usb_device_descriptor_t *dd;
-	struct usbd_device *hub;
-	struct usbd_device *adev;
-	int rhport = 0;
 	struct xhci_slot *xs;
 	uint32_t *cp;
-	uint32_t route = 0;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "port=%d depth=%d speed=%d upport %d",
-		 port, depth, speed, up->up_portno);
+	DPRINTFN(4, "port %u depth %u speed %u up %p", port, depth, speed, up);
 
 	dev = kmem_zalloc(sizeof(*dev), KM_SLEEP);
 	if (dev == NULL)
 		return USBD_NOMEM;
 
 	dev->ud_bus = bus;
+	dev->ud_quirks = &usbd_no_quirk;
+	dev->ud_addr = 0;
+	dev->ud_ddesc.bMaxPacketSize = 0;
+	dev->ud_depth = depth;
+	dev->ud_powersrc = up;
+	dev->ud_myhub = up->up_parent;
+	dev->ud_speed = speed;
+	dev->ud_langid = USBD_NOLANG;
+	dev->ud_cookie.cookie = ++usb_cookie_no;
 
 	/* Set up default endpoint handle. */
 	dev->ud_ep0.ue_edesc = &dev->ud_ep0desc;
+	/* doesn't matter, just don't let it uninitialized */
+	dev->ud_ep0.ue_toggle = 0;
 
 	/* Set up default endpoint descriptor. */
 	dev->ud_ep0desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE;
 	dev->ud_ep0desc.bDescriptorType = UDESC_ENDPOINT;
 	dev->ud_ep0desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
 	dev->ud_ep0desc.bmAttributes = UE_CONTROL;
+	dev->ud_ep0desc.bInterval = 0;
 
 	/* 4.3,  4.8.2.1 */
 	switch (speed) {
@@ -2266,70 +2005,9 @@ xhci_new_device(device_t parent, struct 
 		USETW(dev->ud_ep0desc.wMaxPacketSize, USB_MAX_IPACKET);
 		break;
 	}
-	dev->ud_ep0desc.bInterval = 0;
-
-	/* doesn't matter, just don't let it uninitialized */
-	dev->ud_ep0.ue_toggle = 0;
-
-	DPRINTFN(4, "up %p portno %d", up, up->up_portno, 0, 0);
-
-	dev->ud_quirks = &usbd_no_quirk;
-	dev->ud_addr = 0;
-	dev->ud_ddesc.bMaxPacketSize = 0;
-	dev->ud_depth = depth;
-	dev->ud_powersrc = up;
-	dev->ud_myhub = up->up_parent;
 
 	up->up_dev = dev;
 
-	/* Locate root hub port */
-	for (hub = dev; hub != NULL; hub = hub->ud_myhub) {
-		uint32_t dep;
-
-		DPRINTFN(4, "hub %p depth %d upport %p upportno %d",
-		    hub, hub->ud_depth, hub->ud_powersrc,
-		    hub->ud_powersrc ? hub->ud_powersrc->up_portno : -1);
-
-		if (hub->ud_powersrc == NULL)
-			break;
-		dep = hub->ud_depth;
-		if (dep == 0)
-			break;
-		rhport = hub->ud_powersrc->up_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->up_parent;
-	     hub != NULL && hub->ud_speed != USB_SPEED_HIGH;
-	     adev = hub, hub = hub->ud_myhub)
-		;
-	if (hub) {
-		int p;
-		for (p = 0; p < hub->ud_hub->uh_hubdesc.bNbrPorts; p++) {
-			if (hub->ud_hub->uh_ports[p].up_dev == adev) {
-				dev->ud_myhsport = &hub->ud_hub->uh_ports[p];
-				goto found;
-			}
-		}
-		panic("xhci_new_device: cannot find HS port");
-	found:
-		DPRINTFN(4, "high speed port %d", p, 0, 0, 0);
-	} else {
-		dev->ud_myhsport = NULL;
-	}
-
-	dev->ud_speed = speed;
-	dev->ud_langid = USBD_NOLANG;
-	dev->ud_cookie.cookie = ++usb_cookie_no;
-
 	/* Establish the default pipe. */
 	err = usbd_setup_pipe(dev, 0, &dev->ud_ep0, USBD_DEFAULT_INTERVAL,
 	    &dev->ud_pipe0);
@@ -2375,7 +2053,7 @@ xhci_new_device(device_t parent, struct 
 		}
 
 		/* 4.3.4 Address Assignment */
-		err = xhci_set_address(dev, slot, route, rhport, false);
+		err = xhci_set_address(dev, slot, false);
 		if (err)
 			goto bad;
 
@@ -2917,60 +2595,415 @@ xhci_free_slot(struct xhci_softc *sc, st
  * Set Address Device command.
  */
 static usbd_status
-xhci_set_address(struct usbd_device *dev, uint32_t slot, uint32_t route,
-    int rhport, bool bsr)
+xhci_set_address(struct usbd_device *dev, uint32_t slot, bool bsr)
 {
 	struct xhci_softc * const sc = XHCI_BUS2SC(dev->ud_bus);
 	struct xhci_slot *xs;
 	usbd_status err;
-	uint32_t *cp;
-	uint32_t mps = UGETW(dev->ud_ep0desc.wMaxPacketSize);
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+	DPRINTFN(4, "slot %u bsr %u", slot, bsr, 0, 0);
 
 	xs = &sc->sc_slots[slot];
 
+	xhci_setup_ctx(dev->ud_pipe0);
+
+	hexdump("input context", xhci_slot_get_icv(sc, xs, 0),
+	    sc->sc_ctxsz * 3);
+
+	xhci_set_dcba(sc, DMAADDR(&xs->xs_dc_dma, 0), slot);
+
+	err = xhci_address_device(sc, xhci_slot_get_icp(sc, xs, 0), slot, bsr);
+
+	usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
+	hexdump("output context", xhci_slot_get_dcv(sc, xs, 0),
+	    sc->sc_ctxsz * 2);
+
+	return err;
+}
+
+/*
+ * 4.8.2, 6.2.3.2
+ * construct slot/endpoint context parameters and do syncmem
+ */
+static void
+xhci_setup_ctx(struct usbd_pipe *pipe)
+{
+	struct xhci_softc * const sc = XHCI_PIPE2SC(pipe);
+	struct usbd_device *dev = pipe->up_dev;
+	struct xhci_slot * const xs = dev->ud_hcpriv;
+	usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
+	const u_int dci = xhci_ep_get_dci(ed);
+	const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+	uint32_t *cp;
+	uint16_t mps = UGETW(ed->wMaxPacketSize);
+	uint8_t speed = dev->ud_speed;
+	uint8_t ival = ed->bInterval;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+	DPRINTFN(4, "pipe %p: slot %u dci %u speed %u", pipe, xs->xs_idx, dci,
+	    speed);
+
 	/* set up initial input control context */
 	cp = xhci_slot_get_icv(sc, xs, XHCI_ICI_INPUT_CONTROL);
 	cp[0] = htole32(0);
-	cp[1] = htole32(XHCI_INCTX_1_ADD_MASK(XHCI_DCI_EP_CONTROL)|
-	    XHCI_INCTX_1_ADD_MASK(XHCI_DCI_SLOT));
+	cp[1] = htole32(XHCI_INCTX_1_ADD_MASK(dci));
+	if (dci == XHCI_DCI_EP_CONTROL)
+		cp[1] |= htole32(XHCI_INCTX_1_ADD_MASK(XHCI_DCI_SLOT));
+	cp[7] = htole32(0);
 
 	/* set up input slot context */
 	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_SLOT));
-	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));
+	cp[0] =
+	    XHCI_SCTX_0_CTX_NUM_SET(dci) |
+	    XHCI_SCTX_0_SPEED_SET(xhci_speed2xspeed(speed));
+	cp[1] = 0;
+	cp[2] = XHCI_SCTX_2_IRQ_TARGET_SET(0);
+	cp[3] = 0;
+	xhci_setup_route(pipe, cp);
+	xhci_setup_tthub(pipe, cp);
+
+	cp[0] = htole32(cp[0]);
+	cp[1] = htole32(cp[1]);
+	cp[2] = htole32(cp[2]);
+	cp[3] = htole32(cp[3]);
+
+	/* set up input endpoint context */
+	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(dci));
+	cp[0] =
+	    XHCI_EPCTX_0_EPSTATE_SET(0) |
+	    XHCI_EPCTX_0_MULT_SET(0) |
+	    XHCI_EPCTX_0_MAXP_STREAMS_SET(0) |
+	    XHCI_EPCTX_0_LSA_SET(0) |
+	    XHCI_EPCTX_0_MAX_ESIT_PAYLOAD_HI_SET(0);
+	cp[1] =
+	    XHCI_EPCTX_1_EPTYPE_SET(xhci_ep_get_type(ed)) |
+	    XHCI_EPCTX_1_HID_SET(0) |
+	    XHCI_EPCTX_1_MAXB_SET(0);
+
+	if (xfertype != UE_ISOCHRONOUS)
+		cp[1] |= XHCI_EPCTX_1_CERR_SET(3);
+
+	if (xfertype == UE_CONTROL)
+		cp[4] = XHCI_EPCTX_4_AVG_TRB_LEN_SET(8); /* 6.2.3 */
+	else if (USB_IS_SS(speed))
+		cp[4] = XHCI_EPCTX_4_AVG_TRB_LEN_SET(mps);
+	else
+		cp[4] = XHCI_EPCTX_4_AVG_TRB_LEN_SET(UE_GET_SIZE(mps));
+
+	xhci_setup_maxburst(pipe, cp);
+
+	switch (xfertype) {
+	case UE_CONTROL:
+		break;
+	case UE_BULK:
+		/* XXX Set MaxPStreams, HID, and LSA if streams enabled */
+		break;
+	case UE_INTERRUPT:
+		if (pipe->up_interval != USBD_DEFAULT_INTERVAL)
+			ival = pipe->up_interval;
+
+		ival = xhci_bival2ival(ival, speed);
+		cp[0] |= XHCI_EPCTX_0_IVAL_SET(ival);
+		break;
+	case UE_ISOCHRONOUS:
+		if (pipe->up_interval != USBD_DEFAULT_INTERVAL)
+			ival = pipe->up_interval;
+
+		/* xHCI 6.2.3.6 Table 65, USB 2.0 9.6.6 */
+		if (speed == USB_SPEED_FULL)
+			ival += 3; /* 1ms -> 125us */
+		ival--;
+		cp[0] |= XHCI_EPCTX_0_IVAL_SET(ival);
+		break;
+	default:
+		break;
+	}
 
-	/* set up input EP0 context */
-	cp = xhci_slot_get_icv(sc, xs, xhci_dci_to_ici(XHCI_DCI_EP_CONTROL));
-	cp[0] = htole32(0);
-	cp[1] = htole32(
-		XHCI_EPCTX_1_MAXP_SIZE_SET(mps) |
-		XHCI_EPCTX_1_EPTYPE_SET(4) |
-		XHCI_EPCTX_1_CERR_SET(3)
-		);
 	/* can't use xhci_ep_get_dci() yet? */
 	*(uint64_t *)(&cp[2]) = htole64(
-	    xhci_ring_trbp(&xs->xs_ep[XHCI_DCI_EP_CONTROL].xe_tr, 0) |
+	    xhci_ring_trbp(&xs->xs_ep[dci].xe_tr, 0) |
 	    XHCI_EPCTX_2_DCS_SET(1));
-	cp[4] = htole32(
-		XHCI_EPCTX_4_AVG_TRB_LEN_SET(8)
-		);
+
+	cp[0] = htole32(cp[0]);
+	cp[1] = htole32(cp[1]);
+	cp[4] = htole32(cp[4]);
 
 	/* sync input contexts before they are read from memory */
 	usb_syncmem(&xs->xs_ic_dma, 0, sc->sc_pgsz, BUS_DMASYNC_PREWRITE);
-	hexdump("input context", xhci_slot_get_icv(sc, xs, 0),
-	    sc->sc_ctxsz * 3);
+}
 
-	xhci_set_dcba(sc, DMAADDR(&xs->xs_dc_dma, 0), slot);
+/*
+ * Setup route string and roothub port of given device for slot context
+ */
+static void
+xhci_setup_route(struct usbd_pipe *pipe, uint32_t *cp)
+{
+	struct usbd_device *dev = pipe->up_dev;
+	struct usbd_port *up = dev->ud_powersrc;
+	struct usbd_device *hub;
+	struct usbd_device *adev;
+	uint8_t rhport = 0;
+	uint32_t route = 0;
 
-	err = xhci_address_device(sc, xhci_slot_get_icp(sc, xs, 0), slot, bsr);
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 
-	usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
-	hexdump("output context", xhci_slot_get_dcv(sc, xs, 0),
-	    sc->sc_ctxsz * 2);
+	/* Locate root hub port and Determine route string */
+	/* 4.3.3 route string does not include roothub port */
+	for (hub = dev; hub != NULL; hub = hub->ud_myhub) {
+		uint32_t dep;
 
-	return err;
+		DPRINTFN(4, "hub %p depth %d upport %p upportno %d",
+		    hub, hub->ud_depth, hub->ud_powersrc,
+		    hub->ud_powersrc ? hub->ud_powersrc->up_portno : -1);
+
+		if (hub->ud_powersrc == NULL)
+			break;
+		dep = hub->ud_depth;
+		if (dep == 0)
+			break;
+		rhport = hub->ud_powersrc->up_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 %u Route %05x hub %p", rhport, route, hub, 0);
+
+	/* Locate port on upstream high speed hub */
+	for (adev = dev, hub = up->up_parent;
+	     hub != NULL && hub->ud_speed != USB_SPEED_HIGH;
+	     adev = hub, hub = hub->ud_myhub)
+		;
+	if (hub) {
+		int p;
+		for (p = 0; p < hub->ud_hub->uh_hubdesc.bNbrPorts; p++) {
+			if (hub->ud_hub->uh_ports[p].up_dev == adev) {
+				dev->ud_myhsport = &hub->ud_hub->uh_ports[p];
+				goto found;
+			}
+		}
+		panic("xhci_setup_route: cannot find HS port");
+	found:
+		DPRINTFN(4, "high speed port %d", p, 0, 0, 0);
+	} else {
+		dev->ud_myhsport = NULL;
+	}
+
+	cp[0] |= XHCI_SCTX_0_ROUTE_SET(route);
+	cp[1] |= XHCI_SCTX_1_RH_PORT_SET(rhport);
+}
+
+/*
+ * Setup whether device is hub, whether device uses MTT, and
+ * TT informations if it uses MTT.
+ */
+static void
+xhci_setup_tthub(struct usbd_pipe *pipe, uint32_t *cp)
+{
+	struct usbd_device *dev = pipe->up_dev;
+	usb_device_descriptor_t * const dd = &dev->ud_ddesc;
+	uint32_t speed = dev->ud_speed;
+	uint8_t tthubslot, ttportnum;
+	bool ishub;
+	bool usemtt;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	/*
+	 * 6.2.2, Table 57-60, 6.2.2.1, 6.2.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 ||
+	 *   parent hub is not HS hub ||
+	 *   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 ||
+	 *   parent hub is not HS hub ||
+	 *   attached to root hub.
+	 */
+	if (dev->ud_myhsport != NULL &&
+	    dev->ud_myhub != NULL && dev->ud_myhub->ud_depth != 0 &&
+	    (dev->ud_myhub != NULL &&
+	     dev->ud_myhub->ud_speed == USB_SPEED_HIGH) &&
+	    (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL)) {
+		ttportnum = dev->ud_myhsport->up_portno;
+		tthubslot = dev->ud_myhsport->up_parent->ud_addr;
+	} else {
+		ttportnum = 0;
+		tthubslot = 0;
+	}
+	DPRINTFN(4, "myhsport %p ttportnum=%d tthubslot=%d",
+	    dev->ud_myhsport, ttportnum, tthubslot, 0);
+
+	/* ishub is valid after reading UDESC_DEVICE */
+	ishub = (dd->bDeviceClass == UDCLASS_HUB);
+
+	/* dev->ud_hub is valid after reading UDESC_HUB */
+	if (ishub && dev->ud_hub) {
+		usb_hub_descriptor_t *hd = &dev->ud_hub->uh_hubdesc;
+		uint8_t ttt =
+		    __SHIFTOUT(UGETW(hd->wHubCharacteristics), UHD_TT_THINK);
+
+		cp[1] |= XHCI_SCTX_1_NUM_PORTS_SET(hd->bNbrPorts);
+		cp[2] |= XHCI_SCTX_2_TT_THINK_TIME_SET(ttt);
+		DPRINTFN(4, "nports=%d ttt=%d", hd->bNbrPorts, ttt, 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->ud_myhub != NULL && dev->ud_myhub->ud_depth != 0 &&
+	     (dev->ud_myhub != NULL &&
+	      dev->ud_myhub->ud_speed == USB_SPEED_HIGH) &&
+	     dev->ud_myhsport != NULL &&
+	     IS_TTHUB(&dev->ud_myhsport->up_parent->ud_ddesc))
+		usemtt = true;
+	else
+		usemtt = false;
+	DPRINTFN(4, "class %u proto %u ishub %d usemtt %d",
+	    dd->bDeviceClass, dd->bDeviceProtocol, ishub, usemtt);
+
+#undef IS_TTHUB
+
+	cp[0] |=
+	    XHCI_SCTX_0_HUB_SET(ishub ? 1 : 0) |
+	    XHCI_SCTX_0_MTT_SET(usemtt ? 1 : 0);
+	cp[2] |=
+	    XHCI_SCTX_2_TT_HUB_SID_SET(tthubslot) |
+	    XHCI_SCTX_2_TT_PORT_NUM_SET(ttportnum);
+}
+
+/* set up params for periodic endpoint */
+static void
+xhci_setup_maxburst(struct usbd_pipe *pipe, uint32_t *cp)
+{
+	struct usbd_device *dev = pipe->up_dev;
+	usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
+	const uint8_t xfertype = UE_GET_XFERTYPE(ed->bmAttributes);
+	usbd_desc_iter_t iter;
+	const usb_cdc_descriptor_t *cdcd;
+	uint32_t maxb = 0;
+	uint16_t mps = UGETW(ed->wMaxPacketSize);
+	uint8_t speed = dev->ud_speed;
+	uint8_t ep;
+
+	/* config desc is NULL when opening ep0 */
+	if (dev == NULL || dev->ud_cdesc == NULL)
+		goto no_cdcd;
+	cdcd = (const usb_cdc_descriptor_t *)usb_find_desc(dev,
+	    UDESC_INTERFACE, USBD_CDCSUBTYPE_ANY);
+	if (cdcd == NULL)
+		goto no_cdcd;
+	usb_desc_iter_init(dev, &iter);
+	iter.cur = (const void *)cdcd;
+
+	/* find endpoint_ss_comp desc for ep of this pipe */
+	for (ep = 0;;) {
+		cdcd = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter);
+		if (cdcd == NULL)
+			break;
+		if (ep == 0 && cdcd->bDescriptorType == UDESC_ENDPOINT) {
+			ep = ((const usb_endpoint_descriptor_t *)cdcd)->
+			    bEndpointAddress;
+			if (UE_GET_ADDR(ep) ==
+			    UE_GET_ADDR(ed->bEndpointAddress)) {
+				cdcd = (const usb_cdc_descriptor_t *)
+				    usb_desc_iter_next(&iter);
+				break;
+			}
+			ep = 0;
+		}
+	}
+	if (cdcd != NULL && cdcd->bDescriptorType == UDESC_ENDPOINT_SS_COMP) {
+		const usb_endpoint_ss_comp_descriptor_t * esscd =
+		    (const usb_endpoint_ss_comp_descriptor_t *)cdcd;
+		maxb = esscd->bMaxBurst;
+	}
+
+ no_cdcd:
+	/* 6.2.3.4,  4.8.2.4 */
+	if (USB_IS_SS(speed)) {
+		/* UBS 3.1  9.6.6 */
+		cp[1] |= XHCI_EPCTX_1_MAXP_SIZE_SET(mps);
+		/* UBS 3.1  9.6.7 */
+		cp[1] |= XHCI_EPCTX_1_MAXB_SET(maxb);
+#ifdef notyet
+		if (xfertype == UE_ISOCHRONOUS) {
+		}
+		if (XHCI_HCC2_LEC(sc->sc_hcc2) != 0) {
+			/* use ESIT */
+			cp[4] |= XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(x);
+			cp[0] |= XHCI_EPCTX_0_MAX_ESIT_PAYLOAD_HI_SET(x);
+
+			/* XXX if LEC = 1, set ESIT instead */
+			cp[0] |= XHCI_EPCTX_0_MULT_SET(0);
+		} else {
+			/* use ival */
+		}
+#endif
+	} else {
+		/* UBS 2.0  9.6.6 */
+		cp[1] |= XHCI_EPCTX_1_MAXP_SIZE_SET(UE_GET_SIZE(mps));
+
+		/* 6.2.3.4 */
+		if (speed == USB_SPEED_HIGH &&
+		   (xfertype == UE_ISOCHRONOUS || xfertype == UE_INTERRUPT)) {
+			maxb = UE_GET_TRANS(mps);
+		} else {
+			/* LS/FS or HS CTRL or HS BULK */
+			maxb = 0;
+		}
+		cp[1] |= XHCI_EPCTX_1_MAXB_SET(maxb);
+	}
+}
+
+/*
+ * Convert endpoint bInterval value to endpoint context interval value
+ * for Interrupt pipe.
+ * xHCI 6.2.3.6 Table 65, USB 2.0 9.6.6
+ */
+static uint32_t
+xhci_bival2ival(uint32_t ival, uint32_t speed)
+{
+	if (speed == USB_SPEED_LOW || speed == USB_SPEED_FULL) {
+		int i;
+
+		/*
+		 * round ival down to "the nearest base 2 multiple of
+		 * bInterval * 8".
+		 * bInterval is at most 255 as its type is uByte.
+		 * 255(ms) = 2040(x 125us) < 2^11, so start with 10.
+		 */
+		for (i = 10; i > 0; i--) {
+			if ((ival * 8) >= (1 << i))
+				break;
+		}
+		ival = i;
+	} else {
+		/* Interval = bInterval-1 for SS/HS */
+		ival--;
+	}
+
+	return ival;
 }
 
 /* ----- */

Reply via email to