Skip to content

Commit 523b4dd

Browse files
authored
Merge pull request #305 from TheSven73/rust-for-linux-pdev-pr4
Simple Rust driver that touches real hardware: PR 4/6
2 parents d8c6b9c + 615cc58 commit 523b4dd

File tree

3 files changed

+96
-11
lines changed

3 files changed

+96
-11
lines changed

drivers/char/hw_random/bcm2835_rng_rust.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@
77

88
use alloc::boxed::Box;
99
use core::pin::Pin;
10-
use kernel::of::OfMatchTable;
11-
use kernel::platdev::PlatformDriver;
12-
use kernel::prelude::*;
13-
use kernel::{c_str, platdev};
10+
use kernel::{
11+
file::File,
12+
file_operations::{FileOpener, FileOperations},
13+
io_buffer::IoBufferWriter,
14+
miscdev,
15+
of::OfMatchTable,
16+
platdev::PlatformDriver,
17+
prelude::*,
18+
{c_str, platdev},
19+
};
1420

1521
module! {
1622
type: RngModule,
@@ -20,15 +26,41 @@ module! {
2026
license: b"GPL v2",
2127
}
2228

29+
struct RngDevice;
30+
31+
impl FileOpener<()> for RngDevice {
32+
fn open(_state: &()) -> Result<Self::Wrapper> {
33+
Ok(Box::try_new(RngDevice)?)
34+
}
35+
}
36+
37+
impl FileOperations for RngDevice {
38+
kernel::declare_file_operations!(read);
39+
40+
fn read<T: IoBufferWriter>(&self, _: &File, data: &mut T, offset: u64) -> Result<usize> {
41+
// Succeed if the caller doesn't provide a buffer or if not at the start.
42+
if data.is_empty() || offset != 0 {
43+
return Ok(0);
44+
}
45+
46+
data.write(&0_u32)?;
47+
Ok(4)
48+
}
49+
}
50+
2351
struct RngDriver;
2452

2553
impl PlatformDriver for RngDriver {
26-
fn probe(device_id: i32) -> Result {
54+
type DrvData = Pin<Box<miscdev::Registration<()>>>;
55+
56+
fn probe(device_id: i32) -> Result<Self::DrvData> {
2757
pr_info!("probing discovered hwrng with id {}\n", device_id);
28-
Ok(())
58+
let drv_data =
59+
miscdev::Registration::new_pinned::<RngDevice>(c_str!("rust_hwrng"), None, ())?;
60+
Ok(drv_data)
2961
}
3062

31-
fn remove(device_id: i32) -> Result {
63+
fn remove(device_id: i32, _drv_data: Self::DrvData) -> Result {
3264
pr_info!("removing hwrng with id {}\n", device_id);
3365
Ok(())
3466
}

rust/helpers.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/uio.h>
1010
#include <linux/errname.h>
1111
#include <linux/mutex.h>
12+
#include <linux/platform_device.h>
1213

1314
void rust_helper_BUG(void)
1415
{
@@ -130,6 +131,21 @@ void rust_helper_mutex_lock(struct mutex *lock)
130131
}
131132
EXPORT_SYMBOL_GPL(rust_helper_mutex_lock);
132133

134+
void *
135+
rust_helper_platform_get_drvdata(const struct platform_device *pdev)
136+
{
137+
return platform_get_drvdata(pdev);
138+
}
139+
EXPORT_SYMBOL_GPL(rust_helper_platform_get_drvdata);
140+
141+
void
142+
rust_helper_platform_set_drvdata(struct platform_device *pdev,
143+
void *data)
144+
{
145+
return platform_set_drvdata(pdev, data);
146+
}
147+
EXPORT_SYMBOL_GPL(rust_helper_platform_set_drvdata);
148+
133149
/* We use bindgen's --size_t-is-usize option to bind the C size_t type
134150
* as the Rust usize type, so we can use it in contexts where Rust
135151
* expects a usize like slice (array) indices. usize is defined to be

rust/kernel/platdev.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,31 @@ pub struct Registration {
3030
// (it is fine for multiple threads to have a shared reference to it).
3131
unsafe impl Sync for Registration {}
3232

33+
extern "C" {
34+
#[allow(improper_ctypes)]
35+
fn rust_helper_platform_get_drvdata(
36+
pdev: *const bindings::platform_device,
37+
) -> *mut c_types::c_void;
38+
39+
#[allow(improper_ctypes)]
40+
fn rust_helper_platform_set_drvdata(
41+
pdev: *mut bindings::platform_device,
42+
data: *mut c_types::c_void,
43+
);
44+
}
45+
3346
extern "C" fn probe_callback<P: PlatformDriver>(
3447
pdev: *mut bindings::platform_device,
3548
) -> c_types::c_int {
3649
from_kernel_result! {
3750
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
3851
let device_id = unsafe { (*pdev).id };
39-
P::probe(device_id)?;
52+
let drv_data = P::probe(device_id)?;
53+
let drv_data = drv_data.into_pointer() as *mut c_types::c_void;
54+
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
55+
unsafe {
56+
rust_helper_platform_set_drvdata(pdev, drv_data);
57+
}
4058
Ok(0)
4159
}
4260
}
@@ -47,7 +65,16 @@ extern "C" fn remove_callback<P: PlatformDriver>(
4765
from_kernel_result! {
4866
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
4967
let device_id = unsafe { (*pdev).id };
50-
P::remove(device_id)?;
68+
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
69+
let ptr = unsafe { rust_helper_platform_get_drvdata(pdev) };
70+
// SAFETY:
71+
// - we allocated this pointer using `P::DrvData::into_pointer`,
72+
// so it is safe to turn back into a `P::DrvData`.
73+
// - the allocation happened in `probe`, no-one freed the memory,
74+
// `remove` is the canonical kernel location to free driver data. so OK
75+
// to convert the pointer back to a Rust structure here.
76+
let drv_data = unsafe { P::DrvData::from_pointer(ptr) };
77+
P::remove(device_id, drv_data)?;
5178
Ok(0)
5279
}
5380
}
@@ -124,15 +151,25 @@ impl Drop for Registration {
124151
///
125152
/// Implement this trait whenever you create a platform driver.
126153
pub trait PlatformDriver {
154+
/// Device driver data.
155+
///
156+
/// Corresponds to the data set or retrieved via the kernel's
157+
/// `platform_{set,get}_drvdata()` functions.
158+
///
159+
/// Require that `DrvData` implements `PointerWrapper`. We guarantee to
160+
/// never move the underlying wrapped data structure. This allows
161+
/// driver writers to use pinned or self-referential data structures.
162+
type DrvData: PointerWrapper;
163+
127164
/// Platform driver probe.
128165
///
129166
/// Called when a new platform device is added or discovered.
130167
/// Implementers should attempt to initialize the device here.
131-
fn probe(device_id: i32) -> Result;
168+
fn probe(device_id: i32) -> Result<Self::DrvData>;
132169

133170
/// Platform driver remove.
134171
///
135172
/// Called when a platform device is removed.
136173
/// Implementers should prepare the device for complete removal here.
137-
fn remove(device_id: i32) -> Result;
174+
fn remove(device_id: i32, drv_data: Self::DrvData) -> Result;
138175
}

0 commit comments

Comments
 (0)