linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] rust: usb: add initial USB abstractions
@ 2025-08-25 18:18 Daniel Almeida
  2025-08-25 18:18 ` [PATCH 1/2] rust: usb: add basic " Daniel Almeida
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Daniel Almeida @ 2025-08-25 18:18 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman
  Cc: linux-kernel, rust-for-linux, linux-usb, Daniel Almeida

This adds initial support for USB Rust drivers, not only because I see a
widespread use of module_usb_driver() in media (which is a subsystem I
aim to support) but also because I want to learn about USB in general
and this is a nice opportunity to start doing so.

I tried to keep things as consistent with pci.rs and platform.rs as
possible and tested it by manually binding a device (i.e.: my Logitech
mouse) to the sample driver via:

/sys/bus/usb/drivers/rust_driver_usb/new_id

This initial patch is therefore comprised of the same patterns that are
known to work for pci and platform already.

Physically disconnecting the device also worked, i.e.: nothing bad
showed up in dmesg.

Note that I did not touch MAINTAINERS at all. The objective is to
kickstart the discussion of what to do there here in v1.

---
Daniel Almeida (2):
      rust: usb: add basic USB abstractions
      samples: rust: add a USB driver sample

 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/usb.c              |   8 +
 rust/kernel/lib.rs              |   2 +
 rust/kernel/usb.rs              | 457 ++++++++++++++++++++++++++++++++++++++++
 samples/rust/Kconfig            |  11 +
 samples/rust/Makefile           |   1 +
 samples/rust/rust_driver_usb.rs |  47 +++++
 8 files changed, 528 insertions(+)
---
base-commit: 44d454fcffa8b08d6d66df132121c1d387fa85db
change-id: 20250825-b4-usb-dd0fe44fd78b

Best regards,
-- 
Daniel Almeida <daniel.almeida@collabora.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] rust: usb: add basic USB abstractions
  2025-08-25 18:18 [PATCH 0/2] rust: usb: add initial USB abstractions Daniel Almeida
@ 2025-08-25 18:18 ` Daniel Almeida
  2025-08-25 20:49   ` Benno Lossin
  2025-08-25 18:18 ` [PATCH 2/2] samples: rust: add a USB driver sample Daniel Almeida
  2025-08-25 20:32 ` [PATCH 0/2] rust: usb: add initial USB abstractions Greg Kroah-Hartman
  2 siblings, 1 reply; 6+ messages in thread
From: Daniel Almeida @ 2025-08-25 18:18 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman
  Cc: linux-kernel, rust-for-linux, linux-usb, Daniel Almeida

Add basic USB abstractions, consisting of usb::{Device, Interface,
Driver, Adapter, DeviceId} and the module_usb_driver macro. This is the
first step in being able to write USB device drivers, which paves the
way for USB media drivers - for example - among others.

This initial support will then be used by a subsequent sample driver,
which constitutes the only user of the USB abstractions so far.

Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/usb.c              |   8 +
 rust/kernel/lib.rs              |   2 +
 rust/kernel/usb.rs              | 457 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 469 insertions(+)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 69a975da829f0c35760f71a1b32b8fcb12c8a8dc..645afe578668097ae04455d9eefb102d1f1ce4af 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -73,6 +73,7 @@
 #include <linux/security.h>
 #include <linux/slab.h>
 #include <linux/tracepoint.h>
+#include <linux/usb.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/xarray.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 44b2005d50140d34a44ae37d01c2ddbae6aeaa32..da1ee0d3705739e789c7ad21b957bbdb7ca23521 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -48,6 +48,7 @@
 #include "task.c"
 #include "time.c"
 #include "uaccess.c"
+#include "usb.c"
 #include "vmalloc.c"
 #include "wait.c"
 #include "workqueue.c"
diff --git a/rust/helpers/usb.c b/rust/helpers/usb.c
new file mode 100644
index 0000000000000000000000000000000000000000..fb2aad0cbf4d26ac7fb1a3f176ee7a1d30800f92
--- /dev/null
+++ b/rust/helpers/usb.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/usb.h>
+
+struct usb_device *rust_helper_interface_to_usbdev(struct usb_interface *intf)
+{
+	return interface_to_usbdev(intf);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f8db761c5c95fc66e4c55f539b17fca613161ada..b15277a02028aa1d27480d0630f9f599cacd6e4d 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -127,6 +127,8 @@
 pub mod tracepoint;
 pub mod transmute;
 pub mod types;
+#[cfg(CONFIG_USB)]
+pub mod usb;
 pub mod uaccess;
 pub mod workqueue;
 pub mod xarray;
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8899e7520b58d4e4b08927d54c8912650b78da33
--- /dev/null
+++ b/rust/kernel/usb.rs
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (C) 2025 Collabora Ltd.
+
+//! Abstractions for the USB bus.
+//!
+//! C header: [`include/linux/usb.h`](srctree/include/linux/usb.h)
+
+use crate::{
+    bindings, device,
+    device_id::{RawDeviceId, RawDeviceIdIndex},
+    driver,
+    error::{from_result, to_result, Result},
+    prelude::*,
+    str::CStr,
+    types::{AlwaysRefCounted, Opaque},
+    ThisModule,
+};
+use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
+
+/// An adapter for the registration of USB drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY: A call to `unregister` for a given instance of `RegType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+    type RegType = bindings::usb_driver;
+
+    unsafe fn register(
+        udrv: &Opaque<Self::RegType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        // SAFETY: It's safe to set the fields of `struct usb_driver` on initialization.
+        unsafe {
+            (*udrv.get()).name = name.as_char_ptr();
+            (*udrv.get()).probe = Some(Self::probe_callback);
+            (*udrv.get()).disconnect = Some(Self::disconnect_callback);
+            (*udrv.get()).id_table = T::ID_TABLE.as_ptr();
+        }
+
+        // SAFETY: `udrv` is guaranteed to be a valid `RegType`.
+        to_result(unsafe {
+            bindings::usb_register_driver(udrv.get(), module.0, name.as_char_ptr())
+        })
+    }
+
+    unsafe fn unregister(udrv: &Opaque<Self::RegType>) {
+        // SAFETY: `udrv` is guaranteed to be a valid `RegType`.
+        unsafe { bindings::usb_deregister(udrv.get()) };
+    }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+    extern "C" fn probe_callback(
+        intf: *mut bindings::usb_interface,
+        id: *const bindings::usb_device_id,
+    ) -> kernel::ffi::c_int {
+        // SAFETY: The USB core only ever calls the probe callback with a valid pointer to a
+        // `struct usb_interface` and `struct usb_device_id`.
+        //
+        // INVARIANT: `intf` is valid for the duration of `probe_callback()`.
+        let intf = unsafe { &*intf.cast::<Interface<device::CoreInternal>>() };
+
+        from_result(|| {
+            // SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `struct usb_device_id` and
+            // does not add additional invariants, so it's safe to transmute.
+            let id = unsafe { &*id.cast::<DeviceId>() };
+
+            let info = T::ID_TABLE.info(id.index());
+            let data = T::probe(intf, id, info)?;
+
+            let dev: &device::Device<device::CoreInternal> = intf.as_ref();
+            dev.set_drvdata(data);
+            Ok(0)
+        })
+    }
+
+    extern "C" fn disconnect_callback(intf: *mut bindings::usb_interface) {
+        // SAFETY: The USB core only ever calls the disconnect callback with a valid pointer to a
+        // `struct usb_interface`.
+        //
+        // INVARIANT: `intf` is valid for the duration of `disconnect_callback()`.
+        let intf = unsafe { &*intf.cast::<Interface<device::CoreInternal>>() };
+
+        let dev: &device::Device<device::CoreInternal> = intf.as_ref();
+
+        // SAFETY: `disconnect_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T>>`.
+        let data = unsafe { dev.drvdata_obtain::<Pin<KBox<T>>>() };
+
+        T::disconnect(intf, data.as_ref());
+    }
+}
+
+/// Abstraction for the USB device ID structure, i.e. [`struct usb_device_id`].
+///
+/// [`struct usb_device_id`]: https://docs.kernel.org/driver-api/basics.html#c.usb_device_id
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::usb_device_id);
+
+impl DeviceId {
+    /// Equivalent to C's `USB_DEVICE` macro.
+    pub const fn from_id(vendor: u16, product: u16) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16,
+            idVendor: vendor,
+            idProduct: product,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_DEVICE_VER` macro.
+    pub const fn from_device_ver(vendor: u16, product: u16, bcd_lo: u16, bcd_hi: u16) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION as u16,
+            idVendor: vendor,
+            idProduct: product,
+            bcdDevice_lo: bcd_lo,
+            bcdDevice_hi: bcd_hi,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_DEVICE_INFO` macro.
+    pub const fn from_device_info(class: u8, subclass: u8, protocol: u8) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: bindings::USB_DEVICE_ID_MATCH_DEV_INFO as u16,
+            bDeviceClass: class,
+            bDeviceSubClass: subclass,
+            bDeviceProtocol: protocol,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_INTERFACE_INFO` macro.
+    pub const fn from_interface_info(class: u8, subclass: u8, protocol: u8) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: bindings::USB_DEVICE_ID_MATCH_INT_INFO as u16,
+            bInterfaceClass: class,
+            bInterfaceSubClass: subclass,
+            bInterfaceProtocol: protocol,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_DEVICE_INTERFACE_CLASS` macro.
+    pub const fn from_device_interface_class(vendor: u16, product: u16, class: u8) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: (bindings::USB_DEVICE_ID_MATCH_DEVICE
+                | bindings::USB_DEVICE_ID_MATCH_INT_CLASS) as u16,
+            idVendor: vendor,
+            idProduct: product,
+            bInterfaceClass: class,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_DEVICE_INTERFACE_PROTOCOL` macro.
+    pub const fn from_device_interface_protocol(vendor: u16, product: u16, protocol: u8) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: (bindings::USB_DEVICE_ID_MATCH_DEVICE
+                | bindings::USB_DEVICE_ID_MATCH_INT_PROTOCOL) as u16,
+            idVendor: vendor,
+            idProduct: product,
+            bInterfaceProtocol: protocol,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_DEVICE_INTERFACE_NUMBER` macro.
+    pub const fn from_device_interface_number(vendor: u16, product: u16, number: u8) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: (bindings::USB_DEVICE_ID_MATCH_DEVICE
+                | bindings::USB_DEVICE_ID_MATCH_INT_NUMBER) as u16,
+            idVendor: vendor,
+            idProduct: product,
+            bInterfaceNumber: number,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Equivalent to C's `USB_DEVICE_AND_INTERFACE_INFO` macro.
+    pub const fn from_device_and_interface_info(
+        vendor: u16,
+        product: u16,
+        class: u8,
+        subclass: u8,
+        protocol: u8,
+    ) -> Self {
+        Self(bindings::usb_device_id {
+            match_flags: (bindings::USB_DEVICE_ID_MATCH_INT_INFO
+                | bindings::USB_DEVICE_ID_MATCH_DEVICE) as u16,
+            idVendor: vendor,
+            idProduct: product,
+            bInterfaceClass: class,
+            bInterfaceSubClass: subclass,
+            bInterfaceProtocol: protocol,
+            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+}
+
+// SAFETY: `DeviceId` is a `#[repr(transparent)]` wrapper of `usb_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+unsafe impl RawDeviceId for DeviceId {
+    type RawType = bindings::usb_device_id;
+}
+
+// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_info` field.
+unsafe impl RawDeviceIdIndex for DeviceId {
+    const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::usb_device_id, driver_info);
+
+    fn index(&self) -> usize {
+        self.0.driver_info
+    }
+}
+
+/// [`IdTable`](kernel::device_id::IdTable) type for USB.
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a USB `IdTable` with its alias for modpost.
+#[macro_export]
+macro_rules! usb_device_table {
+    ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+        const $table_name: $crate::device_id::IdArray<
+            $crate::usb::DeviceId,
+            $id_info_type,
+            { $table_data.len() },
+        > = $crate::device_id::IdArray::new($table_data);
+
+        $crate::module_device_table!("usb", $module_table_name, $table_name);
+    };
+}
+
+/// The USB driver trait.
+///
+/// # Examples
+///
+///```
+/// # use kernel::{bindings, device::Core, usb};
+/// use kernel::prelude::*;
+///
+/// struct MyDriver;
+///
+/// kernel::usb_device_table!(
+///     USB_TABLE,
+///     MODULE_USB_TABLE,
+///     <MyDriver as usb::Driver>::IdInfo,
+///     [
+///         (usb::DeviceId::from_id(0x1234, 0x5678), ()),
+///         (usb::DeviceId::from_id(0xabcd, 0xef01), ()),
+///     ]
+/// );
+///
+/// impl usb::Driver for MyDriver {
+///     type IdInfo = ();
+///     const ID_TABLE: usb::IdTable<Self::IdInfo> = &USB_TABLE;
+///
+///     fn probe(
+///         _interface: &usb::Interface<Core>,
+///         _id: &usb::DeviceId,
+///         _info: &Self::IdInfo,
+///     ) -> Result<Pin<KBox<Self>>> {
+///         Err(ENODEV)
+///     }
+///
+///     fn disconnect(_interface: &usb::Interface<Core>, _data: Pin<&Self>) {}
+/// }
+///```
+pub trait Driver {
+    /// The type holding information about each one of the device ids supported by the driver.
+    type IdInfo: 'static;
+
+    /// The table of device ids supported by the driver.
+    const ID_TABLE: IdTable<Self::IdInfo>;
+
+    /// USB driver probe.
+    ///
+    /// Called when a new USB interface is bound to this driver.
+    /// Implementers should attempt to initialize the interface here.
+    fn probe(
+        interface: &Interface<device::Core>,
+        id: &DeviceId,
+        id_info: &Self::IdInfo,
+    ) -> Result<Pin<KBox<Self>>>;
+
+    /// USB driver disconnect.
+    ///
+    /// Called when the USB interface is about to be unbound from this driver.
+    fn disconnect(interface: &Interface<device::Core>, data: Pin<&Self>);
+}
+
+/// A USB interface.
+///
+/// This structure represents the Rust abstraction for a C [`struct usb_interface`].
+/// The implementation abstracts the usage of a C [`struct usb_interface`] passed
+/// in from the C side.
+///
+/// # Invariants
+///
+/// An [`Interface`] instance represents a valid [`struct usb_interface`] created
+/// by the C portion of the kernel.
+///
+/// [`struct usb_interface`]: https://www.kernel.org/doc/html/latest/driver-api/usb/usb.html#c.usb_interface
+#[repr(transparent)]
+pub struct Interface<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::usb_interface>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Interface<Ctx> {
+    fn as_raw(&self) -> *mut bindings::usb_interface {
+        self.0.get()
+    }
+}
+
+// SAFETY: `Interface` is a transparent wrapper of a type that doesn't depend on
+// `Interface`'s generic argument.
+kernel::impl_device_context_deref!(unsafe { Interface });
+kernel::impl_device_context_into_aref!(Interface);
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Interface<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct usb_interface`.
+        let dev = unsafe { &raw mut ((*self.as_raw()).dev) };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<Device<Ctx>> for Interface<Ctx> {
+    fn as_ref(&self) -> &Device<Ctx> {
+        // SAFETY: `self.as_raw()` is valid by the type invariants. For a valid interface,
+        // the helper should always return a valid USB device pointer.
+        let usb_dev = unsafe { bindings::interface_to_usbdev(self.as_raw()) };
+
+        // SAFETY: The helper returns a valid interface pointer that shares the
+        // same `DeviceContext`.
+        unsafe { &*(usb_dev.cast()) }
+    }
+}
+
+// SAFETY: Instances of `Interface` are always reference-counted.
+unsafe impl AlwaysRefCounted for Interface {
+    fn inc_ref(&self) {
+        // SAFETY: The invariants of `Interface` guarantee that `self.as_raw()`
+        // returns a valid `struct usb_interface` pointer, for which we will
+        // acquire a new refcount.
+        unsafe { bindings::usb_get_intf(self.as_raw()) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::usb_put_intf(obj.cast().as_ptr()) }
+    }
+}
+
+// SAFETY: A `Interface` is always reference-counted and can be released from any thread.
+unsafe impl Send for Interface {}
+
+// SAFETY: It is safe to send a &Interface to another thread because we do not
+// allow any mutation through a shared reference.
+unsafe impl Sync for Interface {}
+
+/// A USB device.
+///
+/// This structure represents the Rust abstraction for a C [`struct usb_device`].
+/// The implementation abstracts the usage of a C [`struct usb_device`] passed in
+/// from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid [`struct usb_device`] created by the C portion of the
+/// kernel.
+///
+/// [`struct usb_device`]: https://www.kernel.org/doc/html/latest/driver-api/usb/usb.html#c.usb_device
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::usb_device>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+    fn as_raw(&self) -> *mut bindings::usb_device {
+        self.0.get()
+    }
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        // SAFETY: The invariants of `Device` guarantee that `self.as_raw()`
+        // returns a valid `struct usb_device` pointer, for which we will
+        // acquire a new refcount.
+        unsafe { bindings::usb_get_dev(self.as_raw()) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::usb_put_dev(obj.cast().as_ptr()) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct usb_device`.
+        let dev = unsafe { &raw mut ((*self.as_raw()).dev) };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+// SAFETY: A `Device` is always reference-counted and can be released from any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: It is safe to send a &Device to another thread because we do not
+// allow any mutation through a shared reference.
+unsafe impl Sync for Device {}
+
+/// Declares a kernel module that exposes a single USB driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// module_usb_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     author: ["Author name"],
+///     description: "Description",
+///     license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_usb_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::usb::Adapter<T>, { $($f)* });
+    }
+}

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] samples: rust: add a USB driver sample
  2025-08-25 18:18 [PATCH 0/2] rust: usb: add initial USB abstractions Daniel Almeida
  2025-08-25 18:18 ` [PATCH 1/2] rust: usb: add basic " Daniel Almeida
@ 2025-08-25 18:18 ` Daniel Almeida
  2025-08-25 20:32 ` [PATCH 0/2] rust: usb: add initial USB abstractions Greg Kroah-Hartman
  2 siblings, 0 replies; 6+ messages in thread
From: Daniel Almeida @ 2025-08-25 18:18 UTC (permalink / raw)
  To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Greg Kroah-Hartman
  Cc: linux-kernel, rust-for-linux, linux-usb, Daniel Almeida

In light of the newly-added Rust abstractions for USB devices and
drivers, add a sample USB rust driver that serves both to showcase what
is currently supported, as well as be the only user of the USB
abstractions for now.

Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 samples/rust/Kconfig            | 11 ++++++++++
 samples/rust/Makefile           |  1 +
 samples/rust/rust_driver_usb.rs | 47 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 7f7371a004ee0a8f67dca99c836596709a70c4fa..fb222f93014c921b27a8a9a4293e90a2532faa82 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -83,6 +83,17 @@ config SAMPLE_RUST_DRIVER_PLATFORM
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_USB
+	tristate "USB Driver"
+	depends on USB
+	help
+	  This option builds the Rust USB driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_usb.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_FAUX
 	tristate "Faux Driver"
 	help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index bd2faad63b4f3befe7d1ed5139fe25c7a8b6d7f6..4e7df8a5cd277d101920c4b89a3ac6648b372b28 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
 obj-$(CONFIG_SAMPLE_RUST_DMA)			+= rust_dma.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB)		+= rust_driver_usb.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX)		+= rust_driver_faux.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY)	+= rust_driver_auxiliary.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5c396f421de7f972985e57af48e8a9da0c558973
--- /dev/null
+++ b/samples/rust/rust_driver_usb.rs
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (C) 2025 Collabora Ltd.
+
+//! Rust USB driver sample.
+
+use kernel::{device, device::Core, prelude::*, sync::aref::ARef, usb};
+
+struct SampleDriver {
+    _intf: ARef<usb::Interface>,
+}
+
+kernel::usb_device_table!(
+    USB_TABLE,
+    MODULE_USB_TABLE,
+    <SampleDriver as usb::Driver>::IdInfo,
+    [(usb::DeviceId::from_id(0x1234, 0x5678), ()),]
+);
+
+impl usb::Driver for SampleDriver {
+    type IdInfo = ();
+    const ID_TABLE: usb::IdTable<Self::IdInfo> = &USB_TABLE;
+
+    fn probe(
+        intf: &usb::Interface<Core>,
+        _id: &usb::DeviceId,
+        _info: &Self::IdInfo,
+    ) -> Result<Pin<KBox<Self>>> {
+        let dev: &device::Device<Core> = intf.as_ref();
+        dev_info!(dev, "Rust USB driver sample probed\n");
+
+        let drvdata = KBox::new(Self { _intf: intf.into() }, GFP_KERNEL)?;
+        Ok(drvdata.into())
+    }
+
+    fn disconnect(intf: &usb::Interface<Core>, _data: Pin<&Self>) {
+        let dev: &device::Device<Core> = intf.as_ref();
+        dev_info!(dev, "Rust USB driver sample disconnected\n");
+    }
+}
+
+kernel::module_usb_driver! {
+    type: SampleDriver,
+    name: "rust_driver_usb",
+    authors: ["Daniel Almeida"],
+    description: "Rust USB driver sample",
+    license: "GPL v2",
+}

-- 
2.50.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/2] rust: usb: add initial USB abstractions
  2025-08-25 18:18 [PATCH 0/2] rust: usb: add initial USB abstractions Daniel Almeida
  2025-08-25 18:18 ` [PATCH 1/2] rust: usb: add basic " Daniel Almeida
  2025-08-25 18:18 ` [PATCH 2/2] samples: rust: add a USB driver sample Daniel Almeida
@ 2025-08-25 20:32 ` Greg Kroah-Hartman
  2 siblings, 0 replies; 6+ messages in thread
