blob: 90d01fc2fabacebbd86cc7786776b9c74c2e92b6 [file]
// Licensed under the Apache-2.0 license
//! I2C MCTP sender — outbound transport binding.
//!
//! Direct port of Hubris `mctp-server/src/i2c.rs` `I2cSender`.
//! Only the I2C driver API is replaced: `drv_i2c_api::I2cDevice` →
//! `openprot_i2c_api::I2cClientBlocking`.
use mctp::Result;
use mctp_stack::i2c::{MctpI2cEncap, MCTP_I2C_MAXMTU};
use openprot_i2c_api::{BusIndex, I2cAddress, I2cClientBlocking};
/// I2C MCTP sender.
///
/// Implements `mctp_stack::Sender` to fragment and send MCTP packets
/// over I2C using the OpenPRoT I2C client API.
///
/// This is a direct port of the Hubris `I2cSender`. The fragmentation
/// loop, I2C encoding via `MctpI2cEncap`, and error mapping are preserved
/// as-is. Only the I2C write call is changed from `drv_i2c_api::I2cDevice::write`
/// to `I2cClientBlocking::write`.
pub struct I2cSender<C: I2cClientBlocking> {
i2c: C,
bus: BusIndex,
own_addr: u8,
// TODO: neighbor table mapping EID → I2C address
// see https://github.com/OpenPRoT/mctp-lib/issues/4
// For now, destination address is hardcoded (same as Hubris).
}
impl<C: I2cClientBlocking> I2cSender<C> {
/// Create a new I2C sender.
///
/// * `i2c` - I2C client for bus writes
/// * `bus` - I2C bus index to use
/// * `own_addr` - Own I2C address (7-bit, used in MCTP-I2C header)
pub fn new(i2c: C, bus: BusIndex, own_addr: u8) -> Self {
Self {
i2c,
bus,
own_addr,
}
}
}
impl<C: I2cClientBlocking> mctp_stack::Sender for I2cSender<C> {
fn send_vectored(
&mut self,
mut fragmenter: mctp_stack::fragment::Fragmenter,
payload: &[&[u8]],
) -> Result<mctp::Tag> {
// TODO The stack needs to provide the destination EID to the sender
// let addr = self
// .neighbor_table
// .get(eid)
// .ok_or(mctp::Error::Unreachable)?;
let addr = 0x42;
let dest_address = I2cAddress::new_unchecked(addr);
let encoder = MctpI2cEncap::new(self.own_addr);
loop {
let mut pkt = [0u8; mctp_stack::serial::MTU_MAX];
let r = fragmenter.fragment_vectored(payload, &mut pkt);
match r {
mctp_stack::fragment::SendOutput::Packet(p) => {
let mut out = [0; MCTP_I2C_MAXMTU + 8]; // max MTU + I2C header size
let _packet = encoder.encode(addr, p, &mut out, true)?;
self.i2c
.write(self.bus, dest_address, &out)
.map_err(|_| mctp::Error::TxFailure)?;
}
mctp_stack::fragment::SendOutput::Complete { tag, .. } => {
break Ok(tag);
}
mctp_stack::fragment::SendOutput::Error { err, .. } => {
break Err(err);
}
}
}
}
fn get_mtu(&self) -> usize {
MCTP_I2C_MAXMTU
}
}