blob: 817d8d7adf6b8965dcaa75007b4b790be21b3dc4 [file] [log] [blame]
// Licensed under the Apache-2.0 license
//! # I2C Hardware Abstraction Traits
//!
//! This module defines composable traits for I2C hardware abstraction following
//! a clean separation of concerns. Each trait has a specific responsibility
//! and can be composed to build complete I2C functionality.
//!
//! ## Design Philosophy
//!
//! The traits are designed to be:
//! - **Composable**: Small, focused traits that can be combined
//! - **Flexible**: Different implementations can pick the traits they need
//! - **Extensible**: New functionality can be added without breaking existing code
//! - **Clear**: Each trait has a single, well-defined responsibility
//!
//! ## Trait Hierarchy
//!
//! ```text
//! I2cHardwareCore (foundation)
//! ├── I2cMaster (master operations)
//! └── slave module (blocking operations only)
//! ├── I2cSlaveCore (basic slave setup)
//! ├── I2cSlaveBuffer (data transfer)
//! ├── I2cSlaveInterrupts (interrupt & status management)
//! │ └── I2cSlaveEventSync (sync/blocking events)
//! ├── Composite Traits:
//! │ ├── I2cSlaveBasic (core + buffer)
//! │ └── I2cSlaveSync (basic + sync events)
//! └── I2cMasterSlave (master + sync slave)
//! ```
//!
//! For non-blocking slave operations, see `openprot-hal-nb::i2c_hardware`.
use embedded_hal::i2c::{AddressMode, Operation, SevenBitAddress};
/// Core I2C hardware interface providing basic operations
///
/// This is the foundation trait that all I2C hardware implementations must provide.
/// It contains only the most basic operations needed for any I2C controller.
pub trait I2cHardwareCore {
/// Hardware-specific error type that implements embedded-hal error traits
type Error: embedded_hal::i2c::Error + core::fmt::Debug;
/// Hardware-specific configuration type for I2C initialization and setup
type Config;
/// I2C speed configuration type
type I2cSpeed;
/// Timing configuration type
type TimingConfig;
/// Initialize the I2C hardware with the given configuration
fn init(&mut self, config: &mut Self::Config) -> Result<(), Self::Error>;
/// Configure timing parameters (clock speed, setup/hold times)
///
/// Takes timing parameters as input and returns the calculated clock source frequency.
/// This provides type safety by making clear what is read vs. what is computed/returned.
///
/// # Arguments
///
/// * `speed` - Target I2C bus speed (Standard, Fast, FastPlus, etc.)
/// * `timing` - Timing configuration parameters for setup/hold times
///
/// # Returns
///
/// Returns the actual calculated clock source frequency in Hz.
///
/// # Errors
///
/// Returns an error if the requested timing cannot be achieved with the
/// available hardware clock sources or if parameters are invalid.
fn configure_timing(
&mut self,
speed: Self::I2cSpeed,
timing: &Self::TimingConfig,
) -> Result<u32, Self::Error>;
/// Enable hardware interrupts with the specified mask
fn enable_interrupts(&mut self, mask: u32);
/// Clear hardware interrupts with the specified mask
fn clear_interrupts(&mut self, mask: u32);
/// Handle hardware interrupt events (called from ISR)
fn handle_interrupt(&mut self);
/// Attempt to recover the I2C bus from stuck conditions
fn recover_bus(&mut self) -> Result<(), Self::Error>;
}
/// I2C Master mode operations
///
/// This trait extends the core interface with master-specific functionality.
/// Implementations provide the actual I2C master protocol operations.
pub trait I2cMaster<A: AddressMode = SevenBitAddress>: I2cHardwareCore {
/// Write data to a slave device at the given address
fn write(&mut self, addr: A, bytes: &[u8]) -> Result<(), Self::Error>;
/// Read data from a slave device at the given address
fn read(&mut self, addr: A, buffer: &mut [u8]) -> Result<(), Self::Error>;
/// Combined write-then-read operation with restart condition
fn write_read(&mut self, addr: A, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error>;
/// Execute a sequence of I2C operations as a single atomic transaction
fn transaction_slice(
&mut self,
addr: A,
ops_slice: &mut [Operation<'_>],
) -> Result<(), Self::Error>;
}
/// I2C Slave/Target mode functionality
///
/// This module contains all slave-related traits decomposed into
/// focused responsibilities for better composability.
pub mod slave {
use super::*;
/// I2C slave events that can occur during slave operations
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum I2cSEvent {
/// Master is requesting to read from slave
SlaveRdReq,
/// Master is requesting to write to slave
SlaveWrReq,
/// Slave read operation is in progress
SlaveRdProc,
/// Slave has received write data from master
SlaveWrRecvd,
/// Stop condition received
SlaveStop,
}
/// Status information for I2C slave operations
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SlaveStatus {
/// Whether slave mode is currently enabled
pub enabled: bool,
/// Current slave address (if enabled)
pub address: Option<u8>,
/// Whether there's data available to read
pub data_available: bool,
/// Number of bytes in receive buffer
pub rx_buffer_count: usize,
/// Number of bytes in transmit buffer
pub tx_buffer_count: usize,
/// Last slave event that occurred
pub last_event: Option<I2cSEvent>,
/// Whether an error condition exists
pub error: bool,
}
/// Core slave functionality - address configuration and mode control
///
/// This trait provides the fundamental slave operations that all slave
/// implementations need: setting slave address and enabling/disabling slave mode.
/// This is the minimal trait for any I2C slave implementation.
pub trait I2cSlaveCore<A: AddressMode = SevenBitAddress>: super::I2cHardwareCore {
/// Configure the slave address for this I2C controller
fn configure_slave_address(&mut self, addr: A) -> Result<(), Self::Error>;
/// Enable slave mode operation
fn enable_slave_mode(&mut self) -> Result<(), Self::Error>;
/// Disable slave mode and return to master-only operation
fn disable_slave_mode(&mut self) -> Result<(), Self::Error>;
/// Check if slave mode is currently enabled
fn is_slave_mode_enabled(&self) -> bool;
/// Get the currently configured slave address
fn slave_address(&self) -> Option<A>;
}
/// Slave buffer operations - data transfer with master
///
/// This trait handles the actual data exchange between slave and master.
/// Separate from core to allow different buffer management strategies.
/// Implementations can choose different buffering approaches (ring buffer,
/// simple array, DMA, etc.) while maintaining the same interface.
pub trait I2cSlaveBuffer<A: AddressMode = SevenBitAddress>: I2cSlaveCore<A> {
/// Read received data from the slave buffer
///
/// Returns the number of bytes actually read. The buffer is filled
/// with data received from the master during the last transaction.
/// This is typically called after detecting a slave write event.
fn read_slave_buffer(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error>;
/// Write response data to the slave transmit buffer
///
/// Prepares data to be sent to the master during the next read transaction.
/// The data will be transmitted when the master requests it.
fn write_slave_response(&mut self, data: &[u8]) -> Result<(), Self::Error>;
/// Non-blocking check for available slave data
///
/// Returns Some(length) if data is available to read, None otherwise.
/// This is useful for polling-based implementations or to check
/// before calling read_slave_buffer.
fn poll_slave_data(&mut self) -> Result<Option<usize>, Self::Error>;
/// Clear the slave receive buffer and reset state
///
/// Clears any pending received data and resets the buffer to
/// a clean state. Useful for error recovery or initialization.
fn clear_slave_buffer(&mut self) -> Result<(), Self::Error>;
/// Get available space in transmit buffer
///
/// Returns the number of bytes that can be written to the transmit
/// buffer without overflowing. Useful for flow control.
fn tx_buffer_space(&self) -> Result<usize, Self::Error>;
/// Get number of bytes available in receive buffer
///
/// Returns the current count of bytes waiting to be read from
/// the receive buffer.
fn rx_buffer_count(&self) -> Result<usize, Self::Error>;
}
/// Slave interrupt and status management
///
/// Common interrupt and status operations shared by both async and sync event patterns.
/// This provides the foundation for event-driven slave operations.
pub trait I2cSlaveInterrupts<A: AddressMode = SevenBitAddress>: I2cSlaveCore<A> {
/// Enable slave-specific hardware interrupts
///
/// Configures the hardware to generate interrupts for slave events.
/// The mask parameter specifies which interrupt sources to enable.
/// Common interrupts include: address match, data received, stop condition, etc.
fn enable_slave_interrupts(&mut self, mask: u32);
/// Clear slave-specific hardware interrupts
///
/// Clears pending interrupt flags for the specified interrupt sources.
/// This is typically called in interrupt service routines to acknowledge
/// that the interrupt has been handled.
fn clear_slave_interrupts(&mut self, mask: u32);
/// Current slave hardware status
///
/// Returns comprehensive status information about the slave controller
/// including enabled state, address, buffer counts, and error conditions.
fn slave_status(&self) -> Result<SlaveStatus, Self::Error>;
/// Last slave event that occurred
///
/// Returns the most recent slave event, useful for debugging
/// and state tracking. May return None if no events have occurred
/// since reset or if the hardware doesn't track this information.
fn last_slave_event(&self) -> Option<I2cSEvent>;
}
/// Blocking slave event handling (sync pattern)
///
/// This trait provides blocking operations suitable for synchronous code
/// that can afford to wait for events. Operations may block the calling
/// thread until the requested condition is met or timeout occurs.
pub trait I2cSlaveEventSync<A: AddressMode = SevenBitAddress>: I2cSlaveInterrupts<A> {
/// Wait for a specific slave event with timeout
///
/// Blocks until the specified event occurs or the timeout expires.
/// Returns true if the event occurred, false if timeout expired.
/// Useful for synchronous slave operations that need to coordinate
/// with master transactions.
fn wait_for_slave_event(
&mut self,
expected_event: I2cSEvent,
timeout_ms: u32,
) -> Result<bool, Self::Error>;
/// Wait for any slave event with timeout
///
/// Blocks until any slave event occurs or timeout expires.
/// Returns the event that occurred, or None if timeout expired.
/// Useful when any event needs to be processed synchronously.
fn wait_for_any_event(&mut self, timeout_ms: u32)
-> Result<Option<I2cSEvent>, Self::Error>;
/// Handle a specific slave event with blocking semantics
///
/// Processes a slave event and may block if the event handling
/// requires waiting for hardware completion. This is different
/// from the polling version which always returns immediately.
fn handle_slave_event_blocking(&mut self, event: I2cSEvent) -> Result<(), Self::Error>;
}
/// Complete slave implementation combining core functionality
///
/// This trait represents a basic slave implementation that combines
/// core setup and buffer operations. It's suitable for most simple
/// slave use cases without requiring event handling.
pub trait I2cSlaveBasic<A: AddressMode = SevenBitAddress>:
I2cSlaveCore<A> + I2cSlaveBuffer<A>
{
}
/// Blanket implementation: any type implementing core + buffer gets basic slave
impl<T, A: AddressMode> I2cSlaveBasic<A> for T where T: I2cSlaveCore<A> + I2cSlaveBuffer<A> {}
/// Complete sync slave implementation
///
/// This trait represents a full sync slave implementation that supports
/// all blocking slave operations. Perfect for traditional blocking
/// implementations that can afford to wait.
pub trait I2cSlaveSync<A: AddressMode = SevenBitAddress>:
I2cSlaveCore<A> + I2cSlaveBuffer<A> + I2cSlaveEventSync<A>
{
}
/// Blanket implementation: any type implementing core + buffer + sync events gets sync slave
impl<T, A: AddressMode> I2cSlaveSync<A> for T where
T: I2cSlaveCore<A> + I2cSlaveBuffer<A> + I2cSlaveEventSync<A>
{
}
/// Combined trait for controllers supporting both master and slave modes
///
/// This is a convenience trait for hardware that supports both modes.
/// Implementations get this automatically via blanket implementation.
pub trait I2cMasterSlave<A: AddressMode = SevenBitAddress>:
super::I2cMaster<A> + I2cSlaveSync<A>
{
}
/// Blanket implementation: any type implementing both master and sync slave gets this trait
impl<T, A: AddressMode> I2cMasterSlave<A> for T where T: super::I2cMaster<A> + I2cSlaveSync<A> {}
}
/// Re-export slave traits for convenience
pub use slave::{
I2cMasterSlave, I2cSlaveBasic, I2cSlaveBuffer, I2cSlaveCore, I2cSlaveEventSync,
I2cSlaveInterrupts, I2cSlaveSync,
};