[PATCH 2/2] rust/kernel: Add platform::ModuleDevice

Lyude Paul
2025-01-22T23:54:32Z

A number of kernel modules work with virtual devices, where being virtual
implies that there's no physical device to actually be plugged into the
system. Because of that, such modules need to be able to manually
instantiate a kernel device themselves - which can then be probed in the
same manner as any other kernel device.

This adds support for such a usecase by introducing another platform device
type, ModuleDevice. This type is interchangeable with normal platform
devices, with the one exception being that it controls the lifetime of the
registration of the device.

Signed-off-by: Lyude Paul <lyude@redhat.com>
Co-authored-by: Maíra Canal <mairacanal@riseup.net>
---
rust/kernel/platform.rs | 96 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 94 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 75dc7824eccf4..b5d38bb182e93 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -13,8 +13,11 @@
types::{ARef, ForeignOwnable, Opaque},
ThisModule,
};
-
-use core::ptr::{NonNull, addr_of_mut};
+use core::{
+ mem::ManuallyDrop,
+ ops::*,
+ ptr::{addr_of_mut, NonNull},
+};

/// An adapter for the registration of platform drivers.
pub struct Adapter<T: Driver>(T);
@@ -213,3 +216,92 @@ fn as_ref(&self) -> &device::Device {
&self.0
}
}
+
+/// A platform device ID specifier.
+///
+/// This type is used for selecting the kind of device ID to use when constructing a new
+/// [`ModuleDevice`].
+#[derive(Copy, Clone)]
+pub enum ModuleDeviceId {
+ /// Do not use a device ID with a device.
+ None,
+ /// Automatically allocate a device ID for a device.
+ Auto,
+ /// Explicitly specify a device ID for a device.
+ Explicit(i32),
+}
+
+impl ModuleDeviceId {
+ fn as_raw(self) -> Result<i32> {
+ match self {
+ ModuleDeviceId::Explicit(id) => {
+ if matches!(
+ id,
+ bindings::PLATFORM_DEVID_NONE | bindings::PLATFORM_DEVID_AUTO
+ ) {
+ Err(EINVAL)
+ } else {
+ Ok(id)
+ }
+ }
+ ModuleDeviceId::None => Ok(bindings::PLATFORM_DEVID_NONE),
+ ModuleDeviceId::Auto => Ok(bindings::PLATFORM_DEVID_AUTO),
+ }
+ }
+}
+
+/// A platform device that was created by a module.
+///
+/// This type represents a platform device that was manually created by a kernel module, typically a
+/// virtual device, instead of being discovered by the kernel. It is probed upon creation in the
+/// same manner as a typical platform device, and the device will not be unregistered until this
+/// type is dropped.
+// We store the Device in a ManuallyDrop container, since we must enforce that our reference to the
+// Device is dropped using platform_device_unregister()
+pub struct ModuleDevice(ManuallyDrop<Device>);
+
+impl ModuleDevice {
+ /// Create and register a new platform device.
+ ///
+ /// This creates and registers a new platform device. This is usually only useful for drivers
+ /// which create virtual devices, as drivers for real hardware can rely on the kernel's probing
+ /// process.
+ pub fn new(name: &'static CStr, id: ModuleDeviceId) -> Result<Self> {
+ // SAFETY:
+ // * ModuleDeviceId::as_raw() always returns a valid device ID
+ // * Returns NULL on failure, or a valid platform_device pointer on success
+ let pdev_ptr = unsafe { bindings::platform_device_alloc(name.as_char_ptr(), id.as_raw()?) };
+ if pdev_ptr.is_null() {
+ return Err(ENOMEM);
+ }
+
+ // SAFETY:
+ // * The previous function is guaranteed to have returned a valid pointer to a platform_dev,
+ // or NULL (which we checked for already)
+ // * The previous function also took a single reference to the platform_dev
+ let pdev = unsafe { Device::from_raw(pdev_ptr) };
+
+ // SAFETY: We already checked that pdev_ptr is valid above.
+ to_result(unsafe { bindings::platform_device_add(pdev_ptr) })
+ .map(|_| ModuleDevice(ManuallyDrop::new(pdev)))
+ }
+}
+
+impl Deref for ModuleDevice {
+ type Target = Device;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl Drop for ModuleDevice {
+ fn drop(&mut self) {
+ // SAFETY: Only one instance of this type can exist for a given platform device, so this is
+ // safe to call.
+ unsafe { bindings::platform_device_unregister(self.as_raw()) }
+
+ // No need to manually drop our contents, as platform_device_unregister() dropped the ref
+ // count that was owned by this type.
+ }
+}
--
2.47.1