blob: ef04777a7edf78d28971fd5aafd6564730004118 [file]
// Licensed under the Apache-2.0 license
//! I2C target mode operations.
//!
//! This module provides types and traits for operating an I2C controller
//! in target (slave) mode, allowing the device to respond to transactions
//! initiated by other controllers on the bus.
//!
//! This is essential for protocols like MCTP where the device needs to
//! receive messages from a BMC or other system controller.
use core::time::Duration;
use embedded_hal::i2c::ErrorType;
use crate::address::I2cAddress;
use crate::client::BusIndex;
/// I2C target mode operations.
///
/// Allows the device to respond to I2C transactions initiated
/// by other controllers on the bus. Uses notification-based message
/// delivery rather than polling for efficiency.
///
/// # Examples
///
/// ```rust,ignore
/// use i2c_api::{I2cTargetClient, I2cAddress, BusIndex, TargetMessage};
/// use core::time::Duration;
///
/// fn setup_target<C: I2cTargetClient>(
/// client: &mut C,
/// bus: BusIndex,
/// my_address: I2cAddress,
/// ) -> Result<(), C::Error> {
/// // Configure our address and enable receive
/// client.configure_target_address(bus, my_address)?;
/// client.enable_receive(bus)?;
///
/// // Wait for incoming messages
/// let mut messages = [TargetMessage::default(); 4];
/// let count = client.wait_for_messages(
/// bus,
/// &mut messages,
/// Some(Duration::from_secs(5)),
/// )?;
///
/// for msg in &messages[..count] {
/// process_message(msg);
/// }
/// Ok(())
/// }
/// ```
pub trait I2cTargetClient: ErrorType {
/// Configure this controller to respond at the given address.
///
/// The controller will ACK transactions sent to this address.
fn configure_target_address(
&mut self,
bus: BusIndex,
address: I2cAddress,
) -> Result<(), Self::Error>;
/// Enable target receive mode.
///
/// After this call, incoming transactions to the configured
/// address will be buffered and trigger notifications if registered.
fn enable_receive(&mut self, bus: BusIndex) -> Result<(), Self::Error>;
/// Disable target receive mode.
///
/// Stops accepting incoming transactions. Buffered messages
/// are retained until retrieved.
fn disable_receive(&mut self, bus: BusIndex) -> Result<(), Self::Error>;
/// Wait for incoming target messages.
///
/// Blocks until one or more messages are available, or timeout expires.
/// Returns the number of messages retrieved.
///
/// # Arguments
///
/// * `bus` - I2C bus to receive on
/// * `messages` - Buffer to store received messages
/// * `timeout` - Optional timeout; `None` waits indefinitely
fn wait_for_messages(
&mut self,
bus: BusIndex,
messages: &mut [TargetMessage],
timeout: Option<Duration>,
) -> Result<usize, Self::Error>;
/// Register a notification callback for incoming messages.
///
/// When a target message arrives, the kernel will post a notification
/// to the calling task using the provided mask. The task can then
/// call `get_pending_messages` to retrieve the buffered data.
///
/// # Arguments
///
/// * `bus` - I2C bus to monitor
/// * `notification_mask` - Bit mask to use for notifications
fn register_notification(
&mut self,
bus: BusIndex,
notification_mask: u32,
) -> Result<(), Self::Error>;
/// Retrieve pending messages after receiving a notification.
///
/// Call this after receiving a target message notification.
/// Returns the number of messages retrieved.
///
/// # Arguments
///
/// * `bus` - I2C bus to read from
/// * `messages` - Buffer to store pending messages
fn get_pending_messages(
&mut self,
bus: BusIndex,
messages: &mut [TargetMessage],
) -> Result<usize, Self::Error>;
}
/// The type of event reported by a slave wait operation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum SlaveEventKind {
/// Master wrote data to us.
DataReceived = 0,
/// Master requested to read from us (TX data was sent).
ReadRequest = 1,
/// Stop condition received.
Stop = 2,
}
impl SlaveEventKind {
/// Decode from a raw byte.
pub fn from_u8(val: u8) -> Option<Self> {
match val {
0 => Some(Self::DataReceived),
1 => Some(Self::ReadRequest),
2 => Some(Self::Stop),
_ => None,
}
}
}
/// Maximum size of a target message payload.
pub const TARGET_MESSAGE_MAX_LEN: usize = 255;
/// A message received in target mode.
///
/// Contains the data received from a controller and metadata
/// about the transaction.
#[derive(Clone)]
pub struct TargetMessage {
/// Address of the controller that sent this message.
pub source_address: I2cAddress,
/// Message data buffer.
data: [u8; TARGET_MESSAGE_MAX_LEN],
/// Actual length of data.
len: u8,
}
impl TargetMessage {
/// Creates a new empty target message.
pub const fn new() -> Self {
Self {
source_address: I2cAddress::GENERAL_CALL,
data: [0u8; TARGET_MESSAGE_MAX_LEN],
len: 0,
}
}
/// Creates a target message with the given data.
///
/// # Panics
///
/// Panics if `data.len() > TARGET_MESSAGE_MAX_LEN`.
pub fn from_data(source_address: I2cAddress, data: &[u8]) -> Self {
assert!(data.len() <= TARGET_MESSAGE_MAX_LEN);
let mut msg = Self::new();
msg.source_address = source_address;
msg.data[..data.len()].copy_from_slice(data);
msg.len = data.len() as u8;
msg
}
/// Returns the message data.
#[inline]
pub fn data(&self) -> &[u8] {
&self.data[..self.len as usize]
}
/// Returns a mutable reference to the message data buffer.
///
/// Use `set_len` to update the valid data length after writing.
#[inline]
pub fn data_mut(&mut self) -> &mut [u8; TARGET_MESSAGE_MAX_LEN] {
&mut self.data
}
/// Returns the message length.
#[inline]
pub const fn len(&self) -> usize {
self.len as usize
}
/// Sets the valid data length.
///
/// # Panics
///
/// Panics if `len > TARGET_MESSAGE_MAX_LEN`.
pub fn set_len(&mut self, len: usize) {
assert!(len <= TARGET_MESSAGE_MAX_LEN);
self.len = len as u8;
}
/// Returns `true` if the message is empty.
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
/// Clears the message data.
pub fn clear(&mut self) {
self.len = 0;
}
}
impl Default for TargetMessage {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for TargetMessage {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TargetMessage")
.field("source_address", &self.source_address)
.field("len", &self.len)
.field("data", &self.data())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_target_message_new() {
let msg = TargetMessage::new();
assert!(msg.is_empty());
assert_eq!(msg.len(), 0);
assert_eq!(msg.data(), &[]);
}
#[test]
fn test_target_message_from_data() {
let source = I2cAddress::new(0x50).unwrap();
let data = [0x01, 0x02, 0x03, 0x04];
let msg = TargetMessage::from_data(source, &data);
assert_eq!(msg.source_address, source);
assert_eq!(msg.len(), 4);
assert_eq!(msg.data(), &data);
}
#[test]
fn test_target_message_set_len() {
let mut msg = TargetMessage::new();
msg.data_mut()[..3].copy_from_slice(&[0xAA, 0xBB, 0xCC]);
msg.set_len(3);
assert_eq!(msg.len(), 3);
assert_eq!(msg.data(), &[0xAA, 0xBB, 0xCC]);
}
#[test]
fn test_target_message_clear() {
let source = I2cAddress::new(0x50).unwrap();
let mut msg = TargetMessage::from_data(source, &[0x01, 0x02]);
msg.clear();
assert!(msg.is_empty());
}
#[test]
fn test_target_message_default() {
let msg = TargetMessage::default();
assert!(msg.is_empty());
}
}