From: Greg Kroah-Hartman @ 2025-08-25 20:32 UTC (permalink / raw)
  To: Daniel Almeida
  Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-kernel, rust-for-linux,
	linux-usb

On Mon, Aug 25, 2025 at 03:18:04PM -0300, Daniel Almeida wrote:
> This adds initial support for USB Rust drivers, not only because I see a
> widespread use of module_usb_driver() in media (which is a subsystem I
> aim to support) but also because I want to learn about USB in general
> and this is a nice opportunity to start doing so.

Oh wow, I wasn't expecting this, nice!

I'm at a conference all this week so I can't review this just yet, give
me a week please.  But I am happy to see this happen, thanks for doing
it.

greg k-h

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] rust: usb: add basic USB abstractions
  2025-08-25 18:18 ` [PATCH 1/2] rust: usb: add basic " Daniel Almeida
@ 2025-08-25 20:49   ` Benno Lossin
  2025-08-25 21:03     ` Daniel Almeida
  0 siblings, 1 reply; 6+ messages in thread
From: Benno Lossin @ 2025-08-25 20:49 UTC (permalink / raw)
  To: Daniel Almeida, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman
  Cc: linux-kernel, rust-for-linux, linux-usb

On Mon Aug 25, 2025 at 8:18 PM CEST, Daniel Almeida wrote:
> +impl DeviceId {
> +    /// Equivalent to C's `USB_DEVICE` macro.
> +    pub const fn from_id(vendor: u16, product: u16) -> Self {
> +        Self(bindings::usb_device_id {
> +            match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16,
> +            idVendor: vendor,
> +            idProduct: product,
> +            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
> +            ..unsafe { MaybeUninit::zeroed().assume_init() }

You can avoid this usage of `unsafe` with this patch series:

    https://lore.kernel.org/all/20250814093046.2071971-1-lossin@kernel.org

I'd like to avoid introducing any new one of these.

---
Cheers,
Benno

> +        })
> +    }

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] rust: usb: add basic USB abstractions
  2025-08-25 20:49   ` Benno Lossin
