From nobody Sun Feb 8 19:54:49 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 40F6C1DF74F; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; cv=none; b=u3/dZ3I8VGlHc2iFPycA0ufqA9dFUGy9+NAsoh/HeJBR6zLmekVyBXzSgMCij4BkEvHeKPgMPMz0b5YPOsFWdy01bYv7LsdaUjdKt03m42V6xmQCYWPGWVnVadBhzvi8wF7ztVuTHRVqCqICh9aaoLodcRlSN5S9y/u7imx0XCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868773; c=relaxed/simple; bh=hp23VIwMy10ONa2wQWefma68fpJyhyAlZMhA9o9qHW0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=cnQqeV4dyf7s7VrWOaMu/IjG3fLIGrVA9mQRDC3jzE1aImNzHn/mHpDdqGeQrSRkWNgrYxwtvf+sZT6SiumSsCjMAiPgX2IiDbIRrV3jDJ2tI7fz/lUX0OBxC6HalYTtgiJUV4cHFYtPvhS20Cso076AtlX18diSBYYsY1I3uKI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CSwEV1W1; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CSwEV1W1" Received: by smtp.kernel.org (Postfix) with ESMTPS id 069D2C2BC9E; Sat, 31 Jan 2026 14:12:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1769868773; bh=hp23VIwMy10ONa2wQWefma68fpJyhyAlZMhA9o9qHW0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=CSwEV1W1RVcf1eAfV0QkvqBYzB6YNzu9GHuHHsYxR0KGz/V1AkOMtMO4rYTB0Xzv3 c5OK2TyB8upCDXX3fkuRYE6xU7L0T5DCzMOIDKDqJkEnz/iyb2PG/YVAyhuDN0rja2 Ae8FhLP8C2d7qaWCQ7RcD/qsmskghuk5l+116ugYgBRAck7qVikDrUV7fDZ3NJY3el OnmTCLdqXD7EWJyKoME6ses6+JCMhF/0VL8vhM4rzNR6ohHBz9XiZwnFWXTeW80y+n GrNroMFx1ZP2CpMHAM04sSwmRJ76bWWfELATDEK8UfZDeMcJUmRzw5DXeI6WjmLAgp PL/XLKGpFXbcg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id F02E4D79773; Sat, 31 Jan 2026 14:12:52 +0000 (UTC) From: Igor Korotin via B4 Relay Date: Sat, 31 Jan 2026 14:12:45 +0000 Subject: [PATCH 3/5] rust: i2c: Add I2C Adapter registration abstractions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260131-i2c-adapter-v1-3-5a436e34cd1a@gmail.com> References: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> In-Reply-To: <20260131-i2c-adapter-v1-0-5a436e34cd1a@gmail.com> To: Danilo Krummrich , Daniel Almeida , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Wolfram Sang Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-i2c@vger.kernel.org, markus.probst@posteo.de, Igor Korotin X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1769868772; l=17907; i=igor.korotin.linux@gmail.com; s=20260125; h=from:subject:message-id; bh=O00AxujwITCbYlKLKHLjBJTRZG5L0086MO+yva6+Jr4=; b=LxB+rq1ASkCN+in6cH21y6M6VHepp11Uq292hpMHHaCkpy5Ly9cB5ZYm2tziGKZ6z/8F0CToL IEwMwh1V+a7ACCXsRDws/S+z/zU9cv5JOP+12oQBhNP+CodhGCieLMd X-Developer-Key: i=igor.korotin.linux@gmail.com; a=ed25519; pk=SjZ//wxnynZgJJgTqANyU10A7hdQQdDXLvXIr+zWroE= X-Endpoint-Received: by B4 Relay for igor.korotin.linux@gmail.com/20260125 with auth_id=615 X-Original-From: Igor Korotin Reply-To: igor.korotin.linux@gmail.com From: Igor Korotin Add safe Rust abstractions for I2C adapter registration and management, wrapping the C functions `i2c_add_adapter` and `i2c_del_adapter`. This includes: - Hide `struct i2c_algorithm` unions from bindgen to avoid unnecessary mangles. - Safe wrappers upon `struct i2c_msg` and `struct i2c_smbus_data` - I2C Algorithm wrapper around `struct i2c_algorithm` - I2C Adapter Flags wrapper - Registration/deregistration logic for I2C adapters Signed-off-by: Igor Korotin --- include/linux/i2c.h | 6 + rust/kernel/i2c.rs | 1 + rust/kernel/i2c/adapter.rs | 120 ++++++++++++++++++ rust/kernel/i2c/algo.rs | 300 +++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 427 insertions(+) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 20fd41b51d5c..c176243de254 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -548,18 +548,24 @@ struct i2c_algorithm { * smbus_xfer. If set to NULL, the SMBus protocol is simulated * using common I2C messages. */ +#ifndef __BINDGEN__ union { +#endif int (*xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +#ifndef __BINDGEN__ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); }; union { +#endif int (*xfer_atomic)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +#ifndef __BINDGEN__ int (*master_xfer_atomic)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); }; +#endif int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 0bebfde3e495..af33a3be83c4 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -19,6 +19,7 @@ }; =20 pub mod adapter; +pub mod algo; pub mod client; =20 /// An I2C device id table. diff --git a/rust/kernel/i2c/adapter.rs b/rust/kernel/i2c/adapter.rs index 4a0d2faaf98b..ec64c1327792 100644 --- a/rust/kernel/i2c/adapter.rs +++ b/rust/kernel/i2c/adapter.rs @@ -6,7 +6,9 @@ use crate::{ bindings, device, + devres::Devres, error::*, + i2c::algo::*, prelude::*, types::Opaque, // }; @@ -38,6 +40,15 @@ impl I2cAdapter { pub(super) fn as_raw(&self) -> *mut bindings::i2c_adapter { self.0.get() } + + /// Convert a raw C `struct i2c_adapter` pointer to a `&'a I2cAdapter`. + pub(super) fn from_raw<'a>(ptr: *mut bindings::i2c_adapter) -> &'a Sel= f { + // SAFETY: Callers must ensure that `ptr` is valid, non-null, and = has a non-zero reference + // count, i.e. it must be ensured that the reference count of the = C `struct i2c_adapter` + // `ptr` points to can't drop to zero, for the duration of this fu= nction call and the entire + // duration when the returned reference exists. + unsafe { &*ptr.cast() } + } } =20 impl I2cAdapter { @@ -78,3 +89,112 @@ unsafe fn dec_ref(obj: NonNull) { unsafe { bindings::i2c_put_adapter(obj.as_ref().as_raw()) } } } + +impl AsRef> for I2cAdapter= { + fn as_ref(&self) -> &device::Device { + let raw =3D self.as_raw(); + // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a p= ointer to a valid + // `struct i2c_adapter`. + let dev =3D unsafe { &raw mut (*raw).dev }; + + // SAFETY: `dev` points to a valid `struct device`. + unsafe { device::Device::from_raw(dev) } + } +} + +/// Options for creating an I2C adapter device. +pub struct I2cAdapterOptions { + /// The name of the I2C adapter device. + pub name: &'static CStr, +} + +impl I2cAdapterOptions { + /// Create a raw `struct i2c_adapter` ready for registration. + pub const fn as_raw(self) -> bindings::i2c_adapter { + let mut adapter: bindings::i2c_adapter =3D pin_init::zeroed(); + // TODO: make it some other way... this looks like shit + let src =3D self.name.to_bytes_with_nul(); + let mut i: usize =3D 0; + while i < src.len() { + adapter.name[i] =3D src[i]; + i +=3D 1; + } + adapter.algo =3D I2cAlgorithmVTable::::build(); + + adapter + } +} + +/// A registration of a I2C Adapter. +/// +/// # Invariants +/// +/// - `inner` contains a `struct i2c_adapter` that is registered using +/// `i2c_add_adapter()`. +/// - This registration remains valid for the entire lifetime of the +/// [`i2c::adapter::Registration`] instance. +/// - Deregistration occurs exactly once in [`Drop`] via `i2c_del_adapter(= )`. +/// - `inner` wraps a valid, pinned `i2c_adapter` created using +/// [`I2cAdapterOptions::as_raw`]. +#[repr(transparent)] +#[pin_data(PinnedDrop)] +pub struct Registration { + #[pin] + inner: Opaque, + t_: PhantomData, +} + +impl Registration { + /// Register an I2C adapter. + pub fn register<'a>( + parent_dev: &'a device::Device, + opts: I2cAdapterOptions, + ) -> impl PinInit, Error> + 'a + where + T: 'a, + { + Devres::new(parent_dev, Self::new(parent_dev, opts)) + } + + fn new<'a>( + parent_dev: &'a device::Device, + opts: I2cAdapterOptions, + ) -> impl PinInit + use<'a, T> + where + T: 'a, + { + try_pin_init! { Self { + inner <- Opaque::try_ffi_init(move |slot: *mut bindings::i2c_a= dapter| { + // SAFETY: The initializer can write to the provided `slot= `. + unsafe {slot.write(opts.as_raw::()) }; + + // SAFETY: `slot` is valid from the initializer; `parent_d= ev` outlives the adapter. + unsafe { (*slot).dev.parent =3D parent_dev.as_raw() }; + + // SAFETY: the `struct i2c_adapter` was just created in sl= ot. The adapter will + // get unregistered before `slot` is deallocated because t= he memory is pinned and + // the destructor of this type deallocates the memory. + // INVARIANT: If this returns `Ok(())`, then the `slot` wi= ll contain a registered + // i2c adapter. + to_result(unsafe {bindings::i2c_add_adapter(slot)}) + }), + t_: PhantomData, + } + } + } +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + // SAFETY: We know that the device is registered by the type invar= iants. + unsafe { bindings::i2c_del_adapter(self.inner.get()) }; + } +} + +// 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 throu= gh &self +// and no mutable access is exposed) +unsafe impl Sync for Registration {} diff --git a/rust/kernel/i2c/algo.rs b/rust/kernel/i2c/algo.rs new file mode 100644 index 000000000000..ca9fcc385426 --- /dev/null +++ b/rust/kernel/i2c/algo.rs @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! I2C subsystem + +// I2C Algorithm abstractions. +use crate::{ + bindings::{ + i2c_adapter, + i2c_msg, + i2c_smbus_data, + u16_, + u32_, + u8_, // + }, + device::Normal, + error::VTABLE_DEFAULT_ERROR, + i2c::adapter::I2cAdapter, + prelude::*, + types::Opaque, // +}; + +use core::{ + marker::PhantomData, // +}; + +/// The i2c msg representation. +/// +/// This structure represents the Rust abstraction for a C `struct i2c_msg= `. The +/// implementation abstracts the usage of an existing C `struct i2c_msg` t= hat +/// gets passed from/to the C side +/// +/// # Invariants +/// +/// A [`I2cMsg`] instance represents a valid `struct i2c_msg` created by t= he C portion of +/// the kernel. +#[repr(transparent)] +pub struct I2cMsg(Opaque); + +impl I2cMsg { + /// Convert a raw C `struct i2c_msg` pointers to `&'a mut [I2cMsg]`. + pub fn from_raw_parts_mut<'a>(msgs: *mut bindings::i2c_msg, len: usize= ) -> &'a mut [Self] { + // SAFETY: Callers must ensure that `msgs` is valid, non-null, for= the duration of this + // function call and the entire duration when the returned slice e= xists. + unsafe { core::slice::from_raw_parts_mut(msgs.cast::(), len)= } + } +} + +/// The i2c smbus data representation. +/// +/// This structure represents the Rust abstraction for a C `struct i2c_smb= us_data`. The +/// implementation abstracts the usage of an existing C `struct i2c_smbus_= data` that +/// gets passed from/to the C side +/// +/// # Invariants +/// +/// A [`I2cSmbusData`] instance represents a valid `struct i2c_smbus_data`= created by the C +/// portion of the kernel. +#[repr(transparent)] +pub struct I2cSmbusData(Opaque); + +impl I2cSmbusData { + /// Convert a raw C `struct i2c_smbus_data` pointer to `&'a I2cSmbusDa= ta`. + fn from_raw<'a>(ptr: *const bindings::i2c_smbus_data) -> &'a Self { + // SAFETY: Callers must ensure that `ptr` is valid, non-null, for = the duration of this + // function call and the entire duration when the returned referen= ce exists. + unsafe { &*ptr.cast() } + } +} + +kernel::define_flags!( + I2cFlags(u32), + I2C_FUNC_I2C =3D bindings::I2C_FUNC_I2C, + I2C_FUNC_10BIT_ADDR =3D bindings::I2C_FUNC_10BIT_ADDR, + I2C_FUNC_PROTOCOL_MANGLING =3D bindings::I2C_FUNC_PROTOCOL_MANGLING, + I2C_FUNC_SMBUS_PEC =3D bindings::I2C_FUNC_SMBUS_PEC, + I2C_FUNC_NOSTART =3D bindings::I2C_FUNC_NOSTART, + I2C_FUNC_SLAVE =3D bindings::I2C_FUNC_SLAVE, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL =3D bindings::I2C_FUNC_SMBUS_BLOCK_PROC= _CALL, + I2C_FUNC_SMBUS_QUICK =3D bindings::I2C_FUNC_SMBUS_QUICK, + I2C_FUNC_SMBUS_READ_BYTE =3D bindings::I2C_FUNC_SMBUS_READ_BYTE, + I2C_FUNC_SMBUS_WRITE_BYTE =3D bindings::I2C_FUNC_SMBUS_WRITE_BYTE, + I2C_FUNC_SMBUS_READ_BYTE_DATA =3D bindings::I2C_FUNC_SMBUS_READ_BYTE_D= ATA, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA =3D bindings::I2C_FUNC_SMBUS_WRITE_BYTE= _DATA, + I2C_FUNC_SMBUS_READ_WORD_DATA =3D bindings::I2C_FUNC_SMBUS_READ_WORD_D= ATA, + I2C_FUNC_SMBUS_WRITE_WORD_DATA =3D bindings::I2C_FUNC_SMBUS_WRITE_WORD= _DATA, + I2C_FUNC_SMBUS_PROC_CALL =3D bindings::I2C_FUNC_SMBUS_PROC_CALL, + I2C_FUNC_SMBUS_READ_BLOCK_DATA =3D bindings::I2C_FUNC_SMBUS_READ_BLOCK= _DATA, + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA =3D bindings::I2C_FUNC_SMBUS_WRITE_BLO= CK_DATA, + I2C_FUNC_SMBUS_READ_I2C_BLOCK =3D bindings::I2C_FUNC_SMBUS_READ_I2C_BL= OCK, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK =3D bindings::I2C_FUNC_SMBUS_WRITE_I2C_= BLOCK, + I2C_FUNC_SMBUS_HOST_NOTIFY =3D bindings::I2C_FUNC_SMBUS_HOST_NOTIFY, +); + +/// Trait implemented by the private data of an i2c adapter. +#[vtable] +pub trait I2cAlgorithm { + /// Handler for transfer a given number of messages defined by the msg= s array + /// via the specified adapter. + fn xfer(_adap: &I2cAdapter, _msgs: &mut [I2cMsg]) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Same as @xfer. Yet, only using atomic context so e.g. PMICs + /// can be accessed very late before shutdown. Optional. + fn xfer_atomic(_adap: &I2cAdapter, _msgs: &mut [I2cMsg]) -> Re= sult { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Issue SMBus transactions to the given I2C adapter. If this + /// is not present, then the bus layer will try and convert the SMBus = calls + /// into I2C transfers instead. + fn smbus_xfer( + _adap: &I2cAdapter, + _addr: u16, + _flags: u16, + _read_write: u8, + _command: u8, + _size: usize, + _data: &I2cSmbusData, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Same as @smbus_xfer. Yet, only using atomic context + /// so e.g. PMICs can be accessed very late before shutdown. Optional. + fn smbus_xfer_atomic( + _adap: &I2cAdapter, + _addr: u16, + _flags: u16, + _read_write: u8, + _command: u8, + _size: usize, + _data: &I2cSmbusData, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Return the flags that this algorithm/adapter pair supports + /// from the ``I2C_FUNC_*`` flags. + fn functionality(_adap: &I2cAdapter) -> I2cFlags { + build_error!(VTABLE_DEFAULT_ERROR) + } +} + +/// A vtable for the I2C xfer operations of a Rust i2c adapter. +pub struct I2cAlgorithmVTable(PhantomData); + +impl I2cAlgorithmVTable { + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `msgs` must be a valid pointer to `struct i2c_msg` for reading/wri= ting. + unsafe extern "C" fn xfer( + adap: *mut i2c_adapter, + msgs: *mut i2c_msg, + num: ffi::c_int, + ) -> ffi::c_int { + let num =3D match usize::try_from(num) { + Ok(num) =3D> num, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let msg_slice =3D I2cMsg::from_raw_parts_mut(msgs, num); + + match T::xfer(I2cAdapter::from_raw(adap), msg_slice) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `msgs` must be a valid pointer to `struct i2c_msg` for reading/wri= ting. + unsafe extern "C" fn xfer_atomic( + adap: *mut i2c_adapter, + msgs: *mut i2c_msg, + num: ffi::c_int, + ) -> ffi::c_int { + let num =3D match usize::try_from(num) { + Ok(num) =3D> num, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let msg_slice =3D I2cMsg::from_raw_parts_mut(msgs, num); + + match T::xfer_atomic(I2cAdapter::from_raw(adap), msg_slice) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `data` must be a valid pointer to `struct i2c_smbus_data` for read= ing/writing. + unsafe extern "C" fn smbus_xfer( + adap: *mut i2c_adapter, + addr: u16_, + flags: ffi::c_ushort, + read_write: ffi::c_char, + command: u8_, + size: ffi::c_int, + data: *mut i2c_smbus_data, + ) -> ffi::c_int { + let size =3D match usize::try_from(size) { + Ok(size) =3D> size, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let data =3D I2cSmbusData::from_raw(data); + + match T::smbus_xfer( + I2cAdapter::from_raw(adap), + addr, + flags, + read_write, + command, + size, + data, + ) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `i2c::adapter::Registration`. + /// `data` must be a valid pointer to `struct i2c_smbus_data` for read= ing/writing. + unsafe extern "C" fn smbus_xfer_atomic( + adap: *mut i2c_adapter, + addr: u16_, + flags: ffi::c_ushort, + read_write: ffi::c_char, + command: u8_, + size: ffi::c_int, + data: *mut i2c_smbus_data, + ) -> ffi::c_int { + let size =3D match usize::try_from(size) { + Ok(size) =3D> size, + Err(_err) =3D> return EINVAL.to_errno(), + }; + + let data =3D I2cSmbusData::from_raw(data); + + match T::smbus_xfer_atomic( + I2cAdapter::from_raw(adap), + addr, + flags, + read_write, + command, + size, + data, + ) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + /// # Safety + /// + /// `adap` must be a valid pointer to `struct i2c_adapter` that is ass= ociated with a + /// `I2cAdapterRegistration`. + unsafe extern "C" fn functionality(adap: *mut i2c_adapter) -> u32_ { + T::functionality(I2cAdapter::from_raw(adap)).as_raw() + } + + const VTABLE: bindings::i2c_algorithm =3D bindings::i2c_algorithm { + xfer: if T::HAS_XFER { Some(Self::xfer) } else { None }, + xfer_atomic: if T::HAS_XFER_ATOMIC { + Some(Self::xfer_atomic) + } else { + None + }, + smbus_xfer: if T::HAS_SMBUS_XFER { + Some(Self::smbus_xfer) + } else { + None + }, + smbus_xfer_atomic: if T::HAS_SMBUS_XFER_ATOMIC { + Some(Self::smbus_xfer_atomic) + } else { + None + }, + functionality: if T::HAS_FUNCTIONALITY { + Some(Self::functionality) + } else { + None + }, + }; + + pub(super) const fn build() -> &'static bindings::i2c_algorithm { + &Self::VTABLE + } +} --=20 2.43.0