mirror of https://github.com/torvalds/linux.git
rust: i2c: add manual I2C device creation abstractions
In addition to the basic I2C device support, add rust abstractions
upon `i2c_new_client_device`/`i2c_unregister_device` C functions.
Implement the core abstractions needed for manual creation/deletion
of I2C devices, including:
* `i2c::Registration` — a NonNull pointer created by the function
`i2c_new_client_device`
* `i2c::I2cAdapter` — a ref counted wrapper around `struct i2c_adapter`
* `i2c::I2cBoardInfo` — a safe wrapper around `struct i2c_board_info`
Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Igor Korotin <igor.korotin.linux@gmail.com>
Link: https://patch.msgid.link/20251116162154.171493-1-igor.korotin.linux@gmail.com
[ Remove unnecessary safety comment. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
parent
57c5bd9aee
commit
f3cc26a417
|
|
@ -11,6 +11,7 @@
|
||||||
RawDeviceId,
|
RawDeviceId,
|
||||||
RawDeviceIdIndex, //
|
RawDeviceIdIndex, //
|
||||||
},
|
},
|
||||||
|
devres::Devres,
|
||||||
driver,
|
driver,
|
||||||
error::*,
|
error::*,
|
||||||
of,
|
of,
|
||||||
|
|
@ -23,9 +24,14 @@
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ptr::NonNull, //
|
ptr::{
|
||||||
|
from_ref,
|
||||||
|
NonNull, //
|
||||||
|
}, //
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use kernel::types::ARef;
|
||||||
|
|
||||||
/// An I2C device id table.
|
/// An I2C device id table.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -354,6 +360,100 @@ fn unbind(dev: &I2cClient<device::Core>, this: Pin<&Self>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The i2c adapter representation.
|
||||||
|
///
|
||||||
|
/// This structure represents the Rust abstraction for a C `struct i2c_adapter`. The
|
||||||
|
/// implementation abstracts the usage of an existing C `struct i2c_adapter` that
|
||||||
|
/// gets passed from the C side
|
||||||
|
///
|
||||||
|
/// # Invariants
|
||||||
|
///
|
||||||
|
/// A [`I2cAdapter`] instance represents a valid `struct i2c_adapter` created by the C portion of
|
||||||
|
/// the kernel.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct I2cAdapter<Ctx: device::DeviceContext = device::Normal>(
|
||||||
|
Opaque<bindings::i2c_adapter>,
|
||||||
|
PhantomData<Ctx>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<Ctx: device::DeviceContext> I2cAdapter<Ctx> {
|
||||||
|
fn as_raw(&self) -> *mut bindings::i2c_adapter {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl I2cAdapter {
|
||||||
|
/// Returns the I2C Adapter index.
|
||||||
|
#[inline]
|
||||||
|
pub fn index(&self) -> i32 {
|
||||||
|
// SAFETY: `self.as_raw` is a valid pointer to a `struct i2c_adapter`.
|
||||||
|
unsafe { (*self.as_raw()).nr }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets pointer to an `i2c_adapter` by index.
|
||||||
|
pub fn get(index: i32) -> Result<ARef<Self>> {
|
||||||
|
// SAFETY: `index` must refer to a valid I2C adapter; the kernel
|
||||||
|
// guarantees that `i2c_get_adapter(index)` returns either a valid
|
||||||
|
// pointer or NULL. `NonNull::new` guarantees the correct check.
|
||||||
|
let adapter = NonNull::new(unsafe { bindings::i2c_get_adapter(index) }).ok_or(ENODEV)?;
|
||||||
|
|
||||||
|
// SAFETY: `adapter` is non-null and points to a live `i2c_adapter`.
|
||||||
|
// `I2cAdapter` is #[repr(transparent)], so this cast is valid.
|
||||||
|
Ok(unsafe { (&*adapter.as_ptr().cast::<I2cAdapter<device::Normal>>()).into() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: `I2cAdapter` is a transparent wrapper of a type that doesn't depend on
|
||||||
|
// `I2cAdapter`'s generic argument.
|
||||||
|
kernel::impl_device_context_deref!(unsafe { I2cAdapter });
|
||||||
|
kernel::impl_device_context_into_aref!(I2cAdapter);
|
||||||
|
|
||||||
|
// SAFETY: Instances of `I2cAdapter` are always reference-counted.
|
||||||
|
unsafe impl crate::types::AlwaysRefCounted for I2cAdapter {
|
||||||
|
fn inc_ref(&self) {
|
||||||
|
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
|
||||||
|
unsafe { bindings::i2c_get_adapter(self.index()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn dec_ref(obj: NonNull<Self>) {
|
||||||
|
// SAFETY: The safety requirements guarantee that the refcount is non-zero.
|
||||||
|
unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The i2c board info representation
|
||||||
|
///
|
||||||
|
/// This structure represents the Rust abstraction for a C `struct i2c_board_info` structure,
|
||||||
|
/// which is used for manual I2C client creation.
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct I2cBoardInfo(bindings::i2c_board_info);
|
||||||
|
|
||||||
|
impl I2cBoardInfo {
|
||||||
|
const I2C_TYPE_SIZE: usize = 20;
|
||||||
|
/// Create a new [`I2cBoardInfo`] for a kernel driver.
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn new(type_: &'static CStr, addr: u16) -> Self {
|
||||||
|
build_assert!(
|
||||||
|
type_.len_with_nul() <= Self::I2C_TYPE_SIZE,
|
||||||
|
"Type exceeds 20 bytes"
|
||||||
|
);
|
||||||
|
let src = type_.as_bytes_with_nul();
|
||||||
|
let mut i2c_board_info: bindings::i2c_board_info = pin_init::zeroed();
|
||||||
|
let mut i: usize = 0;
|
||||||
|
while i < src.len() {
|
||||||
|
i2c_board_info.type_[i] = src[i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_board_info.addr = addr;
|
||||||
|
Self(i2c_board_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_raw(&self) -> *const bindings::i2c_board_info {
|
||||||
|
from_ref(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The i2c client representation.
|
/// The i2c client representation.
|
||||||
///
|
///
|
||||||
/// This structure represents the Rust abstraction for a C `struct i2c_client`. The
|
/// This structure represents the Rust abstraction for a C `struct i2c_client`. The
|
||||||
|
|
@ -432,3 +532,54 @@ unsafe impl Send for I2cClient {}
|
||||||
// SAFETY: `I2cClient` can be shared among threads because all methods of `I2cClient`
|
// SAFETY: `I2cClient` can be shared among threads because all methods of `I2cClient`
|
||||||
// (i.e. `I2cClient<Normal>) are thread safe.
|
// (i.e. `I2cClient<Normal>) are thread safe.
|
||||||
unsafe impl Sync for I2cClient {}
|
unsafe impl Sync for I2cClient {}
|
||||||
|
|
||||||
|
/// The registration of an i2c client device.
|
||||||
|
///
|
||||||
|
/// This type represents the registration of a [`struct i2c_client`]. When an instance of this
|
||||||
|
/// type is dropped, its respective i2c client device will be unregistered from the system.
|
||||||
|
///
|
||||||
|
/// # Invariants
|
||||||
|
///
|
||||||
|
/// `self.0` always holds a valid pointer to an initialized and registered
|
||||||
|
/// [`struct i2c_client`].
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Registration(NonNull<bindings::i2c_client>);
|
||||||
|
|
||||||
|
impl Registration {
|
||||||
|
/// The C `i2c_new_client_device` function wrapper for manual I2C client creation.
|
||||||
|
pub fn new<'a>(
|
||||||
|
i2c_adapter: &I2cAdapter,
|
||||||
|
i2c_board_info: &I2cBoardInfo,
|
||||||
|
parent_dev: &'a device::Device<device::Bound>,
|
||||||
|
) -> impl PinInit<Devres<Self>, Error> + 'a {
|
||||||
|
Devres::new(parent_dev, Self::try_new(i2c_adapter, i2c_board_info))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_new(i2c_adapter: &I2cAdapter, i2c_board_info: &I2cBoardInfo) -> Result<Self> {
|
||||||
|
// SAFETY: the kernel guarantees that `i2c_new_client_device()` returns either a valid
|
||||||
|
// pointer or NULL. `from_err_ptr` separates errors. Following `NonNull::new`
|
||||||
|
// checks for NULL.
|
||||||
|
let raw_dev = from_err_ptr(unsafe {
|
||||||
|
bindings::i2c_new_client_device(i2c_adapter.as_raw(), i2c_board_info.as_raw())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let dev_ptr = NonNull::new(raw_dev).ok_or(ENODEV)?;
|
||||||
|
|
||||||
|
Ok(Self(dev_ptr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Registration {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// SAFETY: `Drop` is only called for a valid `Registration`, which by invariant
|
||||||
|
// always contains a non-null pointer to an `i2c_client`.
|
||||||
|
unsafe { bindings::i2c_unregister_device(self.0.as_ptr()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: A `Registration` of a `struct i2c_client` can be released from any thread.
|
||||||
|
unsafe impl Send for Registration {}
|
||||||
|
|
||||||
|
// SAFETY: `Registration` offers no interior mutability (no mutation through &self
|
||||||
|
// and no mutable access is exposed)
|
||||||
|
unsafe impl Sync for Registration {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue