Skip to content

Commit 6e1c7b8

Browse files
committed
rust: add driver module.
It defines general functionality related to registering drivers with their respective subsystems, and registering modules that implement drivers. This is in preparation for adding support for Amba and PCI drivers. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 4946eec commit 6e1c7b8

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

rust/kernel/driver.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.).
4+
//!
5+
//! Each bus/subsystem is expected to implement [`DriverOps`], which allows drivers to register
6+
//! using the [`Registration`] class.
7+
8+
use crate::{str::CStr, Error, KernelModule, Result, ScopeGuard, ThisModule};
9+
use alloc::{boxed::Box, vec::Vec};
10+
use core::{cell::UnsafeCell, mem::MaybeUninit, pin::Pin};
11+
12+
/// A subsystem (e.g., PCI, Platform, Amba, etc.) that allows drivers to be written for it.
13+
pub trait DriverOps {
14+
/// The type that holds information about the registration. This is typically a struct defined
15+
/// by the C portion of the kernel.
16+
type RegType: Default;
17+
18+
/// The type that holds identification data for the devices supported by the driver. In
19+
/// addition to the information required by the bus, it may also store device-specific data
20+
/// using Rust types.
21+
type IdType: 'static;
22+
23+
/// The table of ids containing all supported devices.
24+
const ID_TABLE: &'static [Self::IdType];
25+
26+
/// The raw type that holds identification data for the devices supported by the driver. This
27+
/// is typically a struct defined by the C portion of the kernel.
28+
///
29+
/// A zero-terminated array of this type is produced and passed to the C portion during
30+
/// registration.
31+
type RawIdType;
32+
33+
/// Registers a driver.
34+
///
35+
/// # Safety
36+
///
37+
/// `reg` must point to valid, initialised, and writable memory. It may be modified by this
38+
/// function to hold registration state.
39+
///
40+
/// `id_table` must point to a valid for read zero-terminated array of IDs.
41+
///
42+
/// On success, `reg` and `id_table` must remain pinned and valid until the matching call to
43+
/// [`DriverOps::unregister`].
44+
unsafe fn register(
45+
reg: *mut Self::RegType,
46+
name: &'static CStr,
47+
id_table: *const Self::RawIdType,
48+
) -> Result;
49+
50+
/// Unregisters a driver previously registered with [`DriverOps::register`].
51+
///
52+
/// # Safety
53+
///
54+
/// `reg` must point to valid writable memory, initialised by a previous successful call to
55+
/// [`DriverOps::register`].
56+
unsafe fn unregister(reg: *mut Self::RegType);
57+
58+
/// Converts an id into a raw id.
59+
///
60+
/// This is used when building a zero-terminated array from the Rust array.
61+
fn to_raw_id(index: usize, id: &Self::IdType) -> Self::RawIdType;
62+
}
63+
64+
/// The registration of a driver.
65+
pub struct Registration<T: DriverOps> {
66+
is_registered: bool,
67+
concrete_reg: UnsafeCell<T::RegType>,
68+
id_table: Vec<MaybeUninit<T::RawIdType>>,
69+
}
70+
71+
// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to
72+
// share references to it with multiple threads as nothing can be done.
73+
unsafe impl<T: DriverOps> Sync for Registration<T> {}
74+
75+
impl<T: DriverOps> Registration<T> {
76+
/// Creates a new instance of the registration object.
77+
pub fn new() -> Self {
78+
Self {
79+
is_registered: false,
80+
concrete_reg: UnsafeCell::new(T::RegType::default()),
81+
id_table: Vec::new(),
82+
}
83+
}
84+
85+
/// Allocates a pinned registration object and registers it.
86+
///
87+
/// Returns a pinned heap-allocated representation of the registration.
88+
pub fn new_pinned(name: &'static CStr) -> Result<Pin<Box<Self>>> {
89+
let mut reg = Pin::from(Box::try_new(Self::new())?);
90+
reg.as_mut().register(name)?;
91+
Ok(reg)
92+
}
93+
94+
/// Registers a driver with its subsystem.
95+
///
96+
/// It must be pinned because the memory block that represents the registration is potentially
97+
/// self-referential.
98+
pub fn register(self: Pin<&mut Self>, name: &'static CStr) -> Result {
99+
// SAFETY: We never move out of `this`.
100+
let this = unsafe { self.get_unchecked_mut() };
101+
if this.is_registered {
102+
// Already registered.
103+
return Err(Error::EINVAL);
104+
}
105+
106+
if this.id_table.is_empty() {
107+
this.build_table()?;
108+
}
109+
110+
// SAFETY: `concrete_reg` was initialised via its default constructor. `id_table` was just
111+
// initialised above with a zero terminating entry. Both are only freed after `Self::drop`
112+
// is called, which first calls `T::unregister`.
113+
unsafe {
114+
T::register(
115+
this.concrete_reg.get(),
116+
name,
117+
&this.id_table[0] as *const _ as *const _,
118+
)
119+
}?;
120+
121+
this.is_registered = true;
122+
Ok(())
123+
}
124+
125+
/// Builds the zero-terminated raw-type array of supported devices.
126+
///
127+
/// This is not ideal because the table is built at runtime. Once Rust fully supports const
128+
/// generics, we can build the table at compile time.
129+
fn build_table(&mut self) -> Result {
130+
// Clear the table on failure, to indicate that the table isn't initialised.
131+
let mut table = ScopeGuard::new_with_data(&mut self.id_table, |t| t.clear());
132+
133+
table.try_reserve_exact(T::ID_TABLE.len() + 1)?;
134+
for (i, id) in T::ID_TABLE.iter().enumerate() {
135+
table.try_push(MaybeUninit::new(T::to_raw_id(i, id)))?;
136+
}
137+
table.try_push(MaybeUninit::zeroed())?;
138+
table.dismiss();
139+
Ok(())
140+
}
141+
}
142+
143+
impl<T: DriverOps> Default for Registration<T> {
144+
fn default() -> Self {
145+
Self::new()
146+
}
147+
}
148+
149+
impl<T: DriverOps> Drop for Registration<T> {
150+
fn drop(&mut self) {
151+
if self.is_registered {
152+
// SAFETY: This path only runs if a previous call to `T::register` completed
153+
// successfully.
154+
unsafe { T::unregister(self.concrete_reg.get()) };
155+
}
156+
}
157+
}
158+
159+
/// Custom code within device removal.
160+
pub trait DeviceRemoval {
161+
/// Cleans resources up when the device is removed.
162+
///
163+
/// This is called when a device is removed and offers implementers the chance to run some code
164+
/// that cleans state up.
165+
fn device_remove(&self);
166+
}
167+
168+
/// A kernel module that only registers the given driver on init.
169+
///
170+
/// This is a helper struct to make it easier to define single-functionality modules, in this case,
171+
/// modules that offer a single driver.
172+
pub struct Module<T: DriverOps> {
173+
_driver: Pin<Box<Registration<T>>>,
174+
}
175+
176+
impl<T: DriverOps> KernelModule for Module<T> {
177+
fn init(name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
178+
Ok(Self {
179+
_driver: Registration::new_pinned(name)?,
180+
})
181+
}
182+
}
183+
184+
/// Declares a kernel module that exposes a single driver.
185+
///
186+
/// It is meant to be used as a helper by other subsystems so they can more easily expose their own
187+
/// macros.
188+
#[macro_export]
189+
macro_rules! module_driver {
190+
(<$gen_type:ident>, $driver_ops:ty, { type: $type:ty, $($f:tt)* }) => {
191+
type Ops<$gen_type> = $driver_ops;
192+
type ModuleType = $crate::driver::Module<Ops<$type>>;
193+
$crate::prelude::module! {
194+
type: ModuleType,
195+
$($f)*
196+
}
197+
}
198+
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub mod bindings;
4444
pub mod buffer;
4545
pub mod c_types;
4646
pub mod chrdev;
47+
pub mod driver;
4748
mod error;
4849
pub mod file;
4950
pub mod file_operations;

0 commit comments

Comments
 (0)