From nobody Thu Jun 11 15:08:40 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gbmHs0Plgz6hgg5 for ; Thu, 11 Jun 2026 15:08:41 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gbmHr6Y0dz3ly5 for ; Thu, 11 Jun 2026 15:08:40 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781190520; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=DBbnIiLMe48hRNmYo0tq8LBCr9LAwAC7rbbjUDZw8RI=; b=n6e6rsGioQ1KYL9cNG8G9E7bFvcGxVHbZwLr/ejqGFzJ6GQGvXLLHioXb3WAFmJNwY2B1T Yjk7LiC9dVIhBiaWzVeeEHIZ622JdFM8MP8g5Po9mGDIdKeHc4tUvnquvWEzwHzDXuB3cm aVWxiWxaibhnIKT+DL7BsaiU6oSHGHzJo5n32Od8DYmR6djXXy82tKnKAAdm2afCX9FBXv cvH0Nxr0nufRgyRuLWDyCCEClBGef3+S/JWLhVq8md9rzfXdt7zDSu5QcwS2vNgwTxkZlP tRb0A8s9jSFlmcHdaOmsQfDJPx1+j7eHax+ar8rRCPRfjvpZUwLvPZRd8D2rdQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781190520; a=rsa-sha256; cv=none; b=laiK95IGXKvP4Ei17zxtK37jZuQbPBLdHtkIePAxku4nZZhhbL4L5AiUV7uaWzchMWUeB0 7wdAoIqMUWOSgd12dqikPkcuV+o+yDeZ5ta6upmqVxavNpKkhDMsSEzsCkSc9tURrNk93v xRGps2CpHXdq4GLoyMhR/Y2rRrz/gZettCT0OsmCdy9DEoGCzCu+/QVyXBml8qR/iXG9K5 we7Nl/yvx4wI2MlpgIt3HnZEW8gA8sK4DIKLg5c2kxpgRVEv+b2okAQM3f3EN4ndipGctH Oyna2SazHyiQeLUXTsKCZcS8BvwORjHn8TeYJQG/H67b9RKJbHRzqTU9Lichog== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781190520; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=DBbnIiLMe48hRNmYo0tq8LBCr9LAwAC7rbbjUDZw8RI=; b=v/+tqZ0zMmO6uGCWI/9SMf0b8TzpQunHORaDiOrXsj1IFGPfkl3NVJNhehDrZ1NtWgjZsL VT/J1SO0cpHtnUNJjNz7tbhZ0QqZ/Ldd47S90ScCZnbjQ2bZRVoc4/yAGGscMA8SXKLQkm EBYIujY1yZuqIQSuUMm8xfRB5ORxU2FmDm/s1NYyAs9yoriD5SwgTLCbk4yneVb1fqf01X HV41fpOmru/UP7AcjkRSolvy4rfltlw9ISKUi+XE+kShi6G9YxID6iGcp6uAzSsOOgxT5R DK5/6r2qidokkR7TevEXbcNUzWtf0urQrglQklXx1F+VgiHPTdGOYVWctEtYKg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gbmHr5h9Tzw8Z for ; Thu, 11 Jun 2026 15:08:40 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 27715 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 11 Jun 2026 15:08:40 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: ShengYi Hung Subject: git: ff46acfd521e - main - usb: implement attach kernel driver feature List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: aokblast X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: ff46acfd521e7b7b3761a395c8fa0a929e49e5d7 Auto-Submitted: auto-generated Date: Thu, 11 Jun 2026 15:08:40 +0000 Message-Id: <6a2acf78.27715.6ce6a992@gitrepo.freebsd.org> The branch main has been updated by aokblast: URL: https://cgit.FreeBSD.org/src/commit/?id=ff46acfd521e7b7b3761a395c8fa0a929e49e5d7 commit ff46acfd521e7b7b3761a395c8fa0a929e49e5d7 Author: ShengYi Hung AuthorDate: 2025-08-22 14:24:19 +0000 Commit: ShengYi Hung 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 " "\n" " remove_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]);