#[allow(unused)]
use crate::fmt::{debug, error, info, trace, warn};
use crate::{
AppCookie, Fragmenter, MctpMessage, MsgIC, ReceiveHandle, SendOutput,
Stack, MAX_PAYLOAD,
};
use mctp::{Eid, Error, MsgType, Result, Tag};
use heapless::Vec;
pub const MCTP_I2C_COMMAND_CODE: u8 = 0x0f;
const MCTP_I2C_HEADER: usize = 4;
pub const MCTP_I2C_MAXMTU: usize = u8::MAX as usize - 1;
type MctpI2cHeader =
libmctp::smbus_proto::MCTPSMBusHeader<[u8; MCTP_I2C_HEADER]>;
#[derive(Debug, Clone)]
pub struct MctpI2cEncap {
own_addr: u8,
}
impl MctpI2cEncap {
pub fn new(own_addr: u8) -> Self {
Self { own_addr }
}
pub fn own_addr(&self) -> u8 {
self.own_addr
}
pub fn decode<'f>(
&self,
mut packet: &'f [u8],
pec: bool,
) -> Result<(&'f [u8], u8)> {
if pec {
if packet.is_empty() {
return Err(Error::InvalidInput);
}
let packet_pec;
(packet_pec, packet) = packet.split_last().unwrap();
let calc_pec = smbus_pec::pec(packet);
if calc_pec != *packet_pec {
trace!("Incorrect PEC");
return Err(Error::InvalidInput);
}
}
if packet.len() < MCTP_I2C_HEADER {
return Err(Error::InvalidInput);
}
let (i2c, packet) = packet.split_at(MCTP_I2C_HEADER);
let header = MctpI2cHeader::new_from_buf(i2c.try_into().unwrap());
if header.byte_count() as usize != packet.len() + 1 {
return Err(Error::InvalidInput);
}
if header.command_code() != MCTP_I2C_COMMAND_CODE {
return Err(Error::InvalidInput);
}
Ok((packet, header.source_slave_addr()))
}
pub fn receive_done_pec<'f>(
&self,
packet: &[u8],
mctp: &'f mut Stack,
) -> Result<Option<(MctpMessage<'f>, u8, ReceiveHandle)>> {
let (mctp_packet, i2c_src) = self.decode(packet, false)?;
let m = mctp.receive(mctp_packet)?;
Ok(m.map(|(msg, handle)| (msg, i2c_src, handle)))
}
pub fn send<'f>(
&self,
i2c_dest: u8,
payload: &[u8],
out: &'f mut [u8],
fragmenter: &mut Fragmenter,
) -> SendOutput<'f> {
if out.len() < MCTP_I2C_HEADER {
return SendOutput::failure(Error::InvalidInput, fragmenter);
}
let (i2chead, packet) = out.split_at_mut(MCTP_I2C_HEADER);
let r = fragmenter.fragment(payload, packet);
let packet = match r {
SendOutput::Packet(packet) => packet,
SendOutput::Complete { .. } | SendOutput::Error { .. } => {
return r.unborrowed().unwrap()
}
};
let mut header = MctpI2cHeader::new();
header.set_dest_slave_addr(i2c_dest);
header.set_source_slave_addr(self.own_addr);
header.set_source_read_write(1);
header.set_command_code(MCTP_I2C_COMMAND_CODE);
debug_assert!(packet.len() <= MCTP_I2C_MAXMTU);
header.set_byte_count((packet.len() + 1) as u8);
i2chead.copy_from_slice(&header.0);
let out_len = MCTP_I2C_HEADER + packet.len();
let out = &mut out[..out_len];
SendOutput::Packet(out)
}
pub fn encode<'f>(
&self,
i2c_dest: u8,
inp: &[u8],
out: &'f mut [u8],
pec: bool,
) -> Result<&'f mut [u8]> {
let pec_extra = pec as usize;
let out_len = MCTP_I2C_HEADER + inp.len() + pec_extra;
if out.len() < out_len {
return Err(Error::BadArgument);
}
if inp.len() > MCTP_I2C_MAXMTU {
return Err(Error::BadArgument);
}
let (i2chead, packet) = out.split_at_mut(MCTP_I2C_HEADER);
let mut header = MctpI2cHeader::new();
header.set_dest_slave_addr(i2c_dest);
header.set_source_slave_addr(self.own_addr);
header.set_source_read_write(1);
header.set_command_code(MCTP_I2C_COMMAND_CODE);
header.set_byte_count((1 + inp.len()) as u8);
i2chead.copy_from_slice(&header.0);
packet[..inp.len()].copy_from_slice(inp);
if pec {
let pec_content = &out[..MCTP_I2C_HEADER + inp.len()];
out[MCTP_I2C_HEADER + inp.len()] = smbus_pec::pec(pec_content);
}
Ok(&mut out[..out_len])
}
}
pub struct MctpI2cHandler {
encap: MctpI2cEncap,
send_message: &'static mut Vec<u8, MAX_PAYLOAD>,
send_state: HandlerSendState,
}
impl core::fmt::Debug for MctpI2cHandler {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MctpI2cHandler")
.field("send_state", &self.send_state)
.finish_non_exhaustive()
}
}
impl MctpI2cHandler {
pub fn new(
own_addr: u8,
send_message: &'static mut Vec<u8, MAX_PAYLOAD>,
) -> Self {
Self {
encap: MctpI2cEncap::new(own_addr),
send_message,
send_state: HandlerSendState::Idle,
}
}
pub fn receive<'f>(
&mut self,
packet: &[u8],
mctp: &'f mut Stack,
) -> Result<Option<(MctpMessage<'f>, u8, ReceiveHandle)>> {
self.encap.receive_done_pec(packet, mctp)
}
pub fn is_send_ready(&self) -> bool {
matches!(self.send_state, HandlerSendState::Sending { .. })
}
pub fn is_send_idle(&self) -> bool {
matches!(self.send_state, HandlerSendState::Idle)
}
pub fn send_fill<'f>(&mut self, buf: &'f mut [u8]) -> SendOutput<'f> {
let HandlerSendState::Sending {
fragmenter,
i2c_dest,
} = &mut self.send_state
else {
debug_assert!(false, "called when not !is_send_ready()");
return SendOutput::bare_failure(Error::Other);
};
let r = self
.encap
.send(*i2c_dest, self.send_message, buf, fragmenter);
match r {
SendOutput::Complete { .. } | SendOutput::Error { .. } => {
self.send_message.clear();
self.send_state = HandlerSendState::Idle;
}
SendOutput::Packet(_) => (),
};
r
}
pub fn cancel_send(&mut self) -> Option<AppCookie> {
let mut cookie = None;
if let HandlerSendState::Sending { fragmenter, .. } =
&mut self.send_state
{
cookie = fragmenter.cookie();
}
self.send_message.clear();
self.send_state = HandlerSendState::Idle;
cookie
}
pub fn send_enqueue<F>(
&mut self,
eid: Eid,
typ: MsgType,
tag: Option<Tag>,
ic: MsgIC,
i2c_dest: u8,
cookie: Option<AppCookie>,
mctp: &mut Stack,
fill_msg: F,
) -> Result<()>
where
F: FnOnce(&mut Vec<u8, MAX_PAYLOAD>) -> Option<()>,
{
if !self.is_send_idle() {
return Err(Error::Other);
}
if !self.send_message.is_empty() {
trace!("sendmsg not empty");
}
fill_msg(self.send_message).ok_or(Error::InvalidInput)?;
let fragmenter = mctp.start_send(
eid,
typ,
tag,
true,
ic,
Some(MCTP_I2C_MAXMTU),
cookie,
)?;
self.send_state = HandlerSendState::Sending {
fragmenter,
i2c_dest,
};
Ok(())
}
}
#[derive(Debug)]
enum HandlerSendState {
Idle,
Sending {
fragmenter: Fragmenter,
i2c_dest: u8,
},
}