aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShengYi Hung <aokblast@FreeBSD.org>2026-06-25 22:38:17 +0000
committerShengYi Hung <aokblast@FreeBSD.org>2026-06-25 22:58:23 +0000
commit240330a85e5f7f5d448123902ebee4136ef221fd (patch)
tree7084fd6588312405dfc8d4b6ed08e4811c3c14f1
parent90ea8e89d9b751e8b5ae90ef3397883b035788e5 (diff)
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
-rw-r--r--lib/libusb/libusb20_ugen20.c11
1 files changed, 10 insertions, 1 deletions
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 */