@ 2025-08-25 21:03     ` Daniel Almeida
  0 siblings, 0 replies; 6+ messages in thread
From: Daniel Almeida @ 2025-08-25 21:03 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Greg Kroah-Hartman, linux-kernel,
	rust-for-linux, linux-usb

Hi Benno,

> On 25 Aug 2025, at 17:49, Benno Lossin <lossin@kernel.org> wrote:
> 
> On Mon Aug 25, 2025 at 8:18 PM CEST, Daniel Almeida wrote:
>> +impl DeviceId {
>> +    /// Equivalent to C's `USB_DEVICE` macro.
>> +    pub const fn from_id(vendor: u16, product: u16) -> Self {
>> +        Self(bindings::usb_device_id {
>> +            match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16,
>> +            idVendor: vendor,
>> +            idProduct: product,
>> +            // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`.
>> +            ..unsafe { MaybeUninit::zeroed().assume_init() }
> 
> You can avoid this usage of `unsafe` with this patch series:
> 
>    https://lore.kernel.org/all/20250814093046.2071971-1-lossin@kernel.org
> 
> I'd like to avoid introducing any new one of these.
> 
> ---
> Cheers,
> Benno
> 
>> +        })
>> +    }
> 

Ah, nice, you spoke about this in the last RFL call, I remember it now.

Ok, I will address this in the next version.

— Daniel

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-08-25 21:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-25 18:18 [PATCH 0/2] rust: usb: add initial USB abstractions Daniel Almeida
2025-08-25 18:18 ` [PATCH 1/2] rust: usb: add basic " Daniel Almeida
2025-08-25 20:49   ` Benno Lossin
2025-08-25 21:03     ` Daniel Almeida
2025-08-25 18:18 ` [PATCH 2/2] samples: rust: add a USB driver sample Daniel Almeida
2025-08-25 20:32 ` [PATCH 0/2] rust: usb: add initial USB abstractions Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).