Re: xhci data toggle out of sync

From: Hans Petter Selasky <hps_at_selasky.org>
Date: Tue, 19 Apr 2022 11:45:24 UTC
On 4/19/22 13:36, Mahesh Vardhamanaiah wrote:
> Hi HPS,
> 
> Please find the diff below for the error prints.
> 
> 
> [maheshmv@svl-bsdx-02 /b/maheshmv/usb_issue/src]$ git diff sys/dev/usb/controller/xhci.c
> diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c
> index 70a73dcc94c..fa7c1062ada 100644
> --- a/sys/dev/usb/controller/xhci.c
> +++ b/sys/dev/usb/controller/xhci.c
> @@ -3895,6 +3895,7 @@ xhci_configure_reset_endpoint(struct usb_xfer *xfer)
>           * Get the endpoint into the stopped state according to the
>           * endpoint context state diagram in the XHCI specification:
>           */
> +#if 0
>          switch (xhci_get_endpoint_state(udev, epno)) {
>          case XHCI_EPCTX_0_EPSTATE_DISABLED:
>                   break;
> @@ -3909,8 +3910,25 @@ xhci_configure_reset_endpoint(struct usb_xfer *xfer)
>                  err = xhci_cmd_stop_ep(sc, 0, epno, index);
>                  if (err != 0)
>                          DPRINTF("Could not stop endpoint %u\n", epno);
> +               /*
> +                * Need to reset the data toggle, because stop
> +                * endpoint doesn't do that:
> +                */
> +               err = xhci_cmd_reset_ep(sc, 0, epno, index);
> +               if (err != 0)
> +                       DPRINTF("Mahesh Could not reset endpoint %u\n", epno);
>                  break;
>          }
> +#endif
> +
> +       device_printf(sc->sc_bus.parent, "MMV endpoint %u state %x\n", epno, xhci_get_endpoint_state(udev, epno));
> +       err = xhci_cmd_stop_ep(sc, 0, epno, index);
> +       if (err !=0)
> +               device_printf(sc->sc_bus.parent, "MMV Could not stop endpoint %u err %x\n", epno, err);
> +
> +       err = xhci_cmd_reset_ep(sc, 0, epno, index);
> +       if (err !=0)
> +               device_printf(sc->sc_bus.parent, "MMV Could not reset endpoint %u err %x\n", epno, err);
> 
>          err = xhci_cmd_set_tr_dequeue_ptr(sc,
>              (pepext->physaddr + (stream_id * sizeof(struct xhci_trb) *
> 
> 

Hi,

Can you check on your side where the USB_ERR_NO_PIPE comes from? I 
cannot find it?

xhci_cmd_reset_ep() looks like this:

> static usb_error_t
> xhci_cmd_reset_ep(struct xhci_softc *sc, uint8_t preserve,
>     uint8_t ep_id, uint8_t slot_id)
> {
> 	struct xhci_trb trb;
> 	uint32_t temp;
> 
> 	DPRINTF("\n");
> 
> 	trb.qwTrb0 = 0;
> 	trb.dwTrb2 = 0;
> 	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP) |
> 	    XHCI_TRB_3_SLOT_SET(slot_id) |
> 	    XHCI_TRB_3_EP_SET(ep_id);
> 
> 	if (preserve)
> 		temp |= XHCI_TRB_3_PRSV_BIT;
> 
> 	trb.dwTrb3 = htole32(temp);
> 
> 	return (xhci_do_command(sc, &trb, 100 /* ms */));
> }

> static usb_error_t
> xhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb, 
>     uint16_t timeout_ms)
> {
> 	struct usb_page_search buf_res;
> 	struct xhci_hw_root *phwr;
> 	uint64_t addr;
> 	uint32_t temp;
> 	uint8_t i;
> 	uint8_t j;
> 	uint8_t timeout = 0;
> 	int err;
> 
> 	XHCI_CMD_ASSERT_LOCKED(sc);
> 
> 	/* get hardware root structure */
> 
> 	usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
> 
> 	phwr = buf_res.buffer;
> 
> 	/* Queue command */
> 
> 	USB_BUS_LOCK(&sc->sc_bus);
> retry:
> 	i = sc->sc_command_idx;
> 	j = sc->sc_command_ccs;
> 
> 	DPRINTFN(10, "command[%u] = %u (0x%016llx, 0x%08lx, 0x%08lx)\n",
> 	    i, XHCI_TRB_3_TYPE_GET(le32toh(trb->dwTrb3)),
> 	    (long long)le64toh(trb->qwTrb0),
> 	    (long)le32toh(trb->dwTrb2),
> 	    (long)le32toh(trb->dwTrb3));
> 
> 	phwr->hwr_commands[i].qwTrb0 = trb->qwTrb0;
> 	phwr->hwr_commands[i].dwTrb2 = trb->dwTrb2;
> 
> 	usb_pc_cpu_flush(&sc->sc_hw.root_pc);
> 
> 	temp = trb->dwTrb3;
> 
> 	if (j)
> 		temp |= htole32(XHCI_TRB_3_CYCLE_BIT);
> 	else
> 		temp &= ~htole32(XHCI_TRB_3_CYCLE_BIT);
> 
> 	temp &= ~htole32(XHCI_TRB_3_TC_BIT);
> 
> 	phwr->hwr_commands[i].dwTrb3 = temp;
> 
> 	usb_pc_cpu_flush(&sc->sc_hw.root_pc);
> 
> 	addr = buf_res.physaddr;
> 	addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[i];
> 
> 	sc->sc_cmd_addr = htole64(addr);
> 
> 	i++;
> 
> 	if (i == (XHCI_MAX_COMMANDS - 1)) {
> 
> 		if (j) {
> 			temp = htole32(XHCI_TRB_3_TC_BIT |
> 			    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
> 			    XHCI_TRB_3_CYCLE_BIT);
> 		} else {
> 			temp = htole32(XHCI_TRB_3_TC_BIT |
> 			    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
> 		}
> 
> 		phwr->hwr_commands[i].dwTrb3 = temp;
> 
> 		usb_pc_cpu_flush(&sc->sc_hw.root_pc);
> 
> 		i = 0;
> 		j ^= 1;
> 	}
> 
> 	sc->sc_command_idx = i;
> 	sc->sc_command_ccs = j;
> 
> 	XWRITE4(sc, door, XHCI_DOORBELL(0), 0);
> 
> 	err = cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx,
> 	    USB_MS_TO_TICKS(timeout_ms));
> 
> 	/*
> 	 * In some error cases event interrupts are not generated.
> 	 * Poll one time to see if the command has completed.
> 	 */
> 	if (err != 0 && xhci_interrupt_poll(sc) != 0) {
> 		DPRINTF("Command was completed when polling\n");
> 		err = 0;
> 	}
> 	if (err != 0) {
> 		DPRINTF("Command timeout!\n");
> 		/*
> 		 * After some weeks of continuous operation, it has
> 		 * been observed that the ASMedia Technology, ASM1042
> 		 * SuperSpeed USB Host Controller can suddenly stop
> 		 * accepting commands via the command queue. Try to
> 		 * first reset the command queue. If that fails do a
> 		 * host controller reset.
> 		 */
> 		if (timeout == 0 &&
> 		    xhci_reset_command_queue_locked(sc) == 0) {
> 			temp = le32toh(trb->dwTrb3);
> 
> 			/*
> 			 * Avoid infinite XHCI reset loops if the set
> 			 * address command fails to respond due to a
> 			 * non-enumerating device:
> 			 */
> 			if (XHCI_TRB_3_TYPE_GET(temp) == XHCI_TRB_TYPE_ADDRESS_DEVICE &&
> 			    (temp & XHCI_TRB_3_BSR_BIT) == 0) {
> 				DPRINTF("Set address timeout\n");
> 			} else {
> 				timeout = 1;
> 				goto retry;
> 			}
> 		} else {
> 			DPRINTF("Controller reset!\n");
> 			usb_bus_reset_async_locked(&sc->sc_bus);
> 		}
> 		err = USB_ERR_TIMEOUT;
> 		trb->dwTrb2 = 0;
> 		trb->dwTrb3 = 0;
> 	} else {
> 		temp = le32toh(sc->sc_cmd_result[0]);
> 		if (XHCI_TRB_2_ERROR_GET(temp) != XHCI_TRB_ERROR_SUCCESS)
> 			err = USB_ERR_IOERROR;
> 
> 		trb->dwTrb2 = sc->sc_cmd_result[0];
> 		trb->dwTrb3 = sc->sc_cmd_result[1];
> 	}
> 
> 	USB_BUS_UNLOCK(&sc->sc_bus);
> 
> 	return (err);
> }

--HPS