git: c6203b6c00ca - stable/13 - usb(4): Call optional endpoint_uninit() when changing configuration or alternate setting.

From: Hans Petter Selasky <hselasky_at_FreeBSD.org>
Date: Sun, 30 Apr 2023 06:57:49 UTC
The branch stable/13 has been updated by hselasky:

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

commit c6203b6c00ca87a182a8a81d372ade0dc3e1f5f5
Author:     Hans Petter Selasky <hselasky@FreeBSD.org>
AuthorDate: 2022-12-15 22:32:47 +0000
Commit:     Hans Petter Selasky <hselasky@FreeBSD.org>
CommitDate: 2023-04-30 06:56:16 +0000

    usb(4): Call optional endpoint_uninit() when changing configuration or alternate setting.
    
    Sponsored by:   NVIDIA Networking
    
    (cherry picked from commit e4611d26265fb9e3bd2a345cf4776863f49a2587)
---
 sys/dev/usb/usb_device.c | 55 ++++++++++++++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 20 deletions(-)

diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c
index b5e51cbdc7a7..392d969587c0 100644
--- a/sys/dev/usb/usb_device.c
+++ b/sys/dev/usb/usb_device.c
@@ -841,38 +841,53 @@ usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd)
 	DPRINTFN(5, "iface_index=%d cmd=%d\n",
 	    iface_index, cmd);
 
-	if (cmd == USB_CFG_FREE)
-		goto cleanup;
-
-	if (cmd == USB_CFG_INIT) {
+	if (cmd == USB_CFG_INIT || cmd == USB_CFG_FREE) {
 		sx_assert(&udev->enum_sx, SA_LOCKED);
 
 		/* check for in-use endpoints */
 
+		if (cmd == USB_CFG_INIT) {
+			ep = udev->endpoints;
+			ep_max = udev->endpoints_max;
+			while (ep_max--) {
+				/* look for matching endpoints */
+				if (iface_index == USB_IFACE_INDEX_ANY ||
+				    iface_index == ep->iface_index) {
+					if (ep->refcount_alloc != 0)
+						return (USB_ERR_IN_USE);
+				}
+			}
+		}
+
 		ep = udev->endpoints;
 		ep_max = udev->endpoints_max;
 		while (ep_max--) {
 			/* look for matching endpoints */
-			if ((iface_index == USB_IFACE_INDEX_ANY) ||
-			    (iface_index == ep->iface_index)) {
-				if (ep->refcount_alloc != 0) {
-					/*
-					 * This typically indicates a
-					 * more serious error.
-					 */
-					err = USB_ERR_IN_USE;
-				} else {
-					/* reset endpoint */
-					memset(ep, 0, sizeof(*ep));
-					/* make sure we don't zero the endpoint again */
-					ep->iface_index = USB_IFACE_INDEX_ANY;
-				}
+			if (iface_index == USB_IFACE_INDEX_ANY ||
+			    iface_index == ep->iface_index) {
+				/*
+				 * Check if hardware needs a callback
+				 * to unconfigure the endpoint. This
+				 * may happen multiple times,
+				 * because the requested alternate
+				 * setting may fail. The callback
+				 * implementation should be aware of
+				 * and handle that.
+				 */
+				if (ep->edesc != NULL &&
+				    udev->bus->methods->endpoint_uninit != NULL)
+					udev->bus->methods->endpoint_uninit(udev, ep);
+
+				/* reset endpoint */
+				memset(ep, 0, sizeof(*ep));
+				/* make sure we don't zero the endpoint again */
+				ep->iface_index = USB_IFACE_INDEX_ANY;
 			}
 			ep++;
 		}
 
-		if (err)
-			return (err);
+		if (cmd == USB_CFG_FREE)
+			goto cleanup;
 	}
 
 	memset(&ips, 0, sizeof(ips));