Implement the `IntoBusDevice` trait for converting a `Device` reference to a
bus device reference for all bus devices. `Device` implements this trait as a
fallback.
The `IntoBusDevice` trait allows abstractions to provide the bus device in
class device callbacks.
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
rust/kernel/auxiliary.rs | 7 +++++++
rust/kernel/device.rs | 41 ++++++++++++++++++++++++++++++++++++++++
rust/kernel/i2c.rs | 7 +++++++
rust/kernel/pci.rs | 7 +++++++
rust/kernel/usb.rs | 6 ++++++
5 files changed, 68 insertions(+)
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index e11848bbf206..dea24265f549 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -15,6 +15,7 @@
};
use core::{
marker::PhantomData,
+ mem::offset_of,
ptr::{addr_of_mut, NonNull},
};
@@ -239,6 +240,12 @@ extern "C" fn release(dev: *mut bindings::device) {
}
}
+// SAFETY: `auxilary::Device` is a transparent wrapper of `struct auxiliary_device`.
+// The offset is guaranteed to point to a valid device field inside `auxilary::Device`.
+unsafe impl<Ctx: device::DeviceContext> device::IntoBusDevice<Ctx> for Device<Ctx> {
+ const OFFSET: usize = offset_of!(bindings::auxiliary_device, dev);
+}
+
// 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 });
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 1321e6f0b53c..5527854a195f 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -511,6 +511,47 @@ impl DeviceContext for Core {}
impl DeviceContext for CoreInternal {}
impl DeviceContext for Normal {}
+/// Bus devices can implement this trait to allow abstractions to provide the bus device in
+/// class device callbacks.
+///
+/// # Safety
+///
+/// `IntoBusDevice::OFFSET` must be a offset to a device field in the implemented struct.
+pub(crate) unsafe trait IntoBusDevice<Ctx: DeviceContext>:
+ AsRef<Device<Ctx>>
+{
+ /// The relative offset to the device field.
+ ///
+ /// Use `offset_of!(bindings, field)` macro to avoid breakage.
+ const OFFSET: usize;
+
+ /// Convert a reference to [`Device`] into `Self`.
+ ///
+ /// # Safety
+ ///
+ /// `dev` must be contained in `Self`.
+ unsafe fn from_device(dev: &Device<Ctx>) -> &Self
+ where
+ Self: Sized,
+ {
+ let raw = dev.as_raw();
+ // SAFETY: `raw - Self::OFFSET` is guaranteed by the safety requirements
+ // to be a valid pointer to `Self`.
+ unsafe { &*raw.byte_sub(Self::OFFSET).cast::<Self>() }
+ }
+}
+
+// SAFETY: `Device` is a transparent wrapper of `device`.
+unsafe impl<Ctx: DeviceContext> IntoBusDevice<Ctx> for Device<Ctx> {
+ const OFFSET: usize = 0;
+}
+
+impl<Ctx: DeviceContext> AsRef<Device<Ctx>> for Device<Ctx> {
+ fn as_ref(&self) -> &Device<Ctx> {
+ self
+ }
+}
+
/// # Safety
///
/// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index 7fccffebbd6c..e9a7e09b0116 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -15,6 +15,7 @@
use core::{
marker::PhantomData,
+ mem::offset_of,
ptr::{from_ref, NonNull},
};
@@ -465,6 +466,12 @@ fn as_raw(&self) -> *mut bindings::i2c_client {
}
}
+// SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`.
+// The offset is guaranteed to point to a valid device field inside `I2cClient`.
+unsafe impl<Ctx: device::DeviceContext> device::IntoBusDevice<Ctx> for I2cClient<Ctx> {
+ const OFFSET: usize = offset_of!(bindings::i2c_client, dev);
+}
+
// SAFETY: `I2cClient` is a transparent wrapper of a type that doesn't depend on `I2cClient`'s generic
// argument.
kernel::impl_device_context_deref!(unsafe { I2cClient });
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 7fcc5f6022c1..ad00a5c1294a 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -19,6 +19,7 @@
};
use core::{
marker::PhantomData,
+ mem::offset_of,
ops::Deref,
ptr::{addr_of_mut, NonNull},
};
@@ -593,6 +594,12 @@ pub fn set_master(&self) {
}
}
+// SAFETY: `pci::Device` is a transparent wrapper of `struct pci_dev`.
+// The offset is guaranteed to point to a valid device field inside `pci::Device`.
+unsafe impl<Ctx: device::DeviceContext> device::IntoBusDevice<Ctx> for Device<Ctx> {
+ const OFFSET: usize = offset_of!(bindings::pci_dev, dev);
+}
+
// 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 });
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 14ddb711bab3..8862004e54f9 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -324,6 +324,12 @@ fn as_raw(&self) -> *mut bindings::usb_interface {
}
}
+// SAFETY: `usb::Interface` is a transparent wrapper of `struct usb_interface`.
+// The offset is guaranteed to point to a valid device field inside `usb::Interface`.
+unsafe impl<Ctx: device::DeviceContext> device::IntoBusDevice<Ctx> for Interface<Ctx> {
+ const OFFSET: usize = offset_of!(bindings::usb_interface, dev);
+}
+
// 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 });
--
2.49.1
On Sat Oct 18, 2025 at 10:59 PM CEST, Markus Probst wrote:
> Implement the `IntoBusDevice` trait for converting a `Device` reference to a
> bus device reference for all bus devices. `Device` implements this trait as a
> fallback.
>
> The `IntoBusDevice` trait allows abstractions to provide the bus device in
> class device callbacks.
>
> Signed-off-by: Markus Probst <markus.probst@posteo.de>
> ---
> rust/kernel/auxiliary.rs | 7 +++++++
> rust/kernel/device.rs | 41 ++++++++++++++++++++++++++++++++++++++++
> rust/kernel/i2c.rs | 7 +++++++
i2c is not upstream yet, hence it should not be part of this patch. Instead you
should include the platform bus though.
> rust/kernel/pci.rs | 7 +++++++
> rust/kernel/usb.rs | 6 ++++++
> 5 files changed, 68 insertions(+)
>
> diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
> index e11848bbf206..dea24265f549 100644
> --- a/rust/kernel/auxiliary.rs
> +++ b/rust/kernel/auxiliary.rs
> @@ -15,6 +15,7 @@
> };
> use core::{
> marker::PhantomData,
> + mem::offset_of,
> ptr::{addr_of_mut, NonNull},
> };
>
> @@ -239,6 +240,12 @@ extern "C" fn release(dev: *mut bindings::device) {
> }
> }
>
> +// SAFETY: `auxilary::Device` is a transparent wrapper of `struct auxiliary_device`.
> +// The offset is guaranteed to point to a valid device field inside `auxilary::Device`.
> +unsafe impl<Ctx: device::DeviceContext> device::IntoBusDevice<Ctx> for Device<Ctx> {
> + const OFFSET: usize = offset_of!(bindings::auxiliary_device, dev);
> +}
> +
> // 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 });
> diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
> index 1321e6f0b53c..5527854a195f 100644
> --- a/rust/kernel/device.rs
> +++ b/rust/kernel/device.rs
> @@ -511,6 +511,47 @@ impl DeviceContext for Core {}
> impl DeviceContext for CoreInternal {}
> impl DeviceContext for Normal {}
>
> +/// Bus devices can implement this trait to allow abstractions to provide the bus device in
> +/// class device callbacks.
> +///
> +/// # Safety
> +///
> +/// `IntoBusDevice::OFFSET` must be a offset to a device field in the implemented struct.
I think we should also require that this must only be implemented by bus device
types.
> +pub(crate) unsafe trait IntoBusDevice<Ctx: DeviceContext>:
> + AsRef<Device<Ctx>>
We should probably name this AsBusDevice.
> +{
> + /// The relative offset to the device field.
> + ///
> + /// Use `offset_of!(bindings, field)` macro to avoid breakage.
> + const OFFSET: usize;
> +
> + /// Convert a reference to [`Device`] into `Self`.
> + ///
> + /// # Safety
> + ///
> + /// `dev` must be contained in `Self`.
> + unsafe fn from_device(dev: &Device<Ctx>) -> &Self
As mentioned in the other thread, my concern remains that this could be abused
by drivers.
For now the trait is pub(crate), but with the new build system coming soon,
we're able to split things out of the kernel crate, and hence bus abstractions
and driver-core code may end up in different crates requiring this to become
public.
We should at least document that this must not be used by drivers and is
intended for bus and class device abstractions only.
> + where
> + Self: Sized,
> + {
> + let raw = dev.as_raw();
> + // SAFETY: `raw - Self::OFFSET` is guaranteed by the safety requirements
> + // to be a valid pointer to `Self`.
> + unsafe { &*raw.byte_sub(Self::OFFSET).cast::<Self>() }
> + }
> +}
> +
> +// SAFETY: `Device` is a transparent wrapper of `device`.
> +unsafe impl<Ctx: DeviceContext> IntoBusDevice<Ctx> for Device<Ctx> {
> + const OFFSET: usize = 0;
> +}
A generic device is not guaranteed to be a bus device. Also, I don't see a
reason why class device abstractions would want to work with a generic device
rather than the actual bus device parent. Hence, let's drop this impl.
© 2016 - 2026 Red Hat, Inc.