Skip to content

Commit b65c684

Browse files
authored
Merge pull request #544 from wedsonaf/driver
rust: add `driver` module.
2 parents 1560918 + 6e1c7b8 commit b65c684

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)