Skip to content

Commit b087b60

Browse files
kloenkFabo
authored andcommitted
rust: add I2C client abstraction
Add a new Rust abstraction that allows writing drivers for I2C based devices. Signed-off-by: Fiona Behrens <[email protected]> Co-developed-by: Fabien Parent <[email protected]> Signed-off-by: Fabien Parent <[email protected]>
1 parent faf3c48 commit b087b60

File tree

5 files changed

+245
-0
lines changed

5 files changed

+245
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/errname.h>
1414
#include <linux/ethtool.h>
1515
#include <linux/firmware.h>
16+
#include <linux/i2c.h>
1617
#include <linux/jiffies.h>
1718
#include <linux/mdio.h>
1819
#include <linux/phy.h>

rust/helpers/helpers.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "build_bug.c"
1414
#include "device.c"
1515
#include "err.c"
16+
#include "i2c.c"
1617
#include "io.c"
1718
#include "kunit.c"
1819
#include "mutex.c"

rust/helpers/i2c.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <linux/i2c.h>
4+
5+
void *rust_helper_i2c_get_clientdata(const struct i2c_client *client)
6+
{
7+
return i2c_get_clientdata(client);
8+
}
9+
10+
void rust_helper_i2c_set_clientdata(struct i2c_client *client, void *data)
11+
{
12+
i2c_set_clientdata(client, data);
13+
}

rust/kernel/i2c.rs

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

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub mod driver;
4545
pub mod error;
4646
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
4747
pub mod firmware;
48+
#[cfg(CONFIG_I2C)]
49+
pub mod i2c;
4850
pub mod init;
4951
pub mod ioctl;
5052
#[cfg(CONFIG_KUNIT)]

0 commit comments

Comments
 (0)