git: 240330a85e5f - main - libusb: don't treat EINVAL from USB_FS_COMPLETE as device detach

From: ShengYi Hung <aokblast_at_FreeBSD.org>
Date: Thu, 25 Jun 2026 23:04:41 UTC
The branch main has been updated by aokblast:

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

commit 240330a85e5f7f5d448123902ebee4136ef221fd
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2026-06-25 22:38:17 +0000
Commit:     ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-06-25 22:58:23 +0000

    libusb: don't treat EINVAL from USB_FS_COMPLETE as device detach
    
    ugen20_process() treats any non-EBUSY errno returned by USB_FS_COMPLETE
    as device detach and returns LIBUSB20_ERROR_OTHER. This causes libusb10
    to set device_is_gone and fail all subsequent transfer with
    LIBUSB_ERROR_NO_DEVICE.
    
    However, USB_FS_COMPLETE can also return EINVAL when a completion
    references an endpoint that no longer exists, for example after
    SET_INTERFACE or SET_CONFIG removes and recreates endpoints. This is a
    transient condition and does not indicate device detach.
    
    Treat EINVAL the same as EBUSY and stop draining completions. This
    prevents a guest selecting an isochronous streaming altsetting from
    permanently breaking the passed-through device.
    
    Reviewed by:    bapt
    Event:          Halifax Hackathon 202606
    Location:       Peggy's Cove Rock
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D57542
---
 lib/libusb/libusb20_ugen20.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c
index 6c838814a45d..32c8d53cd611 100644
--- a/lib/libusb/libusb20_ugen20.c
+++ b/lib/libusb/libusb20_ugen20.c
@@ -765,7 +765,16 @@ ugen20_process(struct libusb20_device *pdev)
 	while (1) {
 
 	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
-			if (errno == EBUSY) {
+			if (errno == EBUSY || errno == EINVAL) {
+				/*
+				 * EBUSY: no completion is pending.
+				 * EINVAL: a dequeued completion referenced an
+				 * endpoint that no longer exists, e.g. a stale
+				 * completion left over after a SET_INTERFACE /
+				 * SET_CONFIG tore the endpoints down. This is not
+				 * a device detach, so stop draining rather than
+				 * declaring the device gone.
+				 */
 				break;
 			} else {
 				/* device detached */