Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 11 Jun 2026 15:08:40 +0000
From:      ShengYi Hung <aokblast@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: ff46acfd521e - main - usb: implement attach kernel driver feature
Message-ID:  <6a2acf78.27715.6ce6a992@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by aokblast:

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

commit ff46acfd521e7b7b3761a395c8fa0a929e49e5d7
Author:     ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2025-08-22 14:24:19 +0000
Commit:     ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-06-11 15:08:30 +0000

    usb: implement attach kernel driver feature
    
    FreeBSD's USB framework supports detaching kernel drivers to allow
    user space applications to exclusively claim USB interfaces. However,
    it lacked support for reattaching the kernel driver afterward.
    
    This commit adds the missing functionality, enabling user space
    to return control of the device back to the kernel.
    
    Reviewed by:    lwhsu
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D52122
---
 lib/libusb/Makefile            |  1 +
 lib/libusb/libusb.3            |  7 +++++--
 lib/libusb/libusb.h            |  4 ++--
 lib/libusb/libusb01.c          | 18 ++++++++++++++++++
 lib/libusb/libusb10.c          |  8 ++++++--
 lib/libusb/libusb20.3          | 11 +++++++++++
 lib/libusb/libusb20.c          | 11 +++++++++++
 lib/libusb/libusb20.h          |  2 ++
 lib/libusb/libusb20_int.h      |  2 ++
 lib/libusb/libusb20_ugen20.c   | 12 ++++++++++++
 lib/libusb/usb.h               |  1 +
 sys/dev/usb/usb_generic.c      | 22 ++++++++++++++++++++++
 sys/dev/usb/usb_ioctl.h        |  3 ++-
 usr.sbin/usbconfig/usbconfig.8 |  5 +++++
 usr.sbin/usbconfig/usbconfig.c | 16 ++++++++++++++++
 15 files changed, 116 insertions(+), 7 deletions(-)

diff --git a/lib/libusb/Makefile b/lib/libusb/Makefile
index b3ef5a061584..89cf600236cc 100644
--- a/lib/libusb/Makefile
+++ b/lib/libusb/Makefile
@@ -225,6 +225,7 @@ MLINKS += libusb20.3 libusb20_dev_get_desc.3
 MLINKS += libusb20.3 libusb20_dev_get_stats.3
 MLINKS += libusb20.3 libusb20_dev_close.3
 MLINKS += libusb20.3 libusb20_dev_detach_kernel_driver.3
+MLINKS += libusb20.3 libusb20_dev_attach_kernel_driver.3
 MLINKS += libusb20.3 libusb20_dev_set_config_index.3
 MLINKS += libusb20.3 libusb20_dev_get_debug.3
 MLINKS += libusb20.3 libusb20_dev_get_fd.3
diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3
index 607a7f645d95..90d9afdb5609 100644
--- a/lib/libusb/libusb.3
+++ b/lib/libusb/libusb.3
@@ -95,8 +95,10 @@ supports hotplug notifications.
 can access HID devices without requiring user intervention.
 .It Va LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER
 .Nm
-supports detaching of the default USB driver with
-.Fn libusb_detach_kernel_driver .
+supports detaching and attaching of the default USB driver with
+.Fn libusb_detach_kernel_driver
+and
+.Fn libusb_attach_kernel_driver .
 .El
 .Pp
 .Ft const char *
@@ -869,6 +871,7 @@ The library is also compliant with LibUSB version 0.1.12.
 .Fn usb_check_connected
 .Fn usb_get_driver_np
 .Fn usb_detach_kernel_driver_np
+.Fn usb_attach_kernel_driver_np
 .Sh SEE ALSO
 .Xr libusb20 3 ,
 .Xr usb 4 ,
diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h
index 46a2ed5eb8a6..fd99a67216aa 100644
--- a/lib/libusb/libusb.h
+++ b/lib/libusb/libusb.h
@@ -209,8 +209,8 @@ enum libusb_capability {
 	LIBUSB_CAP_HAS_HID_ACCESS,
 
 	/*
-	 * Supports detaching of the default USB driver with
-	 * libusb_detach_kernel_driver().
+	 * Supports detaching and attaching of the default USB driver with
+	 * libusb_detach_kernel_driver() and libusb_attach_kernel_driver().
 	 */
 	LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
 };
