tech-kern archive

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

xhci patch 20150623



Hello,

Here is xhci patch for nick-nhusb branch.


nh-xhci-check.diff
	+ Add port range check in xhci_rhpsc().
	+ Add sanity check if xfer->ux_pipe != NULL in xhci_handle_event().
nh-xhci-prc.diff
	+ Remove SET_FEATURE C_PORT_RESET(PRC) -- what would happen?
nh-xhci-methods1.diff
	+ Sync return type of xhci_close_pipe() with return type of
	  upm_close method.
nh-xhci-abort.diff
	+ Add routines for aborting xfer and command.
nh-xhci-comments.diff
	+ Update comments.
nh-usb_subr.diff
	+ Don't give up doing SET_CONFIG in usbd_set_config_index()
	  even if it fails to get BOS descriptor.
	  In this case ud_bdesc will be NULL.


Known problems:

+ HS hub in 3.0 hub under 3.0 port is disconnected and reconnected every
   several minutes repeatedly.
   I don't know what is culprit yet.
+ Detaching hubs or devices might cause panic.
   Especially when the hub that hangs many devices is disconnected.
+ KASSERT(sc->sc_command_addr == 0) in xhci_do_command() may fail.
   If two or more threads run xhci_do_command(), all of them except one
   should be blocked at mutex_enter. But one of blocked thread can get
   sc_lock with sc_command_addr != 0 when cv_timedwait() unlocks sc_lock.
+ xhci.c cannot handle cross-64k transfer yet.
   (It works anyway though.)
+ Power management is not implemented.
+ USB keyboard interaction is laggy and annoying.
+ ASM1042 and some Intel PCH do not work at all.
   No interrupts.
+ Fresco1100 does not report CSC if the device is connected at boot.
   Only PortResetChange is set in change bits.
   uhub ignores ports without CSC bit, so cannot detect devices.
+ SS part of asm107x hub under hudson2 xhci roothub is not recognized.
   It drops Port Enable Disable (PED) bit after port reset.
   rhpsc: 0x21203<CSC,PIC=0x0,XSPEED=0x4=SS,PP,PLS=0x0=U0,PED,CCS>
   after reset: 0x6202c0<PLC,PRC,CSC,PIC=0x0,XSPEED=0x0=NONE,PP,PLS=0x6=SS_INA>
+ axen does not work after interface up -> down -> up.
   When the interface goes down, axen driver closes both of RX and TX pipes.
   That causes transtion slot state of this device to ADDRESSED from CONFIGURED.
+ xhci.c does not snoop SET_CONFIG request and does not transtion the
   slot state to CONFIGURED from ADDRESSED even if SET_CONFIG is issued.
+ Isochronous transfer is not implemented.
+ Stream protocol is not supported.
+ Conexant CX93010 umodem is not recognized (XACT in ADDRESS_DEVICE).
   It can be recognized by inserting 150ms delay before port reset
   while enumeration.
+ usbd_clear_endpoint_stall{,_async}() does not work on xhci to clear stall
   condition because this function does not issue RESET_ENDPOINT command.
   However, xhci.c detects whether xfer is stalled and issues RESET_ENDPOINT
   automatically.
+ Address of root hub is 0 (but it works anyway).
+ Not sure it work on big endian machines.

+ 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.
   (currently usbdevs is not part of nick-nhusb branch.)


--
t-hash

--- src/sys/dev/usb/xhci.c.orig	2015-06-08 20:49:41.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2015-06-08 21:15:24.000000000 +0900
@@ -1683,11 +1683,15 @@ xhci_rhpsc(struct xhci_softc * const sc,
 	uint8_t *p;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "port %u status change", port, 0, 0, 0);
+	DPRINTFN(4, "xhci%d: port %u status change",
+	    device_unit(sc->sc_dev), port, 0, 0);
 
 	if (xfer == NULL)
 		return;
 
