blob: 14b438a60963f83ca0b6359d1fe579537eb40b53 [file]
// Licensed under the Apache-2.0 license
//! I2C Target (Slave) Adapter
//!
//! This module provides an adapter that bridges the low-level [`Ast1060I2c`]
//! slave API with a user-supplied `TargetCallbacks` implementation. The
//! adapter polls hardware events from the controller and dispatches them
//! to the callback methods, keeping the hardware layer (slave.rs)
//! decoupled from any particular target abstraction.
//!
//! # Example
//!
//! ```rust,ignore
//! use ast10x0_peripherals::i2c::target_adapter::{TargetAdapter, TargetCallbacks};
//!
//! struct MyTarget { /* ... */ }
//! impl TargetCallbacks for MyTarget { /* ... */ }
//!
//! let mut adapter = TargetAdapter::new(i2c_controller, MyTarget { /* ... */ });
//! adapter.configure(0x42)?;
//!
//! // In an interrupt handler or poll loop:
//! adapter.process_events()?;
//! ```
use super::controller::Ast1060I2c;
use super::error::I2cError;
use super::slave::{SlaveConfig, SlaveEvent};
/// Internal hardware buffer for adapter operations
const ADAPTER_BUFFER_SIZE: usize = 32;
/// Target adapter that bridges hardware events to [`TargetCallbacks`].
///
/// Wraps an [`Ast1060I2c`] controller and a user-provided callback
/// implementation, mapping hardware slave events to the appropriate
/// callback methods.
///
/// # Type Parameters
///
/// * `'a` - Lifetime of the I2C register references
/// * `T` - User's [`TargetCallbacks`] implementation
/// * `Y` - Yield closure threaded through the underlying [`Ast1060I2c`]
pub struct TargetAdapter<'a, T, Y: FnMut(u32)> {
/// The underlying hardware controller
i2c: Ast1060I2c<'a, Y>,
/// User's target implementation
target: T,
/// Internal receive buffer
rx_buffer: [u8; ADAPTER_BUFFER_SIZE],
/// Internal transmit buffer
tx_buffer: [u8; ADAPTER_BUFFER_SIZE],
/// Flag indicating we're in a transaction
in_transaction: bool,
}
impl<'a, T, Y: FnMut(u32)> TargetAdapter<'a, T, Y> {
/// Create a new target adapter.
///
/// # Arguments
///
/// * `i2c` - The I2C controller configured for the desired bus
/// * `target` - User's target implementation
///
/// # Returns
///
/// A new `TargetAdapter` instance ready to be configured.
pub fn new(i2c: Ast1060I2c<'a, Y>, target: T) -> Self {
Self {
i2c,
target,
rx_buffer: [0u8; ADAPTER_BUFFER_SIZE],
tx_buffer: [0u8; ADAPTER_BUFFER_SIZE],
in_transaction: false,
}
}
/// Get a reference to the underlying target implementation.
pub fn target(&self) -> &T {
&self.target
}
/// Get a mutable reference to the underlying target implementation.
pub fn target_mut(&mut self) -> &mut T {
&mut self.target
}
/// Get a reference to the underlying I2C controller.
pub fn controller(&self) -> &Ast1060I2c<'a, Y> {
&self.i2c
}
/// Get a mutable reference to the underlying I2C controller.
pub fn controller_mut(&mut self) -> &mut Ast1060I2c<'a, Y> {
&mut self.i2c
}
/// Decompose the adapter into its parts.
///
/// Returns the I2C controller and target implementation.
pub fn into_parts(self) -> (Ast1060I2c<'a, Y>, T) {
(self.i2c, self.target)
}
}
impl<'a, T, Y: FnMut(u32)> TargetAdapter<'a, T, Y>
where
T: TargetCallbacks,
{
/// Configure the controller for target (slave) mode.
///
/// # Arguments
///
/// * `address` - The 7-bit I2C address to respond to
///
/// # Returns
///
/// `Ok(())` on success, or an error if configuration fails.
pub fn configure(&mut self, address: u8) -> Result<(), I2cError> {
let config = SlaveConfig::new(address)?;
self.i2c.configure_slave(&config)?;
// Initialize the target with the address
self.target
.on_init(address)
.map_err(|_| I2cError::SlaveError)?;
self.in_transaction = false;
Ok(())
}
/// Disable target mode.
pub fn disable(&mut self) {
self.i2c.disable_slave();
self.in_transaction = false;
}
/// Process pending hardware events.
///
/// This method should be called from an interrupt handler or poll loop.
/// It reads hardware status, maps events to trait callbacks, and handles
/// data transfer between hardware and the target implementation.
///
/// # Returns
///
/// `Ok(Some(event))` if an event was processed, `Ok(None)` if no events pending,
/// or an error if processing failed.
pub fn process_events(&mut self) -> Result<Option<SlaveEvent>, I2cError> {
let Some(event) = self.i2c.handle_slave_interrupt() else {
return Ok(None);
};
match event {
SlaveEvent::AddressMatch => {
if !self.in_transaction {
self.target.on_transaction_start(false);
self.in_transaction = true;
}
self.target.on_address_match();
}
SlaveEvent::ReadRequest => {
// Master wants to read from us - get data from target
let len = self
.target
.on_read(&mut self.tx_buffer)
.map_err(|_| I2cError::SlaveError)?;
// Send data to master
if len > 0 {
let to_send = len.min(ADAPTER_BUFFER_SIZE);
self.i2c.slave_write(&self.tx_buffer[..to_send])?;
}
}
SlaveEvent::WriteRequest => {
// Master wants to write to us - nothing to do here,
// data will arrive in DataReceived
}
SlaveEvent::DataReceived { len } => {
// Read data from hardware
let read_len = self.i2c.slave_read(&mut self.rx_buffer)?;
let actual_len = read_len.min(len);
if actual_len > 0 {
// Pass data to target
self.target
.on_write(&self.rx_buffer[..actual_len])
.map_err(|_| I2cError::SlaveError)?;
}
}
SlaveEvent::DataSent { len: _ } => {
// Data was sent successfully - target can track if needed
self.target.on_data_sent();
}
SlaveEvent::Stop => {
self.target.on_stop();
self.in_transaction = false;
}
}
Ok(Some(event))
}
/// Poll for and process all pending events.
///
/// Continues processing until no more events are available.
///
/// # Returns
///
/// The count of events processed, or an error if processing failed.
pub fn process_all_events(&mut self) -> Result<usize, I2cError> {
let mut count: usize = 0;
while self.process_events()?.is_some() {
count = count.saturating_add(1);
}
Ok(count)
}
}
/// Target callback trait for adapter integration.
///
/// This trait defines the callbacks that the adapter invokes in response to
/// hardware events from the I2C controller in slave mode. Implementors plug
/// in their own logic for address-match, read, write, and stop events.
pub trait TargetCallbacks {
/// Error type for callback operations
type Error;
/// Called when the target is initialized with an address.
///
/// # Arguments
///
/// * `address` - The 7-bit I2C address assigned to this target
fn on_init(&mut self, address: u8) -> Result<(), Self::Error>;
/// Called when a transaction starts.
///
/// # Arguments
///
/// * `repeated` - True if this is a repeated start condition
fn on_transaction_start(&mut self, repeated: bool);
/// Called when our address is matched.
fn on_address_match(&mut self);
/// Called when the master requests data (read operation).
///
/// # Arguments
///
/// * `buffer` - Buffer to fill with data to send to master
///
/// # Returns
///
/// Number of bytes written to buffer, or an error.
fn on_read(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error>;
/// Called when the master sends data (write operation).
///
/// # Arguments
///
/// * `data` - Data received from the master
fn on_write(&mut self, data: &[u8]) -> Result<(), Self::Error>;
/// Called when data has been sent to the master.
fn on_data_sent(&mut self) {}
/// Called when a stop condition is received.
fn on_stop(&mut self);
}