diff --git a/lib/libusb/libusb01.c b/lib/libusb/libusb01.c
index f246e74353a6..84367765815a 100644
--- a/lib/libusb/libusb01.c
+++ b/lib/libusb/libusb01.c
@@ -1023,3 +1023,21 @@ usb_detach_kernel_driver_np(usb_dev_handle * dev, int interface)
 
 	return (0);
 }
+
+int
+usb_attach_kernel_driver_np(usb_dev_handle *dev, int interface)
+{
+	struct libusb20_device *pdev;
+	int err;
+
+	pdev = (void *)dev;
+
+	if (pdev == NULL)
+		return (-1);
+
+	err = libusb20_dev_attach_kernel_driver(pdev, interface);
+	if (err != 0)
+		return (-1);
+
+	return (0);
+}
diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c
index 56c9caedab16..289b56842b5d 100644
--- a/lib/libusb/libusb10.c
+++ b/lib/libusb/libusb10.c
@@ -1052,10 +1052,14 @@ libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface)
 int
 libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface)
 {
+	int err;
+
 	if (pdev == NULL)
 		return (LIBUSB_ERROR_INVALID_PARAM);
-	/* stub - currently not supported by libusb20 */
-	return (0);
+
+	err = libusb20_dev_attach_kernel_driver(pdev, interface);
+
+	return (err ? LIBUSB_ERROR_OTHER : 0);
 }
 
 int
diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3
index 7854b0f8ed7e..a5213b1aa187 100644
--- a/lib/libusb/libusb20.3
+++ b/lib/libusb/libusb20.3
@@ -126,6 +126,8 @@ USB access library (libusb -lusb)
 .Ft int
 .Fn libusb20_dev_detach_kernel_driver "struct libusb20_device *pdev" "uint8_t iface_index"
 .Ft int
+.Fn libusb20_dev_attach_kernel_driver "struct libusb20_device *pdev" "uint8_t iface_index"
+.Ft int
 .Fn libusb20_dev_set_config_index "struct libusb20_device *pdev" "uint8_t configIndex"
 .Ft int
 .Fn libusb20_dev_get_debug "struct libusb20_device *pdev"
@@ -623,6 +625,15 @@ returned.
 .
 .Pp
 .
+.Fn libusb20_dev_attach_kernel_driver
+will try to attach the kernel driver for the USB interface given by
+.Fa iface_index .
+.
+This function returns zero on success else a LIBUSB20_ERROR value is
+returned.
+.
+.Pp
+.
 .Fn libusb20_dev_set_config_index
 will try to set the configuration index on an USB
 device.
diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c
index 25c95adc27ff..7b443e236cc2 100644
--- a/lib/libusb/libusb20.c
+++ b/lib/libusb/libusb20.c
@@ -80,6 +80,7 @@ dummy_callback(struct libusb20_transfer *xfer)
 #define	dummy_get_stats (void *)dummy_int
 #define	dummy_kernel_driver_active (void *)dummy_int
 #define	dummy_detach_kernel_driver (void *)dummy_int
+#define	dummy_attach_kernel_driver (void *)dummy_int
 #define	dummy_do_request_sync (void *)dummy_int
 #define	dummy_tr_open (void *)dummy_int
 #define	dummy_tr_close (void *)dummy_int
@@ -636,6 +637,16 @@ libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceInd
 	return (error);
 }
 
+int
+libusb20_dev_attach_kernel_driver(struct libusb20_device *pdev,
+    uint8_t ifaceIndex)
+{
+	int error;
+
+	error = pdev->methods->attach_kernel_driver(pdev, ifaceIndex);
+	return (error);
+}
+
 struct LIBUSB20_DEVICE_DESC_DECODED *
 libusb20_dev_get_device_desc(struct libusb20_device *pdev)
 {
diff --git a/lib/libusb/libusb20.h b/lib/libusb/libusb20.h
index c132c58a9f69..a9bb3ec1647e 100644
--- a/lib/libusb/libusb20.h
+++ b/lib/libusb/libusb20.h
@@ -246,6 +246,8 @@ const char *libusb20_dev_get_backend_name(struct libusb20_device *pdev);
 const char *libusb20_dev_get_desc(struct libusb20_device *pdev);
 int	libusb20_dev_close(struct libusb20_device *pdev);
 int	libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index);
