Skip to content

Commit 7581a40

Browse files
kloenkFabo
authored andcommitted
rust: add I2C driver module macro
Signed-off-by: Finn Behrens <[email protected]> Signed-off-by: Fabien Parent <[email protected]>
1 parent aeefa92 commit 7581a40

File tree

4 files changed

+263
-0
lines changed

4 files changed

+263
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
#include <linux/fdtable.h>
1515
#include <linux/file.h>
1616
#include <linux/fs.h>
17+
#include <linux/i2c.h>
1718
#include <linux/jiffies.h>
1819
#include <linux/mdio.h>
20+
#include <linux/pid_namespace.h>
1921
#include <linux/phy.h>
2022
#include <linux/pid_namespace.h>
2123
#include <linux/platform_device.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/err.h>
2929
#include <linux/errname.h>
3030
#include <linux/fs.h>
31+
#include <linux/i2c.h>
3132
#include <linux/mutex.h>
3233
#include <linux/of_device.h>
3334
#include <linux/platform_device.h>
@@ -285,6 +286,18 @@ void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
285286
}
286287
EXPORT_SYMBOL_GPL(rust_helper_platform_set_drvdata);
287288

289+
void *rust_helper_i2c_get_clientdata(const struct i2c_client *client)
290+
{
291+
return i2c_get_clientdata(client);
292+
}
293+
EXPORT_SYMBOL_GPL(rust_helper_i2c_get_clientdata);
294+
295+
void rust_helper_i2c_set_clientdata(struct i2c_client *client, void *data)
296+
{
297+
i2c_set_clientdata(client, data);
298+
}
299+
EXPORT_SYMBOL_GPL(rust_helper_i2c_set_clientdata);
300+
288301
/*
289302
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
290303
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/i2c.rs

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! I2C devices and drivers.
4+
//!
5+
//! C header: [`include/linux/i2c.h`](../../../../include/linux/i2c.h)
6+
7+
use crate::{
8+
bindings,
9+
device::RawDevice,
10+
driver::{self, RawDeviceId},
11+
error::{from_result, to_result, Result},
12+
str::{BStr, CStr},
13+
types::ForeignOwnable,
14+
ThisModule,
15+
};
16+
17+
/// An I2C device id.
18+
#[derive(Clone, Copy)]
19+
pub struct DeviceId(pub &'static BStr);
20+
21+
// SAFETY: `ZERO` is all zeroed-out and `to_rawid` stores `offset` in `i2c_device_id::driver_data`.
22+
unsafe impl crate::driver::RawDeviceId for DeviceId {
23+
type RawType = bindings::i2c_device_id;
24+
const ZERO: Self::RawType = bindings::i2c_device_id {
25+
name: [0; 20],
26+
driver_data: 0,
27+
};
28+
}
29+
30+
impl DeviceId {
31+
#[doc(hidden)]
32+
pub const fn to_rawid(&self, offset: isize) -> <Self as RawDeviceId>::RawType {
33+
let mut id = Self::ZERO;
34+
let mut i = 0;
35+
while i < self.0.len() {
36+
id.name[i] = self.0[i] as _;
37+
i += 1;
38+
}
39+
id.name[i] = b'\0' as _;
40+
id.driver_data = offset as _;
41+
id
42+
}
43+
}
44+
45+
/// Defines a const I2C device id table that also carries per-entry data/context/info.
46+
///
47+
/// The name of the const is `I2C_DEVICE_ID_TABLE`.
48+
///
49+
/// # Examples
50+
///
51+
/// ```
52+
/// use kernel::i2c;
53+
///
54+
/// kernel::define_i2c_id_table! {MY_ID_TABLE, u32, [
55+
/// (i2c::DeviceId(b"test-device1"), Some(0xff)),
56+
/// (i2c::DeviceId(b"test-device2"), None),
57+
/// ]};
58+
/// ```
59+
#[macro_export]
60+
macro_rules! define_i2c_id_table {
61+
($name:ident, $data_type:ty, $($t:tt)*) => {
62+
$crate::define_id_array!($name, $crate::i2c::DeviceId, $data_type, $($t)*);
63+
};
64+
}
65+
///
66+
/// Convenience macro to declare which device ID table to use for a bus driver.
67+
#[macro_export]
68+
macro_rules! driver_i2c_id_table {
69+
($name:expr) => {
70+
$crate::driver_id_table!(
71+
I2C_DEVICE_ID_TABLE,
72+
$crate::i2c::DeviceId,
73+
Self::IdInfo,
74+
$name
75+
);
76+
};
77+
}
78+
79+
/// Declare a device ID table as a module-level table. This creates the necessary module alias
80+
/// entries to enable module autoloading.
81+
#[macro_export]
82+
macro_rules! module_i2c_id_table {
83+
($item_name:ident, $table_name:ident) => {
84+
$crate::module_id_table!($item_name, "i2c", $crate::i2c::DeviceId, $table_name);
85+
};
86+
}
87+
88+
/// An adapter for the registration of i2c drivers.
89+
pub struct Adapter<T: Driver>(T);
90+
91+
impl<T: Driver> driver::DriverOps for Adapter<T> {
92+
type RegType = bindings::i2c_driver;
93+
94+
unsafe fn register(
95+
reg: *mut Self::RegType,
96+
name: &'static CStr,
97+
module: &'static ThisModule,
98+
) -> Result {
99+
// SAFETY: By the safety requirements of this function (defined in the trait definition),
100+
// `reg` is non-null and valid.
101+
let i2cdrv = unsafe { &mut *reg };
102+
103+
i2cdrv.driver.name = name.as_char_ptr();
104+
i2cdrv.probe = Some(Self::probe_callback);
105+
i2cdrv.remove = Some(Self::remove_callback);
106+
if let Some(t) = T::I2C_DEVICE_ID_TABLE {
107+
i2cdrv.id_table = t.as_ref();
108+
}
109+
110+
// SAFETY:
111+
// - `pdrv` lives at least until the call to `platform_driver_unregister()` returns.
112+
// - `name` pointer has static lifetime.
113+
// - `module.0` lives at least as long as the module.
114+
// - `probe()` and `remove()` are static functions.
115+
// - `of_match_table` is either a raw pointer with static lifetime,
116+
// as guaranteed by the [`driver::IdTable`] type, or null.
117+
to_result(unsafe { bindings::i2c_register_driver(module.0, reg) })
118+
}
119+
120+
unsafe fn unregister(reg: *mut Self::RegType) {
121+
// SAFETY: By the safety requirements of this function (defined in the trait definition),
122+
// `reg` was passed (and updated) by a previous successful call to
123+
// `i2c_register_driver`.
124+
unsafe { bindings::i2c_del_driver(reg) };
125+
}
126+
}
127+
128+
impl<T: Driver> Adapter<T> {
129+
extern "C" fn probe_callback(i2c: *mut bindings::i2c_client) -> core::ffi::c_int {
130+
from_result(|| {
131+
let mut client = unsafe { Client::from_ptr(i2c) };
132+
let data = T::probe(&mut client)?;
133+
134+
// SAFETY: `i2c` is guaranteed to be a valid, non-null pointer.
135+
unsafe { bindings::i2c_set_clientdata(i2c, data.into_foreign() as _) };
136+
Ok(0)
137+
})
138+
}
139+
140+
extern "C" fn remove_callback(i2c: *mut bindings::i2c_client) {
141+
// SAFETY: `i2c` is guarenteed to be a valid, non-null pointer
142+
let ptr = unsafe { bindings::i2c_get_clientdata(i2c) };
143+
// SAFETY:
144+
// - we allocated this pointer using `T::Data::into_pointer`,
145+
// so it is safe to turn back into a `T::Data`.
146+
// - the allocation happened in `probe`, no-one freed the memory,
147+
// `remove` is the canonical kernel location to free driver data. so OK
148+
// to convert the pointer back to a Rust structure here.
149+
let data = unsafe { T::Data::from_foreign(ptr) };
150+
T::remove(&data);
151+
<T::Data as driver::DeviceRemoval>::device_remove(&data);
152+
}
153+
}
154+
155+
/// A I2C driver.
156+
pub trait Driver {
157+
/// Data stored on device by driver.
158+
///
159+
/// Corresponds to the data set or retrieved via the kernel's
160+
/// `i2c_{set,get}_clientdata()` functions.
161+
///
162+
/// Require that `Data` implements `PointerWrapper`. We guarantee to
163+
/// never move the underlying wrapped data structure. This allows
164+
type Data: ForeignOwnable + Send + Sync + driver::DeviceRemoval = ();
165+
166+
/// The type holding information about each device id supported by the driver.
167+
type IdInfo: 'static = ();
168+
169+
/// The table of device ids supported by the driver.
170+
const I2C_DEVICE_ID_TABLE: Option<driver::IdTable<'static, DeviceId, Self::IdInfo>> = None;
171+
172+
/// I2C driver probe.
173+
///
174+
/// Called when a new i2c client is added or discovered.
175+
/// Implementers should attempt to initialize the client here.
176+
fn probe(client: &mut Client) -> Result<Self::Data>;
177+
178+
/// I2C driver remove.
179+
///
180+
/// Called when an i2c client is removed.
181+
fn remove(_data: &Self::Data) {}
182+
}
183+
184+
/// A I2C Client device.
185+
///
186+
/// # Invariants
187+
///
188+
/// The field `ptr` is non-null and valid for the lifetime of the object.
189+
pub struct Client {
190+
ptr: *mut bindings::i2c_client,
191+
}
192+
193+
impl Client {
194+
/// Creates a new client from the given pointer.
195+
///
196+
/// # Safety
197+
///
198+
/// `ptr` must be non-null and valid. It must remain valid for the lifetime of the returned
199+
/// instance.
200+
unsafe fn from_ptr(ptr: *mut bindings::i2c_client) -> Self {
201+
// INVARIANT: The safety requirements of the function ensure the lifetime invariant.
202+
Self { ptr }
203+
}
204+
}
205+
206+
unsafe impl RawDevice for Client {
207+
fn raw_device(&self) -> *mut bindings::device {
208+
// SAFETY: By the type invariants, we know that `self.ptr` is non-null and valid.
209+
unsafe { &mut (*self.ptr).dev }
210+
}
211+
}
212+
213+
/// Declares a kernel module that exposes a single i2c driver.
214+
///
215+
/// # Examples
216+
///
217+
/// ```ignore
218+
/// # use kernel::{i2c, define_i2c_id_table, module_i2c_driver};
219+
/// kernel::module_i2c_id_table!(MOD_TABLE, I2C_CLIENT_I2C_ID_TABLE);
220+
/// kernel::define_i2c_id_table! {I2C_CLIENT_I2C_ID_TABLE, (), [
221+
/// (i2c::DeviceId(b"fpga"), None),
222+
/// ]}
223+
/// struct MyDriver;
224+
/// impl i2c::Driver for MyDriver {
225+
/// kernel::driver_i2c_id_table!(I2C_CLIENT_I2C_ID_TABLE);
226+
/// // [...]
227+
/// # fn probe(_client: &mut i2c::Client) -> Result {
228+
/// # Ok(())
229+
/// # }
230+
/// }
231+
///
232+
/// module_i2c_driver! {
233+
/// type: MyDriver,
234+
/// name: "module_name",
235+
/// author: "Author name",
236+
/// license: "GPL",
237+
/// }
238+
/// ```
239+
#[macro_export]
240+
macro_rules! module_i2c_driver {
241+
($($f:tt)*) => {
242+
$crate::module_driver!(<T>, $crate::i2c::Adapter<T>, { $($f)* });
243+
};
244+
}

rust/kernel/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#![feature(coerce_unsized)]
1818
#![feature(const_refs_to_cell)]
1919
#![feature(dispatch_from_dyn)]
20+
#![feature(doc_cfg)]
2021
#![feature(new_uninit)]
2122
#![feature(receiver_trait)]
2223
#![feature(unsize)]
@@ -38,6 +39,9 @@ pub mod device;
3839
pub mod driver;
3940
pub mod error;
4041
pub mod file;
42+
#[cfg(any(CONFIG_I2C, doc))]
43+
#[doc(cfg(CONFIG_I2C))]
44+
pub mod i2c;
4145
pub mod init;
4246
pub mod ioctl;
4347
#[cfg(CONFIG_KUNIT)]

0 commit comments

Comments
 (0)