blob: f218a8a4f88f4d768e7fa1d7730cb65e261e57e5 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! The transport seam.
//!
//! One whole serialized transaction goes out, one whole response comes back —
//! *bytes in → bytes out, one shot, for every impl*. This signature is what
//! makes the whole-object / run-to-completion invariant structural rather than
//! conventional: a transport physically cannot express a per-operation
//! round-trip or a held bus lock.
//!
//! The `I2cClient` is generic over this trait and contains **all** the wire
//! marshalling. Swapping the transport is a wiring choice, never a code fork:
//!
//! - `IpcTransport` (in `i2c-client-ipc`) — production cross-process path
//! (Pigweed `channel_transact`); the only IPC-coupled, kernel-tagged piece.
//! - `LoopbackTransport` (in `i2c-server`) — calls the server `dispatch`
//! directly against an in-process `embedded_hal::i2c::I2c`. Host-buildable,
//! so the *same* client encoders/decoders are exercised with no kernel; also
//! the correct early-boot path before IPC exists.
/// Why a transport round-trip failed. Deliberately tiny and transport-neutral;
/// i2c-level status travels inside the response payload, not here.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransportError {
/// The underlying channel/syscall/loopback call failed.
Failed,
}
impl core::fmt::Display for TransportError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Failed => f.write_str("i2c transport round-trip failed"),
}
}
}
impl core::error::Error for TransportError {}
/// Bytes-in → bytes-out, exactly one round-trip.
///
/// `transact` writes the response into `resp` and returns its length. The
/// request is one fully serialized `i2c_api` transaction; the response is one
/// fully serialized reply. No fragmentation, no state between calls.
pub trait Transport {
fn transact(&mut self, req: &[u8], resp: &mut [u8]) -> Result<usize, TransportError>;
}