+int	libusb20_dev_attach_kernel_driver(struct libusb20_device *pdev,
+    uint8_t iface_index);
 int	libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex);
 int	libusb20_dev_get_debug(struct libusb20_device *pdev);
 int	libusb20_dev_get_fd(struct libusb20_device *pdev);
diff --git a/lib/libusb/libusb20_int.h b/lib/libusb/libusb20_int.h
index 4b0cef442c36..b9d614ffd8ff 100644
--- a/lib/libusb/libusb20_int.h
+++ b/lib/libusb/libusb20_int.h
@@ -92,6 +92,7 @@ typedef void (libusb20_dummy_void_t)(void);
 
 /* USB device specific */
 typedef int (libusb20_detach_kernel_driver_t)(struct libusb20_device *pdev, uint8_t iface_index);
+typedef int (libusb20_attach_kernel_driver_t)(struct libusb20_device *pdev, uint8_t iface_index);
 typedef int (libusb20_do_request_sync_t)(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags);
 typedef int (libusb20_get_config_desc_full_t)(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t index);
 typedef int (libusb20_get_config_index_t)(struct libusb20_device *pdev, uint8_t *pindex);
@@ -116,6 +117,7 @@ typedef void (libusb20_tr_cancel_async_t)(struct libusb20_transfer *xfer);
 
 #define	LIBUSB20_DEVICE(m,n) \
   m(n, detach_kernel_driver) \
+  m(n, attach_kernel_driver) \
   m(n, do_request_sync) \
   m(n, get_config_desc_full) \
   m(n, get_config_index) \
diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c
index 11064d2644d6..6c838814a45d 100644
--- a/lib/libusb/libusb20_ugen20.c
+++ b/lib/libusb/libusb20_ugen20.c
@@ -82,6 +82,7 @@ static libusb20_get_power_usage_t ugen20_get_power_usage;
 static libusb20_get_stats_t ugen20_get_stats;
 static libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
 static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
+static libusb20_attach_kernel_driver_t ugen20_attach_kernel_driver;
 static libusb20_do_request_sync_t ugen20_do_request_sync;
 static libusb20_process_t ugen20_process;
 
@@ -716,6 +717,17 @@ ugen20_detach_kernel_driver(struct libusb20_device *pdev,
 	return (0);			/* kernel driver is detached */
 }
 