+	if (!(port >= 1 && port <= sc->sc_maxports))
+		return;
+
 	p = xfer->ux_buf;
 	memset(p, 0, xfer->ux_length);
 	p[port/NBBY] |= 1 << (port%NBBY);
@@ -1771,6 +1775,10 @@ xhci_handle_event(struct xhci_softc * co
 		}
 		DPRINTFN(14, "xfer %p", xfer, 0, 0, 0);
 		/* XXX I dunno why this happens */
+		if (xfer->ux_pipe == NULL) {
+			DPRINTFN(1, "xfer done: ux_pipe is NULL", 0, 0, 0, 0);
+			break;
+		}
 		if (!xfer->ux_pipe->up_repeat &&
 		    SIMPLEQ_EMPTY(&xfer->ux_pipe->up_queue)) {
 			DPRINTFN(1, "xfer done: xfer not started", 0, 0, 0, 0);

--- src/sys/dev/usb/xhci.c.orig	2015-06-14 16:07:49.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2015-06-14 16:14:28.000000000 +0900
@@ -2867,9 +2867,6 @@ xhci_roothub_ctrl(struct usbd_bus *bus, 
 			/* XXX power control */
 			break;
 		/* XXX more */
-		case UHF_C_PORT_RESET:
-			xhci_op_write_4(sc, port, v | XHCI_PS_PRC);
-			break;
 		case UHF_PORT_U1_TIMEOUT:
 			if (USB_IS_SS(xhci_xspeed2speed(XHCI_PS_SPEED_GET(v)))){
 				return -1;

--- src/sys/dev/usb/xhci.c.orig	2015-06-14 16:14:28.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2015-06-14 16:57:52.000000000 +0900
@@ -125,6 +125,7 @@ struct xhci_pipe {
 #define XHCI_TRB_3_ED_BIT XHCI_TRB_3_ISP_BIT
 
 static usbd_status xhci_open(struct usbd_pipe *);
+static void xhci_close_pipe(struct usbd_pipe *);
 static int xhci_intr1(struct xhci_softc * const);
 static void xhci_softintr(void *);
 static void xhci_poll(struct usbd_bus *);
@@ -1506,7 +1507,7 @@ xhci_open(struct usbd_pipe *pipe)
  * If the endpoint to be closed is ep0, disable_slot.
  * Should be called with sc_lock held.
  */
-static usbd_status
+static void
 xhci_close_pipe(struct usbd_pipe *pipe)
 {
 	struct xhci_softc * const sc = pipe->up_dev->ud_bus->ub_hcpriv;
@@ -1514,17 +1515,16 @@ xhci_close_pipe(struct usbd_pipe *pipe)
 	usb_endpoint_descriptor_t * const ed = pipe->up_endpoint->ue_edesc;
 	const u_int dci = xhci_ep_get_dci(ed);
 	struct xhci_trb trb;
-	usbd_status err;
 	uint32_t *cp;
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 
 	if (sc->sc_dying)
-		return USBD_IOERROR;
+		return;
 
 	if (xs == NULL || xs->xs_idx == 0)
 		/* xs is uninitialized before xhci_init_slot */
-		return USBD_IOERROR;
+		return;
 
 	DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
@@ -1532,11 +1532,12 @@ xhci_close_pipe(struct usbd_pipe *pipe)
 	KASSERT(mutex_owned(&sc->sc_lock));
 
 	if (pipe->up_dev->ud_depth == 0)
-		return USBD_NORMAL_COMPLETION;
+		return;
 
 	if (dci == XHCI_DCI_EP_CONTROL) {
 		DPRINTFN(4, "closing ep0", 0, 0, 0, 0);
-		return xhci_disable_slot(sc, xs->xs_idx);
+		xhci_disable_slot(sc, xs->xs_idx);
+		return;
 	}
 
 	/*
@@ -1566,10 +1567,8 @@ xhci_close_pipe(struct usbd_pipe *pipe)
 	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);
+	(void)xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
 	usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
-
-	return err;
 }
 
 /*

--- src/sys/dev/usb/xhci.c.orig	2015-06-14 16:57:52.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2015-06-14 20:20:33.000000000 +0900
@@ -139,17 +139,12 @@ static int xhci_roothub_ctrl(struct usbd
 
 static usbd_status xhci_configure_endpoint(struct usbd_pipe *);
 //static usbd_status xhci_unconfigure_endpoint(struct usbd_pipe *);
-static usbd_status xhci_reset_endpoint(struct usbd_pipe *);
-static usbd_status xhci_stop_endpoint(struct usbd_pipe *);
-
-static usbd_status xhci_set_dequeue(struct usbd_pipe *);
+static usbd_status xhci_reset_endpoint(struct usbd_pipe *, int);
+static usbd_status xhci_stop_endpoint(struct usbd_pipe *, int);
+static usbd_status xhci_set_dequeue(struct usbd_pipe *, int);
 
 static usbd_status xhci_do_command(struct xhci_softc * const,
-    struct xhci_trb * const, int);
-static usbd_status xhci_do_command1(struct xhci_softc * const,
     struct xhci_trb * const, int, int);
-static usbd_status xhci_do_command_locked(struct xhci_softc * const,
-    struct xhci_trb * const, int);
 static usbd_status xhci_init_slot(struct usbd_device *, uint32_t, int, int);
 static usbd_status xhci_enable_slot(struct xhci_softc * const,
     uint8_t * const);
@@ -290,7 +285,6 @@ xhci_op_write_4(const struct xhci_softc 
 	bus_space_write_4(sc->sc_iot, sc->sc_obh, offset, value);
 }
 
-#if 0 /* unused */
 static inline uint64_t
 xhci_op_read_8(const struct xhci_softc * const sc, bus_size_t offset)
 {
@@ -310,7 +304,6 @@ xhci_op_read_8(const struct xhci_softc *
 
 	return value;
 }
-#endif /* unused */
 
 static inline void
 xhci_op_write_8(const struct xhci_softc * const sc, bus_size_t offset,
@@ -498,6 +491,15 @@ xhci_trb_put(struct xhci_trb * const trb
 	trb->trb_3 = control;
 }
 
+static unsigned int
+xhci_get_epstate(struct xhci_softc * const sc, struct xhci_slot * const xs,
+    u_int dci)
+{
+	return XHCI_EPCTX_0_EPSTATE_GET(
+	    le32toh(((uint32_t *)xhci_slot_get_dcv(sc, xs, dci)))
+	    [0]);
+}
+
 /* --- */
 
 void
@@ -1320,7 +1322,7 @@ xhci_configure_endpoint(struct usbd_pipe
 	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(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, 0);
 
 	usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
 	hexdump("output context", xhci_slot_get_dcv(sc, xs, dci),
@@ -1346,7 +1348,7 @@ xhci_unconfigure_endpoint(struct usbd_pi
 
 /* 4.6.8, 6.4.3.7 */
 static usbd_status
-xhci_reset_endpoint(struct usbd_pipe *pipe)
+xhci_reset_endpoint(struct usbd_pipe *pipe, int locked)
 {
 	struct xhci_softc * const sc = pipe->up_dev->ud_bus->ub_hcpriv;
 	struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
@@ -1357,7 +1359,8 @@ xhci_reset_endpoint(struct usbd_pipe *pi
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 	DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
-	KASSERT(!mutex_owned(&sc->sc_lock));
+	if (!locked)
+		KASSERT(!mutex_owned(&sc->sc_lock));
 
 	trb.trb_0 = 0;
 	trb.trb_2 = 0;
@@ -1365,7 +1368,7 @@ xhci_reset_endpoint(struct usbd_pipe *pi
 	    XHCI_TRB_3_EP_SET(dci) |
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP);
 
-	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, locked);
 
 	return err;
 }
@@ -1376,7 +1379,7 @@ xhci_reset_endpoint(struct usbd_pipe *pi
  * Should be called with sc_lock held.
  */
 static usbd_status
-xhci_stop_endpoint(struct usbd_pipe *pipe)
+xhci_stop_endpoint(struct usbd_pipe *pipe, int locked)
 {
 	struct xhci_softc * const sc = pipe->up_dev->ud_bus->ub_hcpriv;
 	struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
@@ -1395,7 +1398,7 @@ xhci_stop_endpoint(struct usbd_pipe *pip
 	    XHCI_TRB_3_EP_SET(dci) |
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP);
 
-	err = xhci_do_command_locked(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, locked);
 
 	return err;
 }
@@ -1407,7 +1410,7 @@ xhci_stop_endpoint(struct usbd_pipe *pip
  * EPSTATE of endpoint must be ERROR or STOPPED, or CONTEXT_STATE error.
  */
 static usbd_status
-xhci_set_dequeue(struct usbd_pipe *pipe)
+xhci_set_dequeue(struct usbd_pipe *pipe, int locked)
 {
 	struct xhci_softc * const sc = pipe->up_dev->ud_bus->ub_hcpriv;
 	struct xhci_slot * const xs = pipe->up_dev->ud_hcpriv;
@@ -1419,6 +1422,9 @@ xhci_set_dequeue(struct usbd_pipe *pipe)
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 	DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
 
+	if (!locked)
+		KASSERT(!mutex_owned(&sc->sc_lock));
+
 	memset(xr->xr_trb, 0, xr->xr_ntrb * XHCI_TRB_SIZE);
 	usb_syncmem(&xr->xr_dma, 0, xr->xr_ntrb * XHCI_TRB_SIZE,
 	    BUS_DMASYNC_PREWRITE);
@@ -1433,7 +1439,7 @@ xhci_set_dequeue(struct usbd_pipe *pipe)
 	    XHCI_TRB_3_EP_SET(dci) |
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE);
 
-	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, locked);
 
 	return err;
 }
@@ -1526,7 +1532,7 @@ xhci_close_pipe(struct usbd_pipe *pipe)
 		/* xs is uninitialized before xhci_init_slot */
 		return;
 
-	DPRINTFN(4, "slot %u dci %u", xs->xs_idx, dci, 0, 0);
+	DPRINTFN(4, "pipe %p slot %u dci %u", pipe, xs->xs_idx, dci, 0);
 
 	KASSERTMSG(!cpu_intr_p() && !cpu_softintr_p(), "called from intr ctx");
 	KASSERT(mutex_owned(&sc->sc_lock));
@@ -1540,11 +1546,8 @@ xhci_close_pipe(struct usbd_pipe *pipe)
 		return;
 	}
 
-	/*
-	 * This may fail in the case that xhci_close_pipe is called after
-	 * xhci_abort_xfer e.g. usbd_kill_pipe.
-	 */
-	(void)xhci_stop_endpoint(pipe);
+	if (xhci_get_epstate(sc, xs, dci) != XHCI_EPSTATE_STOPPED)
+		(void)xhci_stop_endpoint(pipe, 1);
 
 	/*
 	 * set appropriate bit to be dropped.
@@ -1567,7 +1570,7 @@ xhci_close_pipe(struct usbd_pipe *pipe)
 	trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP);
 
-	(void)xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	(void)xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, 1);
 	usb_syncmem(&xs->xs_dc_dma, 0, sc->sc_pgsz, BUS_DMASYNC_POSTREAD);
 }
 
@@ -1580,10 +1583,12 @@ static void
 xhci_abort_xfer(struct usbd_xfer *xfer, usbd_status status)
 {
 	struct xhci_softc * const sc = xfer->ux_pipe->up_dev->ud_bus->ub_hcpriv;
+	struct xhci_slot * const xs = xfer->ux_pipe->up_dev->ud_hcpriv;
+	const u_int dci = xhci_ep_get_dci(xfer->ux_pipe->up_endpoint->ue_edesc);
 
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
-	DPRINTFN(4, "xfer %p pipe %p status %d",
-	    xfer, xfer->ux_pipe, status, 0);
+	DPRINTFN(4, "xfer %p status %u slot %u dci %u",
+	    xfer, status, xs->xs_idx, dci);
 
 	KASSERT(mutex_owned(&sc->sc_lock));
 
@@ -1596,10 +1601,77 @@ xhci_abort_xfer(struct usbd_xfer *xfer, 
 		return;
 	}
 
-	/* XXX need more stuff */
-	xfer->ux_status = status;
+	/*
+	 * If an abort is already in progress then just wait for it to
+	 * complete and return.
+	 */
+	if (xfer->ux_hcflags & UXFER_ABORTING) {
+		DPRINTFN(4, "already aborting", 0, 0, 0, 0);
+#ifdef DIAGNOSTIC
+		if (status == USBD_TIMEOUT)
+			DPRINTFN(4, "TIMEOUT while aborting", 0, 0, 0, 0);
+#endif
+		/* Override the status which might be USBD_TIMEOUT. */
+		xfer->ux_status = status;
+		DPRINTFN(4, "waiting for abort to finish", 0, 0, 0, 0);
+		xfer->ux_hcflags |= UXFER_ABORTWAIT;
+		while (xfer->ux_hcflags & UXFER_ABORTING)
+			cv_wait(&xfer->ux_hccv, &sc->sc_lock);
+		return;
+	}
+	xfer->ux_hcflags |= UXFER_ABORTING;
+
+	/*
+	 * Step 1: Make interrupt routine and hardware ignore xfer.
+	 */
+	xfer->ux_status = status;  /* make software ignore it */
 	callout_stop(&xfer->ux_callout);
-	usb_transfer_complete(xfer);
+
+	/* Stop execution of TDs, so we can manipulate TDs on ring */
+	switch (xhci_get_epstate(sc, xs, dci)) {
+	case XHCI_EPSTATE_HALTED:
+		(void)xhci_reset_endpoint(xfer->ux_pipe, 1);
+		break;
+	case XHCI_EPSTATE_STOPPED:
+		break;
+	default:
+		(void)xhci_stop_endpoint(xfer->ux_pipe, 1);
+		break;
+	}
+
+	/*
+	 * Step 2: Wait until we know hardware has finished any possible
+	 * use of the xfer.  Also make sure the soft interrupt routine
+	 * has run.
+	 */
+	sc->sc_softwake = 1;
+	usb_schedsoftintr(&sc->sc_bus);
+	cv_wait(&sc->sc_softwake_cv, &sc->sc_lock);
+
+	/*
+	 * Step 3: Remove any vestiges of the xfer from the hardware.
+	 * The complication here is that the hardware may have executed
+	 * beyond the xfer we're trying to abort.  So as we're scanning
+	 * the TDs of this xfer we check if the hardware points to
+	 * any of them.
+	 */
+	xhci_set_dequeue(xfer->ux_pipe, 1);
+
+	/*
+	 * Step 4: Execute callback.
+	 */
+	int wake = xfer->ux_hcflags & UXFER_ABORTWAIT;
+	xfer->ux_hcflags &= ~(UXFER_ABORTING | UXFER_ABORTWAIT);
+	if (xfer != NULL &&
+	    xfer->ux_pipe != NULL &&
+	    xfer->ux_state == XFER_ONQU &&
+	    !(xfer->ux_pipe->up_repeat ||
+	    SIMPLEQ_EMPTY(&xfer->ux_pipe->up_queue))) {
+		usb_transfer_complete(xfer);
+	}
+	if (wake) {
+		cv_broadcast(&xfer->ux_hccv);
+	}
 
 	KASSERT(mutex_owned(&sc->sc_lock));
 }
@@ -1624,8 +1696,8 @@ xhci_clear_endpoint_stall_async_task(voi
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 	DPRINTFN(4, "xfer %p slot %u dci %u", xfer, xs->xs_idx, dci, 0);
 
-	xhci_reset_endpoint(xfer->ux_pipe);
-	xhci_set_dequeue(xfer->ux_pipe);
+	xhci_reset_endpoint(xfer->ux_pipe, 0);
+	xhci_set_dequeue(xfer->ux_pipe, 0);
 
 	mutex_enter(&sc->sc_lock);
 	tr->is_halted = false;
@@ -1905,6 +1977,13 @@ xhci_softintr(void *v)
 	xhci_rt_write_8(sc, XHCI_ERDP(0), xhci_ring_trbp(er, er->xr_ep) |
 	    XHCI_ERDP_LO_BUSY);
 
+	/* notify abort_xfer that softint has been done */
+	if (sc->sc_softwake) {
+		DPRINTFN(4, "cv_broadcast on softwake", 0, 0, 0, 0);
+		sc->sc_softwake = 0;
+		cv_broadcast(&sc->sc_softwake_cv);
+	}
+
 	DPRINTFN(16, "ends", 0, 0, 0, 0);
 
 	return;
@@ -2341,6 +2420,32 @@ xhci_ring_put(struct xhci_softc * const 
 	DPRINTFN(12, "%p xr_ep 0x%x xr_cs %u", xr, xr->xr_ep, xr->xr_cs, 0);
 }
 
+static void
+xhci_abort_command(struct xhci_softc *sc)
+{
+	uint64_t crcr;
+	int i;
+
+	XHCIHIST_FUNC(); XHCIHIST_CALLED();
+
+	/* 4.6.1.2 Aborting a Command */
+	DPRINTFN(14, "command %p timeout, aborting",
+	    sc->sc_command_addr, 0, 0, 0);
+	crcr = xhci_op_read_8(sc, XHCI_CRCR);
+	xhci_op_write_8(sc, XHCI_CRCR, crcr | XHCI_CRCR_LO_CA);
+
+	for (i = 0; i < 500; i++) {
+		crcr = xhci_op_read_8(sc, XHCI_CRCR);
+		if ((crcr & XHCI_CRCR_LO_CRR) == 0)
+			break;
+		usb_delay_ms(&sc->sc_bus, 1);
+	}
+	if ((crcr & XHCI_CRCR_LO_CRR) != 0) {
+		DPRINTFN(1, "Command Abort timeout", 0, 0, 0, 0);
+		/* reset HC here? */
+	}
+}
+
 /*
  * Put a command on command ring, ring bell, set timer, and cv_timedwait.
  * Command completion is notified by cv_signal from xhci_handle_event
@@ -2349,7 +2454,7 @@ xhci_ring_put(struct xhci_softc * const 
  * trb_0 in CMD_COMPLETE TRB and sc->sc_command_addr are identical.
  */
 static usbd_status
-xhci_do_command1(struct xhci_softc * const sc, struct xhci_trb * const trb,
+xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb,
     int timeout, int locked)
 {
 	struct xhci_ring * const cr = &sc->sc_cr;
@@ -2376,6 +2481,7 @@ xhci_do_command1(struct xhci_softc * con
 
 	if (cv_timedwait(&sc->sc_command_cv, &sc->sc_lock,
 	    MAX(1, mstohz(timeout))) == EWOULDBLOCK) {
+		xhci_abort_command(sc);
 		err = USBD_TIMEOUT;
 		goto timedout;
 	}
@@ -2408,25 +2514,6 @@ timedout:
 }
 
 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);
-}
-
-/*
- * This allows xhci_do_command with already sc_lock held.
- * This is needed as USB stack calls close methods with sc_lock_held.
- * (see usbdivar.h)
- */
-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;
@@ -2438,7 +2525,7 @@ xhci_enable_slot(struct xhci_softc * con
 	trb.trb_2 = 0;
 	trb.trb_3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ENABLE_SLOT);
 
-	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, 0);
 	if (err != USBD_NORMAL_COMPLETION) {
 		return err;
 	}
@@ -2479,7 +2566,7 @@ xhci_disable_slot(struct xhci_softc * co
 		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);
+	return xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, 1);
 }
 
 /*
@@ -2503,7 +2590,7 @@ xhci_address_device(struct xhci_softc * 
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ADDRESS_DEVICE) |
 	    (bsr ? XHCI_TRB_3_BSR_BIT : 0);
 
-	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, 0);
 	return err;
 }
 
@@ -2535,7 +2622,7 @@ xhci_update_ep0_mps(struct xhci_softc * 
 	trb.trb_3 = XHCI_TRB_3_SLOT_SET(xs->xs_idx) |
 	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVALUATE_CTX);
 
-	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT);
+	err = xhci_do_command(sc, &trb, USBD_DEFAULT_TIMEOUT, 0);
 	KASSERT(err == USBD_NORMAL_COMPLETION); /* XXX */
 	return err;
 }
@@ -3027,9 +3114,9 @@ xhci_device_ctrl_start(struct usbd_xfer 
 	if (tr->is_halted) {
 		DPRINTFN(1, "ctrl xfer %p halted: slot %u dci %u",
 		    xfer, xs->xs_idx, dci, 0);
-		xhci_reset_endpoint(xfer->ux_pipe);
+		xhci_reset_endpoint(xfer->ux_pipe, 0);
 		tr->is_halted = false;
-		xhci_set_dequeue(xfer->ux_pipe);
+		xhci_set_dequeue(xfer->ux_pipe, 0);
 	}
 
 	/* we rely on the bottom bits for extra info */
@@ -3414,11 +3501,6 @@ xhci_timeout_task(void *addr)
 	XHCIHIST_FUNC(); XHCIHIST_CALLED();
 
 	mutex_enter(&sc->sc_lock);
-#if 0
 	xhci_abort_xfer(xfer, USBD_TIMEOUT);
-#else
-	xfer->ux_status = USBD_TIMEOUT;
-	usb_transfer_complete(xfer);
-#endif
 	mutex_exit(&sc->sc_lock);
 }
--- src/sys/dev/usb/xhcivar.h.orig	2015-04-07 17:55:43.000000000 +0900
+++ src/sys/dev/usb/xhcivar.h	2015-06-02 10:11:37.000000000 +0900
@@ -75,6 +75,7 @@ struct xhci_softc {
 	kmutex_t sc_lock;
 	kmutex_t sc_intr_lock;
 	kcondvar_t sc_softwake_cv;
+	char sc_softwake;
 
 	struct usbd_xfer *sc_intrxfer;
 

--- src/sys/dev/usb/xhci.c.orig	2015-06-14 20:20:33.000000000 +0900
+++ src/sys/dev/usb/xhci.c	2015-06-14 20:29:13.000000000 +0900
@@ -1407,7 +1407,8 @@ xhci_stop_endpoint(struct usbd_pipe *pip
  * Set TR Dequeue Pointer.
  * xCHI 1.1  4.6.10  6.4.3.9
  * Purge all of the transfer requests on ring.
- * EPSTATE of endpoint must be ERROR or STOPPED, or CONTEXT_STATE error.
+ * EPSTATE of endpoint must be ERROR or STOPPED, otherwise CONTEXT_STATE
+ * error will be generated.
  */
 static usbd_status
 xhci_set_dequeue(struct usbd_pipe *pipe, int locked)
@@ -1818,7 +1819,10 @@ xhci_handle_event(struct xhci_softc * co
 		} else {
 			xx = (void *)(uintptr_t)(trb_0 & ~0x3);
 		}
-		/* XXX this may not happen */
+		/*
+		 * stop_endpoint may cause ERR_STOPPED_LENGTH_INVALID,
+		 * in which case this condition may happen.
+		 */
 		if (xx == NULL) {
 			DPRINTFN(1, "xfer done: xx is NULL", 0, 0, 0, 0);
 			break;
@@ -2225,6 +2229,7 @@ xhci_new_device(device_t parent, struct 
 		KASSERT(bus->ub_devices[dev->ud_addr] == NULL);
 		bus->ub_devices[dev->ud_addr] = dev;
 
+		/* read 64 bytes of device descriptor */
 		err = usbd_get_initial_ddesc(dev, dd);
 		if (err)
 			goto bad;
@@ -2452,6 +2457,10 @@ xhci_abort_command(struct xhci_softc *sc
  * (called from interrupt from xHCI), or timed-out.
  * Command validation is performed in xhci_handle_event by checking if
  * trb_0 in CMD_COMPLETE TRB and sc->sc_command_addr are identical.
+ * locked = 0: called without lock held
+ * locked = 1: allows called with lock held
+ * 'locked' is needed as some methods are called with sc_lock_held.
+ * (see usbdivar.h)
  */
 static usbd_status
 xhci_do_command(struct xhci_softc * const sc, struct xhci_trb * const trb,
@@ -2469,7 +2478,7 @@ xhci_do_command(struct xhci_softc * cons
 	if (!locked)
 		mutex_enter(&sc->sc_lock);
 
-	/* XXX KASSERT may fire when cv_timedwait unlocks sc_lock */
+	/* XXX KASSERT may fail when cv_timedwait unlocks sc_lock */
 	KASSERT(sc->sc_command_addr == 0);
 	sc->sc_command_addr = xhci_ring_trbp(cr, cr->xr_ep);
 

--- src/sys/dev/usb/usb_subr.c.orig	2015-06-07 01:42:33.000000000 +0900
+++ src/sys/dev/usb/usb_subr.c	2015-06-14 02:18:09.000000000 +0900
@@ -601,20 +601,20 @@ usbd_set_config_index(struct usbd_device
 		goto bad;
 	}
 
-	if (USB_IS_SS(dev->ud_speed)) {
+	while (USB_IS_SS(dev->ud_speed)) {
 		int blen;
 
 		/* get short bos desc */
 		err = usbd_get_bos_desc(dev, index, &bd);
 		if (err) {
 			DPRINTF("get_bos_desc=%d", err, 0, 0, 0);
-			goto bad;
+			break;
 		}
 		blen = UGETW(bd.wTotalLength);
 		bdp = kmem_alloc(blen, KM_SLEEP);
 		if (bdp == NULL) {
 			err = USBD_NOMEM;
-			goto bad;
+			break;
 		}
 
 		/* Get the full desc */
@@ -626,15 +626,23 @@ usbd_set_config_index(struct usbd_device
 		}
 		if (err) {
 			DPRINTF("get_bos_desc=%d", err, 0, 0, 0);
-			goto bad;
+			break;
 		}
-		if (bdp->bDescriptorType != UDESC_BOS) {
+		if (bdp->bDescriptorType != UDESC_BOS ||
+		    blen != UGETW(bdp->wTotalLength)) {
 			DPRINTF("bad desc %d", bdp->bDescriptorType, 0, 0, 0);
 			err = USBD_INVAL;
-			goto bad;
+			break;
 		}
+		break;
 	}
-	dev->ud_bdesc = bdp;
+	if (err) {
+		if (bdp != NULL) {
+			kmem_free(bdp, UGETW(bdp->wTotalLength));
+			bdp = NULL;
+		}
+	} else
+		dev->ud_bdesc = bdp;
 
 	/*
 	 * Figure out if the device is self or bus powered.



Home | Main Index | Thread Index | Old Index