The documentation for the generic Device type is outdated and deserves
much more detail.
Hence, expand the documentation and cover topics such as device types,
device contexts, as well as information on how to use the generic device
infrastructure to implement bus and class specific device types.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/device.rs | 135 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 122 insertions(+), 13 deletions(-)
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index d7ac56628fe5..1a2ae16d8f11 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -15,23 +15,132 @@
pub mod property;
-/// A reference-counted device.
+/// The core representation of a device in the kernel's driver model.
///
-/// This structure represents the Rust abstraction for a C `struct device`. This implementation
-/// abstracts the usage of an already existing C `struct device` within Rust code that we get
-/// passed from the C side.
+/// This structure represents the Rust abstraction for a C `struct device`. A [`Device`] can either
+/// exist as temporary reference (see also [`Device::from_raw`]), which is only valid within a
+/// certain scope or as [`ARef<Device>`], owning a dedicated reference count.
///
-/// An instance of this abstraction can be obtained temporarily or permanent.
+/// # Device Types
///
-/// A temporary one is bound to the lifetime of the C `struct device` pointer used for creation.
-/// A permanent instance is always reference-counted and hence not restricted by any lifetime
-/// boundaries.
+/// A [`Device`] can represent either a bus device or a class device.
///
-/// For subsystems it is recommended to create a permanent instance to wrap into a subsystem
-/// specific device structure (e.g. `pci::Device`). This is useful for passing it to drivers in
-/// `T::probe()`, such that a driver can store the `ARef<Device>` (equivalent to storing a
-/// `struct device` pointer in a C driver) for arbitrary purposes, e.g. allocating DMA coherent
-/// memory.
+/// ## Bus Devices
+///
+/// A bus device is a [`Device`] that is associated with a physical or virtual bus. Examples of
+/// buses include PCI, USB, I2C, and SPI. Devices attached to a bus are registered with a specific
+/// bus type, which facilitates matching devices with appropriate drivers based on IDs or other
+/// identifying information. Bus devices are visible in sysfs under `/sys/bus/<bus-name>/devices/`.
+///
+/// ## Class Devices
+///
+/// A class device is a [`Device`] that is associated with a logical category of functionality
+/// rather than a physical bus. Examples of classes include block devices, network interfaces, sound
+/// cards, and input devices. Class devices are grouped under a common class and exposed to
+/// userspace via entries in `/sys/class/<class-name>/`.
+///
+/// # Device Context
+///
+/// [`Device`] references are generic over a [`DeviceContext`], which represents the type state of
+/// a [`Device`].
+///
+/// As the name indicates, this type state represents the context of the scope the [`Device`]
+/// reference is valid in. For instance, the [`Bound`] context guarantees that the [`Device`] is
+/// bound to a driver for the entire duration of the existence of a [`Device<Bound>`] reference.
+///
+/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`] and [`CoreInternal`].
+///
+/// Unless selected otherwise [`Device`] defaults to the [`Normal`] [`DeviceContext`], which by
+/// itself has no additional requirements.
+///
+/// It is always up to the caller of [`Device::from_raw`] to select the correct [`DeviceContext`]
+/// type for the corresponding scope the [`Device`] reference is created in.
+///
+/// All [`DeviceContext`] types other than [`Normal`] are intended to be used with
+/// [bus devices](#bus-devices) only.
+///
+/// # Implementing Bus Devices
+///
+/// This section provides a guideline to implement bus specific devices, such as
+/// [`pci::Device`](kernel::pci::Device) or [`platform::Device`](kernel::platform::Device).
+///
+/// A bus specific device should be defined as follows.
+///
+/// ```ignore
+/// #[repr(transparent)]
+/// pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+/// Opaque<bindings::bus_device_type>,
+/// PhantomData<Ctx>,
+/// );
+/// ```
+///
+/// Since devices are reference counted, [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted)
+/// should be implemented for `Device` (i.e. `Device<Normal>`). Note that
+/// [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted) must not be implemented for any other
+/// [`DeviceContext`], since all other device context types are only valid in a certain scope.
+///
+/// In order to be able to implement the [`DeviceContext`] dereference hierarchy, bus device
+/// implementations should call the [`impl_device_context_deref`](kernel::impl_device_context_deref)
+/// macro as shown below.
+///
+/// ```ignore
+/// // 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 });
+/// ```
+/// In order to convert from a any [`Device<Ctx>`] to [`ARef<Device>`], bus devices can implement
+/// the following macro call.
+///
+/// ```ignore
+/// kernel::impl_device_context_into_aref!(Device);
+/// ```
+/// Bus devices should also implement the following [`AsRef`] implementation, such that users can
+/// easily derive a generic [`Device`] reference.
+///
+/// ```ignore
+/// impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+/// fn as_ref(&self) -> &device::Device<Ctx> {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// # Implementing Class Devices
+///
+/// Class device implementations require less infrastructure and depend slightly more on the
+/// specific subsystem.
+///
+/// An example implementation for a class device could look like this.
+///
+/// ```ignore
+/// #[repr(C)]
+/// #[pin_data]
+/// pub struct Device<T: class::Driver> {
+/// dev: Opaque<bindings::class_device_type>,
+/// #[pin]
+/// data: T::Data,
+/// }
+/// ```
+///
+/// This class device uses the sub-classing pattern to embed the driver's private data within the
+/// allocation of the class device. For this to be possible the class device is generic over the
+/// class specific `Driver` trait implementation.
+///
+/// Just like any device, class devices are reference counted and should hence implement
+/// [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted) for `Device`.
+///
+/// Class devices should also implement the following [`AsRef`] implementation, such that users can
+/// easily derive a generic [`Device`] reference.
+///
+/// ```ignore
+/// impl<T: class::Driver> AsRef<device::Device> for Device<T> {
+/// fn as_ref(&self) -> &device::Device {
+/// ...
+/// }
+/// }
+/// ```
+///
+/// An example for a class device implementation is [`drm::Device`](kernel::drm::Device).
///
/// # Invariants
///
--
2.50.0
On Fri, Jul 18, 2025 at 12:45:38AM +0200, Danilo Krummrich wrote: > The documentation for the generic Device type is outdated and deserves > much more detail. > > Hence, expand the documentation and cover topics such as device types, > device contexts, as well as information on how to use the generic device > infrastructure to implement bus and class specific device types. > > Signed-off-by: Danilo Krummrich <dakr@kernel.org> Overall I think this series is pretty great. It also clarifies some things for me, particularly the difference between bus and class devices. > +/// # Device Types > /// > +/// A [`Device`] can represent either a bus device or a class device. > /// > +/// ## Bus Devices > +/// > +/// A bus device is a [`Device`] that is associated with a physical or virtual bus. Examples of > +/// buses include PCI, USB, I2C, and SPI. Devices attached to a bus are registered with a specific > +/// bus type, which facilitates matching devices with appropriate drivers based on IDs or other > +/// identifying information. Bus devices are visible in sysfs under `/sys/bus/<bus-name>/devices/`. > +/// > +/// ## Class Devices > +/// > +/// A class device is a [`Device`] that is associated with a logical category of functionality > +/// rather than a physical bus. Examples of classes include block devices, network interfaces, sound > +/// cards, and input devices. Class devices are grouped under a common class and exposed to > +/// userspace via entries in `/sys/class/<class-name>/`. > +/// > +/// # Device Context > +/// > +/// [`Device`] references are generic over a [`DeviceContext`], which represents the type state of > +/// a [`Device`]. > +/// > +/// As the name indicates, this type state represents the context of the scope the [`Device`] > +/// reference is valid in. For instance, the [`Bound`] context guarantees that the [`Device`] is > +/// bound to a driver for the entire duration of the existence of a [`Device<Bound>`] reference. > +/// > +/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`] and [`CoreInternal`]. > +/// > +/// Unless selected otherwise [`Device`] defaults to the [`Normal`] [`DeviceContext`], which by > +/// itself has no additional requirements. > +/// > +/// It is always up to the caller of [`Device::from_raw`] to select the correct [`DeviceContext`] > +/// type for the corresponding scope the [`Device`] reference is created in. > +/// > +/// All [`DeviceContext`] types other than [`Normal`] are intended to be used with > +/// [bus devices](#bus-devices) only. This raises a few questions for me. The first one is "why"? On other series I have been told that interrupts must be registered and deregistered before the device is unbound. Does the same not apply to interrupts for an input device such as a USB keyboard? The second one is why we use the same `Device` type for both cases? Would it not make more sense to have a BusDevice and ClassDevice type? > +/// # Implementing Bus Devices > +/// > +/// This section provides a guideline to implement bus specific devices, such as > +/// [`pci::Device`](kernel::pci::Device) or [`platform::Device`](kernel::platform::Device). > +/// > +/// A bus specific device should be defined as follows. > +/// > +/// ```ignore > +/// #[repr(transparent)] > +/// pub struct Device<Ctx: device::DeviceContext = device::Normal>( > +/// Opaque<bindings::bus_device_type>, > +/// PhantomData<Ctx>, > +/// ); > +/// ``` > +/// > +/// Since devices are reference counted, [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted) > +/// should be implemented for `Device` (i.e. `Device<Normal>`). Note that > +/// [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted) must not be implemented for any other > +/// [`DeviceContext`], since all other device context types are only valid in a certain scope. As a general comment to all three patches, I would suggest separating out the link locations. /// Since devices are reference counted, [`AlwaysRefCounted`] should be /// implemented for `Device` (i.e. `Device<Normal>`). Note that /// [`AlwaysRefCounted`] must not be implemented for any other /// [`DeviceContext`], since all other device context types are only /// valid in a certain scope. and then at the end: /// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted I think it's a lot easier to read the markdown version when links are separated out like this. Alice
On Mon Jul 21, 2025 at 1:26 PM CEST, Alice Ryhl wrote: > On Fri, Jul 18, 2025 at 12:45:38AM +0200, Danilo Krummrich wrote: >> +/// All [`DeviceContext`] types other than [`Normal`] are intended to be used with >> +/// [bus devices](#bus-devices) only. > > This raises a few questions for me. > > The first one is "why"? On other series I have been told that interrupts > must be registered and deregistered before the device is unbound. Does > the same not apply to interrupts for an input device such as a USB > keyboard? In your example there would be a USB device *and* an input device, where the former is a bus device and the latter a class device. Any resources from the "real" device on the bus are on the USB device, not the input device. Or in other words, class devices do not own resources of a "real" device and consequently are never bound to or unbound from a "real" device on the bus. > The second one is why we use the same `Device` type for both cases? > Would it not make more sense to have a BusDevice and ClassDevice type? Not really, the generic struct device isn't one or the other until it's used by an actual higher level bus or class device implementation. There isn't really a difference between the two for a base device. Regarding the device context, a base device can inherit a device context from the higher level bus or class device. In case of a class device, it's just that there's nothing to inherit other than Normal. >> +/// # Implementing Bus Devices >> +/// >> +/// This section provides a guideline to implement bus specific devices, such as >> +/// [`pci::Device`](kernel::pci::Device) or [`platform::Device`](kernel::platform::Device). >> +/// >> +/// A bus specific device should be defined as follows. >> +/// >> +/// ```ignore >> +/// #[repr(transparent)] >> +/// pub struct Device<Ctx: device::DeviceContext = device::Normal>( >> +/// Opaque<bindings::bus_device_type>, >> +/// PhantomData<Ctx>, >> +/// ); >> +/// ``` >> +/// >> +/// Since devices are reference counted, [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted) >> +/// should be implemented for `Device` (i.e. `Device<Normal>`). Note that >> +/// [`AlwaysRefCounted`](kernel::types::AlwaysRefCounted) must not be implemented for any other >> +/// [`DeviceContext`], since all other device context types are only valid in a certain scope. > > As a general comment to all three patches, I would suggest separating > out the link locations. > > /// Since devices are reference counted, [`AlwaysRefCounted`] should be > /// implemented for `Device` (i.e. `Device<Normal>`). Note that > /// [`AlwaysRefCounted`] must not be implemented for any other > /// [`DeviceContext`], since all other device context types are only > /// valid in a certain scope. > > and then at the end: > > /// [`AlwaysRefCounted`]: kernel::types::AlwaysRefCounted > > I think it's a lot easier to read the markdown version when links are > separated out like this. That's a good suggestion, thanks!
On Mon, Jul 21, 2025 at 11:26:23AM +0000, Alice Ryhl wrote: > On Fri, Jul 18, 2025 at 12:45:38AM +0200, Danilo Krummrich wrote: > > The documentation for the generic Device type is outdated and deserves > > much more detail. > > > > Hence, expand the documentation and cover topics such as device types, > > device contexts, as well as information on how to use the generic device > > infrastructure to implement bus and class specific device types. > > > > Signed-off-by: Danilo Krummrich <dakr@kernel.org> > > Overall I think this series is pretty great. It also clarifies some > things for me, particularly the difference between bus and class > devices. > > > +/// # Device Types > > /// > > +/// A [`Device`] can represent either a bus device or a class device. > > /// > > +/// ## Bus Devices > > +/// > > +/// A bus device is a [`Device`] that is associated with a physical or virtual bus. Examples of > > +/// buses include PCI, USB, I2C, and SPI. Devices attached to a bus are registered with a specific > > +/// bus type, which facilitates matching devices with appropriate drivers based on IDs or other > > +/// identifying information. Bus devices are visible in sysfs under `/sys/bus/<bus-name>/devices/`. > > +/// > > +/// ## Class Devices > > +/// > > +/// A class device is a [`Device`] that is associated with a logical category of functionality > > +/// rather than a physical bus. Examples of classes include block devices, network interfaces, sound > > +/// cards, and input devices. Class devices are grouped under a common class and exposed to > > +/// userspace via entries in `/sys/class/<class-name>/`. > > +/// > > +/// # Device Context > > +/// > > +/// [`Device`] references are generic over a [`DeviceContext`], which represents the type state of > > +/// a [`Device`]. > > +/// > > +/// As the name indicates, this type state represents the context of the scope the [`Device`] > > +/// reference is valid in. For instance, the [`Bound`] context guarantees that the [`Device`] is > > +/// bound to a driver for the entire duration of the existence of a [`Device<Bound>`] reference. > > +/// > > +/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`] and [`CoreInternal`]. > > +/// > > +/// Unless selected otherwise [`Device`] defaults to the [`Normal`] [`DeviceContext`], which by > > +/// itself has no additional requirements. > > +/// > > +/// It is always up to the caller of [`Device::from_raw`] to select the correct [`DeviceContext`] > > +/// type for the corresponding scope the [`Device`] reference is created in. > > +/// > > +/// All [`DeviceContext`] types other than [`Normal`] are intended to be used with > > +/// [bus devices](#bus-devices) only. > > This raises a few questions for me. > > The first one is "why"? On other series I have been told that interrupts > must be registered and deregistered before the device is unbound. Does > the same not apply to interrupts for an input device such as a USB > keyboard? USB drivers don't have interrupts :) Seriously, generally, yes, you should always unregister your irq handler BEFORE you unbind the driver from the device, otherwise the callback logic normally assumes that the driver is bound to the device and the pointers are almost always "stale" at that time. But I don't understand what interrupts have to do with the above documentation, what's the connection? > The second one is why we use the same `Device` type for both cases? > Would it not make more sense to have a BusDevice and ClassDevice type? We used to have both types, years/decades ago, and in the end, just gave up and merged them together as it really didn't make much sense as the structure really was the same type of thing everywhere. So let's not go back to that mess if we can prevent it please. We also used to have a out-of-tree patchset that just merged class and bus together, as they are almost the same thing as well, but that just caused too much confusion so we never merged it. Hope this helps, greg k-h
On Mon, Jul 21, 2025 at 01:42:17PM +0200, Greg KH wrote: > On Mon, Jul 21, 2025 at 11:26:23AM +0000, Alice Ryhl wrote: > > On Fri, Jul 18, 2025 at 12:45:38AM +0200, Danilo Krummrich wrote: > > > The documentation for the generic Device type is outdated and deserves > > > much more detail. > > > > > > Hence, expand the documentation and cover topics such as device types, > > > device contexts, as well as information on how to use the generic device > > > infrastructure to implement bus and class specific device types. > > > > > > Signed-off-by: Danilo Krummrich <dakr@kernel.org> > > > > Overall I think this series is pretty great. It also clarifies some > > things for me, particularly the difference between bus and class > > devices. > > > > > +/// # Device Types > > > /// > > > +/// A [`Device`] can represent either a bus device or a class device. > > > /// > > > +/// ## Bus Devices > > > +/// > > > +/// A bus device is a [`Device`] that is associated with a physical or virtual bus. Examples of > > > +/// buses include PCI, USB, I2C, and SPI. Devices attached to a bus are registered with a specific > > > +/// bus type, which facilitates matching devices with appropriate drivers based on IDs or other > > > +/// identifying information. Bus devices are visible in sysfs under `/sys/bus/<bus-name>/devices/`. > > > +/// > > > +/// ## Class Devices > > > +/// > > > +/// A class device is a [`Device`] that is associated with a logical category of functionality > > > +/// rather than a physical bus. Examples of classes include block devices, network interfaces, sound > > > +/// cards, and input devices. Class devices are grouped under a common class and exposed to > > > +/// userspace via entries in `/sys/class/<class-name>/`. > > > +/// > > > +/// # Device Context > > > +/// > > > +/// [`Device`] references are generic over a [`DeviceContext`], which represents the type state of > > > +/// a [`Device`]. > > > +/// > > > +/// As the name indicates, this type state represents the context of the scope the [`Device`] > > > +/// reference is valid in. For instance, the [`Bound`] context guarantees that the [`Device`] is > > > +/// bound to a driver for the entire duration of the existence of a [`Device<Bound>`] reference. > > > +/// > > > +/// Other [`DeviceContext`] types besides [`Bound`] are [`Normal`], [`Core`] and [`CoreInternal`]. > > > +/// > > > +/// Unless selected otherwise [`Device`] defaults to the [`Normal`] [`DeviceContext`], which by > > > +/// itself has no additional requirements. > > > +/// > > > +/// It is always up to the caller of [`Device::from_raw`] to select the correct [`DeviceContext`] > > > +/// type for the corresponding scope the [`Device`] reference is created in. > > > +/// > > > +/// All [`DeviceContext`] types other than [`Normal`] are intended to be used with > > > +/// [bus devices](#bus-devices) only. > > > > This raises a few questions for me. > > > > The first one is "why"? On other series I have been told that interrupts > > must be registered and deregistered before the device is unbound. Does > > the same not apply to interrupts for an input device such as a USB > > keyboard? > > USB drivers don't have interrupts :) I guess that if class drivers never have interrupts, then that is an answer to my question. > Seriously, generally, yes, you should always unregister your irq handler > BEFORE you unbind the driver from the device, otherwise the callback > logic normally assumes that the driver is bound to the device and the > pointers are almost always "stale" at that time. > > But I don't understand what interrupts have to do with the above > documentation, what's the connection? The connection is that to request an irq you must have a &Device<Bound>, so if you can only obtain a &Device<Bound> to a bus device, then that means that you can never request an irq for a class device. > > The second one is why we use the same `Device` type for both cases? > > Would it not make more sense to have a BusDevice and ClassDevice type? > > We used to have both types, years/decades ago, and in the end, just gave > up and merged them together as it really didn't make much sense as the > structure really was the same type of thing everywhere. So let's not go > back to that mess if we can prevent it please. > > We also used to have a out-of-tree patchset that just merged class and > bus together, as they are almost the same thing as well, but that just > caused too much confusion so we never merged it. Okay, thanks! Alice
On Mon Jul 21, 2025 at 2:07 PM CEST, Alice Ryhl wrote: > The connection is that to request an irq you must have a &Device<Bound>, > so if you can only obtain a &Device<Bound> to a bus device, then that > means that you can never request an irq for a class device. As mentioned in my other reply, a class device never owns resources of a "real" device such as an IRQ. A USB device, which represents a real device on a bus, is a bus device, in your example the class device is the input device.
On Mon, Jul 21, 2025 at 02:13:14PM +0200, Danilo Krummrich wrote: > On Mon Jul 21, 2025 at 2:07 PM CEST, Alice Ryhl wrote: > > The connection is that to request an irq you must have a &Device<Bound>, > > so if you can only obtain a &Device<Bound> to a bus device, then that > > means that you can never request an irq for a class device. > > As mentioned in my other reply, a class device never owns resources of a "real" > device such as an IRQ. > > A USB device, which represents a real device on a bus, is a bus device, in your > example the class device is the input device. > Just to confuse things a bit more (sorry), there are also "USB class devices" that represent USB devices that use the "generic" USB interface to userspace api. You can find these by searching for usb_register_dev(). Note, this is different from the drivers/usb/class/ drivers, which represent various "USB class protocol" that the USB.org group defines, and those talk to userspace through the various common class apis depending on the specific device type (input, network, etc.) and are USB bus drivers. Naming is hard, wait until you learn about "usb gadget" a word we had to invent to describe the thing :) thanks, greg k-h
On Mon Jul 21, 2025 at 3:17 PM CEST, Greg KH wrote: > On Mon, Jul 21, 2025 at 02:13:14PM +0200, Danilo Krummrich wrote: >> On Mon Jul 21, 2025 at 2:07 PM CEST, Alice Ryhl wrote: >> > The connection is that to request an irq you must have a &Device<Bound>, >> > so if you can only obtain a &Device<Bound> to a bus device, then that >> > means that you can never request an irq for a class device. >> >> As mentioned in my other reply, a class device never owns resources of a "real" >> device such as an IRQ. >> >> A USB device, which represents a real device on a bus, is a bus device, in your >> example the class device is the input device. >> > > Just to confuse things a bit more (sorry), there are also "USB class > devices" that represent USB devices that use the "generic" USB interface > to userspace api. You can find these by searching for > usb_register_dev(). In this case the struct usb_interface represents a bus device, that can become associated with e.g. a class device of the usbmisc class. :) So, yeah, there are multiple USB devices, some of them are bus devices, some of them are class devices. I'm not sure about the exact topology, but AFAIK USB has different bus device types representing the hierarchy, i.e. a device structure for the entire "real" USB device and another one for its interfaces. > Note, this is different from the drivers/usb/class/ drivers, which > represent various "USB class protocol" that the USB.org group defines, > and those talk to userspace through the various common class apis > depending on the specific device type (input, network, etc.) and are USB > bus drivers. > > Naming is hard, wait until you learn about "usb gadget" a word we had to > invent to describe the thing :) > > thanks, > > greg k-h
This looks good to me! > On 17 Jul 2025, at 19:45, Danilo Krummrich <dakr@kernel.org> wrote: > > The documentation for the generic Device type is outdated and deserves > much more detail. > > Hence, expand the documentation and cover topics such as device types, > device contexts, as well as information on how to use the generic device > infrastructure to implement bus and class specific device types. > > Signed-off-by: Danilo Krummrich <dakr@kernel.org> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
© 2016 - 2025 Red Hat, Inc.