+static int
+ugen20_attach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index)
+{
+	int temp = iface_index;
+
+	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ATTACH), &temp)) {
+		return (LIBUSB20_ERROR_OTHER);
+	}
+	return (0);			/* kernel driver is attached */
+}
+
 static int
 ugen20_do_request_sync(struct libusb20_device *pdev,
     struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
diff --git a/lib/libusb/usb.h b/lib/libusb/usb.h
index ab225060c55e..fd77a9077fa8 100644
--- a/lib/libusb/usb.h
+++ b/lib/libusb/usb.h
@@ -303,6 +303,7 @@ struct usb_device *usb_device(usb_dev_handle * dev);
 struct usb_bus *usb_get_busses(void);
 int	usb_get_driver_np(usb_dev_handle * dev, int interface, char *name, int namelen);
 int	usb_detach_kernel_driver_np(usb_dev_handle * dev, int interface);
+int	usb_attach_kernel_driver_np(usb_dev_handle *dev, int interface);
 
 #if 0
 {					/* style */
diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c
index ccb0b2184ec4..c564585acd5a 100644
--- a/sys/dev/usb/usb_generic.c
+++ b/sys/dev/usb/usb_generic.c
@@ -2376,6 +2376,28 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
 		 */
 		usbd_set_parent_iface(f->udev, n, n);
 		break;
+	case USB_IFACE_DRIVER_ATTACH:
+
+		error = priv_check(curthread, PRIV_DRIVER);
+
+		if (error)
+			break;
+
+		n = *u.pint & 0xFF;
+
+		if (n == USB_IFACE_INDEX_ANY) {
+			error = EINVAL;
+			break;
+		}
+
+		/*
+		 * Attach the currently detached driver.
+		 */
+		usbd_set_parent_iface(f->udev, n, USB_IFACE_INDEX_ANY);
+
+		usb_probe_and_attach(f->udev, n);
+
+		break;
 
 	case USB_SET_POWER_MODE:
 		error = ugen_set_power_mode(f, *u.pint);
diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h
index 85979b9cf778..a57e7bb7cbfc 100644
--- a/sys/dev/usb/usb_ioctl.h
+++ b/sys/dev/usb/usb_ioctl.h
@@ -273,7 +273,8 @@ struct usb_gen_quirk {
 #define	USB_IFACE_DRIVER_DETACH	_IOW ('U', 125, int)
 #define	USB_GET_PLUGTIME	_IOR ('U', 126, uint32_t)
 #define	USB_READ_DIR		_IOW ('U', 127, struct usb_read_dir)
-/* 128 - 133 unused */
+#define	USB_IFACE_DRIVER_ATTACH	_IOW('U', 128, int)
+/* 129 - 133 unused */
 #define	USB_GET_DEV_PORT_PATH	_IOR ('U', 134, struct usb_device_port_path)
 #define	USB_GET_POWER_USAGE	_IOR ('U', 135, int)
 #define	USB_SET_TX_FORCE_SHORT	_IOW ('U', 136, int)
diff --git a/usr.sbin/usbconfig/usbconfig.8 b/usr.sbin/usbconfig/usbconfig.8
index 511fa9c5dddb..37066519a7cf 100644
--- a/usr.sbin/usbconfig/usbconfig.8
+++ b/usr.sbin/usbconfig/usbconfig.8
@@ -159,6 +159,11 @@ Detach kernel driver for the selected interface and USB device.
 This command uses the
 .Fl i Ar interface_index
 option.
+.It Cm attach_kernel_driver
+Attach kernel driver for the selected interface and USB device.
+This command uses the
+.Fl i Ar interface_index
+option.
 .It Cm suspend
 Force the device to suspend.
 .It Cm resume
diff --git a/usr.sbin/usbconfig/usbconfig.c b/usr.sbin/usbconfig/usbconfig.c
index 7cab4a1354b1..96235889c233 100644
--- a/usr.sbin/usbconfig/usbconfig.c
+++ b/usr.sbin/usbconfig/usbconfig.c
@@ -93,6 +93,7 @@ struct options {
 	uint8_t	got_do_request:1;
 	uint8_t	got_detach_kernel_driver:1;
 	uint8_t opt_dump_in_list_mode:1;
+	uint8_t	got_attach_kernel_driver:1;
 };
 
 struct token {
@@ -129,6 +130,7 @@ enum {
 	T_RESET,
 	T_LIST,
 	T_DO_REQUEST,
+	T_ATTACH_KERNEL_DRIVER,
 };
 
 static struct options options;
@@ -143,6 +145,7 @@ static const struct token token[] = {
 	{"add_quirk", T_ADD_QUIRK, 1},
 	{"remove_quirk", T_REMOVE_QUIRK, 1},
 	{"detach_kernel_driver", T_DETACH_KERNEL_DRIVER, 0},
+	{"attach_kernel_driver", T_ATTACH_KERNEL_DRIVER, 0},
 	{"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0},
 	{"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0},
 	{"dump_all_desc", T_DUMP_ALL_DESC, 0},
@@ -283,6 +286,7 @@ usage(int exitcode)
 	    "  add_quirk <quirk>" "\n"
 	    "  remove_quirk <quirk>" "\n"
 	    "  detach_kernel_driver" "\n"
+	    "  attach_kernel_driver" "\n"
 	    "  dump_quirk_names" "\n"
 	    "  dump_device_quirks" "\n"
 	    "  dump_all_desc" "\n"
@@ -496,6 +500,11 @@ flush_command(struct libusb20_backend *pbe, struct options *opt)
 				err(1, "could not detach kernel driver");
 			}
 		}
+		if (opt->got_attach_kernel_driver) {
+			if (libusb20_dev_attach_kernel_driver(pdev, opt->iface)) {
+				err(1, "could not attach kernel driver");
+			}
+		}
 		dump_any =
 		    (opt->got_dump_all_desc ||
 		    opt->got_dump_device_desc ||
@@ -688,6 +697,13 @@ main(int argc, char **argv)
 			opt->got_any++;
 			break;
 
+		case T_ATTACH_KERNEL_DRIVER:
+			if (opt->got_attach_kernel_driver)
+				duplicate_option(argv[n]);
+			opt->got_attach_kernel_driver = 1;
+			opt->got_any++;
+			break;
+
 		case T_DUMP_QUIRK_NAMES:
 			if (opt->got_dump_quirk_names)
 				duplicate_option(argv[n]);


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a2acf78.27715.6ce6a992>