Skip to content

Commit fb3fdcb

Browse files
author
Sven Van Asbroeck
committed
rust-core/platdev: add of/devicetree matching support
Implement the bare minimum required so a Rust `platdev` can match with devices described in the devicetree. Driver and device will match if their `compatible` strings match. The Linux kernel will instantiate one device per match. Ideally, the of table should be const, and created at build time, but we haven't figured out how to accomplish that yet. Signed-off-by: Sven Van Asbroeck <[email protected]>
1 parent 8687614 commit fb3fdcb

File tree

4 files changed

+98
-2
lines changed

4 files changed

+98
-2
lines changed

rust/kernel/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/mm.h>
1515
#include <uapi/linux/android/binder.h>
1616
#include <linux/platform_device.h>
17+
#include <linux/of_platform.h>
1718

1819
// `bindgen` gets confused at certain things
1920
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub mod sysctl;
6262

6363
pub mod io_buffer;
6464
pub mod iov_iter;
65+
pub mod of;
6566
pub mod platdev;
6667
mod types;
6768
pub mod user_ptr;

rust/kernel/of.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Devicetree and Open Firmware abstractions.
4+
//!
5+
//! C header: [`include/linux/of_*.h`](../../../../include/linux/of_*.h)
6+
7+
use alloc::boxed::Box;
8+
9+
use crate::{
10+
bindings, c_types,
11+
error::{Error, Result},
12+
types::PointerWrapper,
13+
CStr,
14+
};
15+
16+
use core::mem::transmute;
17+
18+
type InnerTable = Box<[bindings::of_device_id; 2]>;
19+
20+
/// Wraps a kernel Open Firmware / devicetree match table.
21+
///
22+
/// Rust drivers may create this structure to match against devices
23+
/// described in the devicetree.
24+
///
25+
/// The ['PointerWrapper'] trait provides conversion to/from a raw pointer,
26+
/// suitable to be assigned to a `bindings::device_driver::of_match_table`.
27+
///
28+
/// # Invariants
29+
///
30+
/// The final array element is always filled with zeros (the default).
31+
pub struct OfMatchTable(InnerTable);
32+
33+
impl OfMatchTable {
34+
/// Creates a [`OfMatchTable`] from a single `compatible` string.
35+
pub fn new(compatible: &CStr<'static>) -> Result<Self> {
36+
let tbl: InnerTable = Box::try_new([
37+
Self::new_of_device_id(compatible)?,
38+
bindings::of_device_id::default(),
39+
])?;
40+
// INVARIANTS: we allocated an array with `default()` as its final
41+
// element, therefore that final element will be filled with zeros,
42+
// and the invariant above will hold.
43+
Ok(Self(tbl))
44+
}
45+
46+
fn new_of_device_id(compatible: &CStr<'static>) -> Result<bindings::of_device_id> {
47+
let mut buf = [0_u8; 128];
48+
if compatible.len() > buf.len() {
49+
return Err(Error::EINVAL);
50+
}
51+
buf.get_mut(..compatible.len())
52+
.ok_or(Error::EINVAL)?
53+
.copy_from_slice(compatible.as_bytes());
54+
Ok(bindings::of_device_id {
55+
// SAFETY: re-interpretation from [u8] to [c_types::c_char] of same length is always safe.
56+
compatible: unsafe { transmute::<[u8; 128], [c_types::c_char; 128]>(buf) },
57+
..Default::default()
58+
})
59+
}
60+
}
61+
62+
impl PointerWrapper for OfMatchTable {
63+
fn into_pointer(self) -> *const c_types::c_void {
64+
// Per the invariant above, the generated pointer points to an
65+
// array of `bindings::of_device_id`, where the final element is
66+
// filled with zeros (the sentinel). Therefore, it's suitable to
67+
// be assigned to `bindings::device_driver::of_match_table`.
68+
self.0.into_pointer()
69+
}
70+
71+
unsafe fn from_pointer(p: *const c_types::c_void) -> Self {
72+
Self(InnerTable::from_pointer(p))
73+
}
74+
}

rust/kernel/platdev.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
use crate::{
1010
bindings, c_types,
1111
error::{Error, Result},
12-
pr_info, CStr,
12+
of::OfMatchTable,
13+
pr_info,
14+
types::PointerWrapper,
15+
CStr,
1316
};
1417
use alloc::boxed::Box;
1518
use core::{marker::PhantomPinned, pin::Pin};
@@ -18,6 +21,7 @@ use core::{marker::PhantomPinned, pin::Pin};
1821
#[derive(Default)]
1922
pub struct Registration {
2023
registered: bool,
24+
of_table: Option<*const c_types::c_void>,
2125
pdrv: bindings::platform_driver,
2226
_pin: PhantomPinned,
2327
}
@@ -40,6 +44,7 @@ impl Registration {
4044
fn register(
4145
self: Pin<&mut Self>,
4246
name: CStr<'static>,
47+
of_match_table: Option<OfMatchTable>,
4348
module: &'static crate::ThisModule,
4449
) -> Result {
4550
// SAFETY: We must ensure that we never move out of `this`.
@@ -49,13 +54,22 @@ impl Registration {
4954
return Err(Error::EINVAL);
5055
}
5156
this.pdrv.driver.name = name.as_ptr() as *const c_types::c_char;
57+
if let Some(tbl) = of_match_table {
58+
let ptr = tbl.into_pointer();
59+
this.of_table = Some(ptr);
60+
this.pdrv.driver.of_match_table = ptr.cast();
61+
}
5262
this.pdrv.probe = Some(probe_callback);
5363
this.pdrv.remove = Some(remove_callback);
5464
// SAFETY:
5565
// - `this.pdrv` lives at least until the call to `platform_driver_unregister()` returns.
5666
// - `name` pointer has static lifetime.
5767
// - `module.0` lives at least as long as the module.
5868
// - `probe()` and `remove()` are static functions.
69+
// - `of_match_table` is either:
70+
// - a raw pointer which lives until after the call to
71+
// `bindings::platform_driver_unregister()`, or
72+
// - null.
5973
let ret = unsafe { bindings::__platform_driver_register(&mut this.pdrv, module.0) };
6074
if ret < 0 {
6175
return Err(Error::from_kernel_errno(ret));
@@ -69,10 +83,11 @@ impl Registration {
6983
/// Returns a pinned heap-allocated representation of the registration.
7084
pub fn new_pinned(
7185
name: CStr<'static>,
86+
of_match_tbl: Option<OfMatchTable>,
7287
module: &'static crate::ThisModule,
7388
) -> Result<Pin<Box<Self>>> {
7489
let mut r = Pin::from(Box::try_new(Self::default())?);
75-
r.as_mut().register(name, module)?;
90+
r.as_mut().register(name, of_match_tbl, module)?;
7691
Ok(r)
7792
}
7893
}
@@ -85,5 +100,10 @@ impl Drop for Registration {
85100
// safe to call.
86101
unsafe { bindings::platform_driver_unregister(&mut self.pdrv) }
87102
}
103+
if let Some(ptr) = self.of_table {
104+
// SAFETY: `ptr` came from an `OfMatchTable`.
105+
let tbl = unsafe { OfMatchTable::from_pointer(ptr) };
106+
drop(tbl);
107+
}
88108
}
89109
}

0 commit comments

Comments
 (0)