summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanilo Krummrich <dakr@kernel.org>2024-08-21 04:48:16 +0200
committerDanilo Krummrich <dakr@kernel.org>2024-08-21 04:48:16 +0200
commit33026ad7c540512bd18dbc7ca7c58e12cba47e54 (patch)
tree04bc7eec7d67553989ea324a0e7e2a8a3a3af27f
parent14ce3e92cb73161638bedd012c4723eb12a24420 (diff)
rust: driver: generalize `Registration` structure
Generalize the `Registration` structure to be used commonly by subsystems / busses. Signed-off-by: Danilo Krummrich <dakr@kernel.org>
-rw-r--r--rust/kernel/driver.rs89
-rw-r--r--rust/kernel/lib.rs1
2 files changed, 90 insertions, 0 deletions
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
new file mode 100644
index 000000000000..4fec6ee1a90d
--- /dev/null
+++ b/rust/kernel/driver.rs
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Generic driver support
+
+use core::cell::UnsafeCell;
+use core::marker::PhantomData;
+use kernel::{alloc::flags::*, prelude::*};
+
+/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform,
+/// Amba, etc.) to privide the corresponding subsystem specific implementation to register /
+/// unregister a driver of the particular type (`RegType`).
+///
+/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call
+/// `bindings::__pci_register_driver` from `RegistrationOps::register` and
+/// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`.
+pub trait RegistrationOps {
+ /// The type that holds information about the registration. This is typically a struct defined
+ /// by the C portion of the kernel, e.g. `bindings::pci_driver.
+ type RegType: Default;
+
+ /// Registers a driver.
+ ///
+ /// # Safety
+ ///
+ /// `reg` must point to valid, initialised, and writable memory. It may be modified by this
+ /// function to hold registration state.
+ ///
+ /// On success, `reg` must remain pinned and valid until the matching call to
+ /// [`RegistrationOps::unregister`].
+ unsafe fn register(
+ reg: *mut Self::RegType,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result;
+
+ /// Unregisters a driver previously registered with [`RegistrationOps::register`].
+ ///
+ /// # Safety
+ ///
+ /// `reg` must point to valid writable memory, initialised by a previous successful call to
+ /// [`RegistrationOps::register`].
+ unsafe fn unregister(reg: *mut Self::RegType);
+}
+
+/// Registration structure for a driver.
+///
+/// The existance of an instance of this structure implies that the corresponding driver is
+/// currently registered.
+pub struct Registration<T: RegistrationOps> {
+ driver: Pin<KBox<UnsafeCell<T::RegType>>>,
+ _p: PhantomData<T>,
+}
+
+impl<T> Registration<T>
+where
+ T: RegistrationOps,
+{
+ /// Register a new driver from `T::RegType`.
+ pub fn new(name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
+ let driver = KBox::pin(UnsafeCell::new(T::RegType::default()), GFP_KERNEL)?;
+
+ // SAFETY: `driver` has just been initialized; it's only freed after `Self::drop`, which
+ // calls `T::unregister` first.
+ unsafe { T::register(driver.get(), name, module) }?;
+
+ Ok(Self {
+ driver,
+ _p: PhantomData::<T>,
+ })
+ }
+}
+
+impl<T> Drop for Registration<T>
+where
+ T: RegistrationOps,
+{
+ fn drop(&mut self) {
+ // SAFETY: Only ever called if the `Registration` was created successfully.
+ unsafe { T::unregister(self.driver.get()) };
+ }
+}
+
+// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to
+// share references to it with multiple threads as nothing can be done.
+unsafe impl<T> Sync for Registration<T> where T: RegistrationOps {}
+
+// SAFETY: Both registration and unregistration are implemented in C and safe to be performed from
+// any thread, so `Registration` is `Send`.
+unsafe impl<T> Send for Registration<T> where T: RegistrationOps {}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index ab8f3cf7fb40..2ef77187c5c7 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -31,6 +31,7 @@ pub mod block;
mod build_assert;
pub mod cred;
pub mod device;
+pub mod driver;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
pub mod firmware;