git: ff46acfd521e - main - usb: implement attach kernel driver feature
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 11 Jun 2026 15:08:40 UTC
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]);