git: 0f59df83869d - main - xhci: Only reset the data toggle value when the USB stack asks for it

From: ShengYi Hung <aokblast_at_FreeBSD.org>
Date: Wed, 26 Aug 2026 20:52:13 UTC
The branch main has been updated by aokblast:

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

commit 0f59df83869d3734a33d96b01381823e6a3ef3ff
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2026-08-24 17:51:41 +0000
Commit:     ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-08-26 20:52:11 +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")
    MFC after:      3 days
    Differential Revision:  https://reviews.freebsd.org/D59186
---
 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 c385631f22d7..72a0b704b9b6 100644
--- a/sys/dev/usb/controller/xhci.c
+++ b/sys/dev/usb/controller/xhci.c
@@ -3940,6 +3940,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 */
@@ -3957,14 +3962,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:
 		/*
@@ -3974,9 +3981,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;
 	}
 
@@ -4241,6 +4249,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;
 };