git: f656bde0c2ef - stable/14 - xhci: Only reset the data toggle value when the USB stack asks for it

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Fri, 04 Sep 2026 18:49:30 UTC
The branch stable/14 has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=f656bde0c2efd016f1c5d1f22460d657e73a7eb5

commit f656bde0c2efd016f1c5d1f22460d657e73a7eb5
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2026-08-24 17:51:41 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2026-09-04 17:56:23 +0000

    xhci: Only reset the data toggle value when the USB stack asks for it
    
    The previous patch assumes that we don't want to reset toggle bit in
    STOPPED_STEP. However, a device can explicitly call
    usbd_clear_data_toggle if necessary. As a result, instead of not
    dropping the bit unconditionally, we added a field in xhci to specify
    that we want to drop it, so that usbd_clear_data_toggle can handle it
    correctly.
    
    Reported by:    oh
    Reviewed by:    kevans
    Tested by:      oh
    Fixes:          28d85db46b48 ("xhci: Do not drop and add bits in xhci")
    
    (cherry picked from commit 0f59df83869d3734a33d96b01381823e6a3ef3ff)
---
 sys/dev/usb/controller/xhci.c | 23 ++++++++++++++++++-----
 sys/dev/usb/controller/xhci.h |  1 +
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c
index 8793611fdc81..88b97ed87a67 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -3796,6 +3796,11 @@ xhci_configure_reset_endpoint(struct usb_xfer *xfer)
  	if (epno == 0)
 		return (USB_ERR_NO_PIPE);		/* invalid */
 
+	USB_BUS_LOCK(udev->bus);
+	drop = pepext->trb_toggle_reset;
+	pepext->trb_toggle_reset = 0;
+	USB_BUS_UNLOCK(udev->bus);
+
 	XHCI_CMD_LOCK(sc);
 
 	/* configure endpoint */
@@ -3813,14 +3818,16 @@ xhci_configure_reset_endpoint(struct usb_xfer *xfer)
 	 */
 	switch (xhci_get_endpoint_state(udev, epno)) {
 	case XHCI_EPCTX_0_EPSTATE_DISABLED:
-	case XHCI_EPCTX_0_EPSTATE_STOPPED:
 		drop = 0;
 		break;
+	case XHCI_EPCTX_0_EPSTATE_STOPPED:
+		break;
 	case XHCI_EPCTX_0_EPSTATE_HALTED:
 		err = xhci_cmd_reset_ep(sc, 0, epno, index);
-		drop = (err != 0);
-		if (drop)
+		if (err != 0) {
+			drop = 1;
 			DPRINTF("Could not reset endpoint %u\n", epno);
+		}
 		break;
 	default:
 		/*
@@ -3830,9 +3837,10 @@ xhci_configure_reset_endpoint(struct usb_xfer *xfer)
 		 * result, xHCI may refuse to receive or process the packet.
 		 */
 		err = xhci_cmd_stop_ep(sc, 0, epno, index);
-		drop = (err != 0);
-		if (drop)
+		if (err != 0) {
+			drop = 1;
 			DPRINTF("Could not stop endpoint %u\n", epno);
+		}
 		break;
 	}
 
@@ -4097,6 +4105,11 @@ xhci_ep_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
 	USB_BUS_LOCK(udev->bus);
 	pepext->trb_halted = 1;
 	pepext->trb_running = 0;
+	/*
+	 * The USB stack has cleared its own data toggle value and expects
+	 * the hardware data toggle value to be cleared as well:
+	 */
+	pepext->trb_toggle_reset = 1;
 	USB_BUS_UNLOCK(udev->bus);
 }
 
diff --git a/sys/dev/usb/controller/xhci.h b/sys/dev/usb/controller/xhci.h
index 3758815238ad..c2e6cde8ad77 100644
--- a/sys/dev/usb/controller/xhci.h
+++ b/sys/dev/usb/controller/xhci.h
@@ -419,6 +419,7 @@ struct xhci_endpoint_ext {
 	uint8_t			trb_index[XHCI_MAX_STREAMS];
 	uint8_t			trb_halted;
 	uint8_t			trb_running;
+	uint8_t			trb_toggle_reset;
 	uint8_t			trb_ep_mode;
 	uint8_t			trb_ep_maxp;
 };