The I2C abstraction can register an I2C *client* driver (i2c::Driver), but not provide a bus. A driver that presents a virtual I2C bus and services the transfers itself -- e.g. DisplayLink's evdi, which exposes a DDC/CI channel so userspace monitor-control tools can reach the display -- needs the controller side.
Add i2c::BusController (a trait with master_xfer/functionality over a driver-supplied Context) and i2c::BusAdapter, which fills a struct i2c_adapter whose i2c_algorithm dispatches to the trait via trampolines, calls i2c_add_adapter() on creation and i2c_del_adapter() on drop. i2c::Msg is a #[repr(transparent)] view over struct i2c_msg exposing addr/flags/is_read and the payload as a (mutable) byte slice, so a transfer routine needs no unsafe. FUNC_I2C is exported for the common functionality() return. Signed-off-by: Mike Lothian <[email protected]> Assisted-by: Claude:claude-opus-4-8 [Claude-Code] --- rust/kernel/i2c.rs | 192 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 624b971ca8b0..5cde59a3ce46 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -601,3 +601,195 @@ 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 {} + +// =========================================================================== +// I2C adapter (bus controller / provider) side +// =========================================================================== + +/// A single I2C message presented to a [`BusController`]'s transfer routine. +/// +/// Wraps a `struct i2c_msg`; `#[repr(transparent)]` so a C `i2c_msg` array can be viewed directly +/// as a `[Msg]`. +#[repr(transparent)] +pub struct Msg(Opaque<bindings::i2c_msg>); + +impl Msg { + /// The 7-bit (or 10-bit) target address. + #[inline] + pub fn addr(&self) -> u16 { + // SAFETY: `self.0` is a valid `i2c_msg` for the callback's duration. + unsafe { (*self.0.get()).addr } + } + + /// The message flags (`I2C_M_*`). + #[inline] + pub fn flags(&self) -> u16 { + // SAFETY: as above. + unsafe { (*self.0.get()).flags } + } + + /// Whether this is a read (`I2C_M_RD`) rather than a write. + #[inline] + pub fn is_read(&self) -> bool { + self.flags() & (bindings::I2C_M_RD as u16) != 0 + } + + /// The message length in bytes. + #[inline] + pub fn len(&self) -> usize { + // SAFETY: as above. + unsafe { (*self.0.get()).len as usize } + } + + /// Whether the message is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// The message payload of a write, as a read-only slice. + #[inline] + pub fn buf(&self) -> &[u8] { + // SAFETY: `buf` points to `len` bytes valid for the callback's duration. + unsafe { core::slice::from_raw_parts((*self.0.get()).buf, self.len()) } + } + + /// The message buffer of a read, as a mutable slice to fill with the reply. + #[inline] + pub fn buf_mut(&mut self) -> &mut [u8] { + // SAFETY: `buf` points to `len` writable bytes valid for the callback's duration. + unsafe { core::slice::from_raw_parts_mut((*self.0.get()).buf, self.len()) } + } +} + +/// `I2C_FUNC_I2C`: the adapter supports plain I2C (not just SMBus) transfers. +pub const FUNC_I2C: u32 = bindings::I2C_FUNC_I2C; + +/// A Rust-implemented I2C bus adapter (the controller/provider side). +/// +/// This is the counterpart to the [`Driver`] trait above: rather than binding to an I2C client on +/// an existing bus, an implementer *is* a bus and services transfers. Virtual buses -- e.g. the +/// DDC/CI channel DisplayLink's evdi exposes so userspace monitor-control tools can reach the +/// display -- use this. +pub trait BusController: 'static { + /// Per-adapter driver context, made available to the transfer callbacks. + type Context: Send + Sync; + + /// Transfer `msgs` on the bus, returning the number of messages successfully transferred + /// (as the C `master_xfer` does), or an error. + fn master_xfer(ctx: &Self::Context, msgs: &mut [Msg]) -> Result<usize>; + + /// Report the bus functionality bitmask (`I2C_FUNC_*`). + fn functionality(ctx: &Self::Context) -> u32; +} + +/// An owned, registered I2C adapter driven by a [`BusController`]. +/// +/// [`BusAdapter::new`] fills a `struct i2c_adapter` (pointing its algorithm at trampolines that +/// dispatch to `T`) and calls `i2c_add_adapter()`; dropping it calls `i2c_del_adapter()`. +#[pin_data(PinnedDrop)] +pub struct BusAdapter<T: BusController> { + #[pin] + adapter: Opaque<bindings::i2c_adapter>, + #[pin] + algo: Opaque<bindings::i2c_algorithm>, + ctx: T::Context, + registered: core::sync::atomic::AtomicBool, +} + +// SAFETY: the adapter is internally synchronized by the I2C core; `ctx` is `Send + Sync`. +unsafe impl<T: BusController> Send for BusAdapter<T> {} +// SAFETY: see `Send`. +unsafe impl<T: BusController> Sync for BusAdapter<T> {} + +impl<T: BusController> BusAdapter<T> { + /// # Safety + /// `adap` is a valid adapter whose `algo_data` points to a live `T::Context`. + unsafe extern "C" fn xfer_trampoline( + adap: *mut bindings::i2c_adapter, + msgs: *mut bindings::i2c_msg, + num: crate::ffi::c_int, + ) -> crate::ffi::c_int { + // SAFETY: by the invariant established in `new`, `algo_data` points to the `T::Context`. + let ctx = unsafe { &*((*adap).algo_data as *const T::Context) }; + // SAFETY: the I2C core passes `num` valid `i2c_msg`s; `Msg` is `#[repr(transparent)]`. + let slice = unsafe { core::slice::from_raw_parts_mut(msgs.cast::<Msg>(), num as usize) }; + match T::master_xfer(ctx, slice) { + Ok(n) => n as crate::ffi::c_int, + Err(e) => e.to_errno(), + } + } + + /// # Safety + /// `adap` is a valid adapter whose `algo_data` points to a live `T::Context`. + unsafe extern "C" fn func_trampoline(adap: *mut bindings::i2c_adapter) -> u32 { + // SAFETY: by the invariant established in `new`, `algo_data` points to the `T::Context`. + let ctx = unsafe { &*((*adap).algo_data as *const T::Context) }; + T::functionality(ctx) + } + + /// Create and register a new I2C adapter named `name`, parented to `parent`, with driver + /// context `ctx`. + pub fn new( + name: &CStr, + parent: &device::Device, + ctx: T::Context, + ) -> Result<Pin<KBox<Self>>> { + let this = KBox::try_pin_init( + try_pin_init!(Self { + adapter <- Opaque::ffi_init(|slot: *mut bindings::i2c_adapter| { + // SAFETY: `slot` is valid, uninitialized storage; a zeroed adapter is a valid + // starting point (self-referential fields are set below, after pinning). + unsafe { *slot = bindings::i2c_adapter::default() }; + }), + algo <- Opaque::ffi_init(|slot: *mut bindings::i2c_algorithm| { + // SAFETY: `slot` is valid, uninitialized storage. + unsafe { + *slot = bindings::i2c_algorithm::default(); + (*slot).__bindgen_anon_1.master_xfer = Some(Self::xfer_trampoline); + (*slot).functionality = Some(Self::func_trampoline); + } + }), + ctx: ctx, + registered: core::sync::atomic::AtomicBool::new(false), + }), + GFP_KERNEL, + )?; + + // `this` now has a stable address; wire the self-referential adapter fields and register. + let adap = this.adapter.get(); + // Copy the name without its NUL into the fixed 48-byte array (capped at 47 so the + // already-zeroed array stays NUL-terminated even if the name is over-long). + let name_bytes = name.to_bytes(); + let n = core::cmp::min(name_bytes.len(), 47); + // SAFETY: `adap`/`algo`/`ctx` live for `this`'s (pinned) lifetime; `name_bytes` is valid + // for `n <= 47` bytes; `parent` is a valid device. + unsafe { + (*adap).algo = this.algo.get(); + (*adap).algo_data = (&this.ctx as *const T::Context).cast_mut().cast(); + core::ptr::copy_nonoverlapping( + name_bytes.as_ptr(), + (*adap).name.as_mut_ptr().cast::<u8>(), + n, + ); + (*adap).dev.parent = parent.as_raw(); + } + + // SAFETY: `adap` is a fully-initialized adapter. + to_result(unsafe { bindings::i2c_add_adapter(adap) })?; + this.registered + .store(true, core::sync::atomic::Ordering::Release); + Ok(this) + } +} + +#[pinned_drop] +impl<T: BusController> PinnedDrop for BusAdapter<T> { + fn drop(self: Pin<&mut Self>) { + if self.registered.load(core::sync::atomic::Ordering::Acquire) { + // SAFETY: the adapter was successfully registered with `i2c_add_adapter` and has not + // been deleted yet. + unsafe { bindings::i2c_del_adapter(self.adapter.get()) }; + } + } +} -- 2.55.0
