Skip to content

Commit e4a70b4

Browse files
committed
IMPORT: rust: port platform api from 'rust' branch
Signed-off-by: Fabien Parent <[email protected]>
1 parent 3efcd05 commit e4a70b4

File tree

4 files changed

+241
-0
lines changed

4 files changed

+241
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/device.h>
1111
#include <linux/errname.h>
1212
#include <linux/io-pgtable.h>
13+
#include <linux/platform_device.h>
1314
#include <linux/slab.h>
1415
#include <linux/refcount.h>
1516
#include <linux/wait.h>

rust/helpers.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/errname.h>
2929
#include <linux/mutex.h>
3030
#include <linux/of_device.h>
31+
#include <linux/platform_device.h>
3132
#include <linux/refcount.h>
3233
#include <linux/sched/signal.h>
3334
#include <linux/spinlock.h>
@@ -178,6 +179,18 @@ const struct of_device_id *rust_helper_of_match_device(
178179
}
179180
EXPORT_SYMBOL_GPL(rust_helper_of_match_device);
180181

182+
void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
183+
{
184+
return platform_get_drvdata(pdev);
185+
}
186+
EXPORT_SYMBOL_GPL(rust_helper_platform_get_drvdata);
187+
188+
void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
189+
{
190+
return platform_set_drvdata(pdev, data);
191+
}
192+
EXPORT_SYMBOL_GPL(rust_helper_platform_set_drvdata);
193+
181194
/*
182195
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
183196
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
#![no_std]
1515
#![feature(allocator_api)]
16+
#![feature(associated_type_defaults)]
1617
#![feature(coerce_unsized)]
1718
#![feature(const_refs_to_cell)]
1819
#![feature(dispatch_from_dyn)]
@@ -44,6 +45,7 @@ pub mod io_pgtable;
4445
#[cfg(CONFIG_KUNIT)]
4546
pub mod kunit;
4647
pub mod of;
48+
pub mod platform;
4749
pub mod prelude;
4850
pub mod print;
4951
mod static_assert;

rust/kernel/platform.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Platform devices and drivers.
4+
//!
5+
//! Also called `platdev`, `pdev`.
6+
//!
7+
//! C header: [`include/linux/platform_device.h`](../../../../include/linux/platform_device.h)
8+
9+
use crate::{
10+
bindings,
11+
device::{self, RawDevice},
12+
driver,
13+
error::{from_result, to_result, Result},
14+
of,
15+
str::CStr,
16+
types::ForeignOwnable,
17+
ThisModule,
18+
};
19+
20+
/// A registration of a platform driver.
21+
pub type Registration<T> = driver::Registration<Adapter<T>>;
22+
23+
/// An adapter for the registration of platform drivers.
24+
pub struct Adapter<T: Driver>(T);
25+
26+
impl<T: Driver> driver::DriverOps for Adapter<T> {
27+
type RegType = bindings::platform_driver;
28+
29+
unsafe fn register(
30+
reg: *mut bindings::platform_driver,
31+
name: &'static CStr,
32+
module: &'static ThisModule,
33+
) -> Result {
34+
// SAFETY: By the safety requirements of this function (defined in the trait definition),
35+
// `reg` is non-null and valid.
36+
let pdrv = unsafe { &mut *reg };
37+
38+
pdrv.driver.name = name.as_char_ptr();
39+
pdrv.probe = Some(Self::probe_callback);
40+
pdrv.remove = Some(Self::remove_callback);
41+
if let Some(t) = T::OF_DEVICE_ID_TABLE {
42+
pdrv.driver.of_match_table = t.as_ref();
43+
}
44+
// SAFETY:
45+
// - `pdrv` lives at least until the call to `platform_driver_unregister()` returns.
46+
// - `name` pointer has static lifetime.
47+
// - `module.0` lives at least as long as the module.
48+
// - `probe()` and `remove()` are static functions.
49+
// - `of_match_table` is either a raw pointer with static lifetime,
50+
// as guaranteed by the [`driver::IdTable`] type, or null.
51+
to_result(unsafe { bindings::__platform_driver_register(reg, module.0) })
52+
}
53+
54+
unsafe fn unregister(reg: *mut bindings::platform_driver) {
55+
// SAFETY: By the safety requirements of this function (defined in the trait definition),
56+
// `reg` was passed (and updated) by a previous successful call to
57+
// `platform_driver_register`.
58+
unsafe { bindings::platform_driver_unregister(reg) };
59+
}
60+
}
61+
62+
impl<T: Driver> Adapter<T> {
63+
fn get_id_info(dev: &Device) -> Option<&'static T::IdInfo> {
64+
let table = T::OF_DEVICE_ID_TABLE?;
65+
66+
// SAFETY: `table` has static lifetime, so it is valid for read. `dev` is guaranteed to be
67+
// valid while it's alive, so is the raw device returned by it.
68+
let id = unsafe { bindings::of_match_device(table.as_ref(), dev.raw_device()) };
69+
if id.is_null() {
70+
return None;
71+
}
72+
73+
// SAFETY: `id` is a pointer within the static table, so it's always valid.
74+
let offset = unsafe { (*id).data };
75+
if offset.is_null() {
76+
return None;
77+
}
78+
79+
// SAFETY: The offset comes from a previous call to `offset_from` in `IdArray::new`, which
80+
// guarantees that the resulting pointer is within the table.
81+
let ptr = unsafe {
82+
id.cast::<u8>()
83+
.offset(offset as _)
84+
.cast::<Option<T::IdInfo>>()
85+
};
86+
87+
// SAFETY: The id table has a static lifetime, so `ptr` is guaranteed to be valid for read.
88+
#[allow(clippy::needless_borrow)]
89+
unsafe {
90+
(&*ptr).as_ref()
91+
}
92+
}
93+
94+
extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
95+
from_result(|| {
96+
// SAFETY: `pdev` is valid by the contract with the C code. `dev` is alive only for the
97+
// duration of this call, so it is guaranteed to remain alive for the lifetime of
98+
// `pdev`.
99+
let mut dev = unsafe { Device::from_ptr(pdev) };
100+
let info = Self::get_id_info(&dev);
101+
let data = T::probe(&mut dev, info)?;
102+
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
103+
unsafe { bindings::platform_set_drvdata(pdev, data.into_foreign() as _) };
104+
Ok(0)
105+
})
106+
}
107+
108+
extern "C" fn remove_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
109+
from_result(|| {
110+
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
111+
let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
112+
// SAFETY:
113+
// - we allocated this pointer using `T::Data::into_foreign`,
114+
// so it is safe to turn back into a `T::Data`.
115+
// - the allocation happened in `probe`, no-one freed the memory,
116+
// `remove` is the canonical kernel location to free driver data. so OK
117+
// to convert the pointer back to a Rust structure here.
118+
let data = unsafe { T::Data::from_foreign(ptr) };
119+
let ret = T::remove(&data);
120+
<T::Data as driver::DeviceRemoval>::device_remove(&data);
121+
ret?;
122+
Ok(0)
123+
})
124+
}
125+
}
126+
127+
/// A platform driver.
128+
pub trait Driver {
129+
/// Data stored on device by driver.
130+
///
131+
/// Corresponds to the data set or retrieved via the kernel's
132+
/// `platform_{set,get}_drvdata()` functions.
133+
///
134+
/// Require that `Data` implements `ForeignOwnable`. We guarantee to
135+
/// never move the underlying wrapped data structure. This allows
136+
type Data: ForeignOwnable + Send + Sync + driver::DeviceRemoval = ();
137+
138+
/// The type holding information about each device id supported by the driver.
139+
type IdInfo: 'static = ();
140+
141+
/// The table of device ids supported by the driver.
142+
const OF_DEVICE_ID_TABLE: Option<driver::IdTable<'static, of::DeviceId, Self::IdInfo>> = None;
143+
144+
/// Platform driver probe.
145+
///
146+
/// Called when a new platform device is added or discovered.
147+
/// Implementers should attempt to initialize the device here.
148+
fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Self::Data>;
149+
150+
/// Platform driver remove.
151+
///
152+
/// Called when a platform device is removed.
153+
/// Implementers should prepare the device for complete removal here.
154+
fn remove(_data: &Self::Data) -> Result {
155+
Ok(())
156+
}
157+
}
158+
159+
/// A platform device.
160+
///
161+
/// # Invariants
162+
///
163+
/// The field `ptr` is non-null and valid for the lifetime of the object.
164+
pub struct Device {
165+
ptr: *mut bindings::platform_device,
166+
}
167+
168+
impl Device {
169+
/// Creates a new device from the given pointer.
170+
///
171+
/// # Safety
172+
///
173+
/// `ptr` must be non-null and valid. It must remain valid for the lifetime of the returned
174+
/// instance.
175+
unsafe fn from_ptr(ptr: *mut bindings::platform_device) -> Self {
176+
// INVARIANT: The safety requirements of the function ensure the lifetime invariant.
177+
Self { ptr }
178+
}
179+
180+
/// Returns id of the platform device.
181+
pub fn id(&self) -> i32 {
182+
// SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid.
183+
unsafe { (*self.ptr).id }
184+
}
185+
}
186+
187+
// SAFETY: The device returned by `raw_device` is the raw platform device.
188+
unsafe impl device::RawDevice for Device {
189+
fn raw_device(&self) -> *mut bindings::device {
190+
// SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid.
191+
unsafe { &mut (*self.ptr).dev }
192+
}
193+
}
194+
195+
/// Declares a kernel module that exposes a single platform driver.
196+
///
197+
/// # Examples
198+
///
199+
/// ```ignore
200+
/// # use kernel::{platform, define_of_id_table, module_platform_driver};
201+
/// #
202+
/// struct MyDriver;
203+
/// impl platform::Driver for MyDriver {
204+
/// // [...]
205+
/// # fn probe(_dev: &mut platform::Device, _id_info: Option<&Self::IdInfo>) -> Result {
206+
/// # Ok(())
207+
/// # }
208+
/// # define_of_id_table! {(), [
209+
/// # (of::DeviceId::Compatible(b"brcm,bcm2835-rng"), None),
210+
/// # ]}
211+
/// }
212+
///
213+
/// module_platform_driver! {
214+
/// type: MyDriver,
215+
/// name: "module_name",
216+
/// author: "Author name",
217+
/// license: "GPL",
218+
/// }
219+
/// ```
220+
#[macro_export]
221+
macro_rules! module_platform_driver {
222+
($($f:tt)*) => {
223+
$crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
224+
};
225+
}

0 commit comments

Comments
 (0)