#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
use core::future::Future;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Eid(pub u8);
impl Eid {
pub const fn new_normal(eid: u8) -> Result<Eid> {
if eid <= 7 || eid == 0xff {
Err(Error::BadArgument)
} else {
Ok(Eid(eid))
}
}
}
impl core::fmt::Display for Eid {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.fmt(fmt)
}
}
pub const MCTP_ADDR_ANY: Eid = Eid(0xff);
pub const MCTP_ADDR_NULL: Eid = Eid(0x00);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TagValue(pub u8);
pub const MCTP_TAG_OWNER: u8 = 0x08;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MsgType(pub u8);
impl core::fmt::Display for MsgType {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.fmt(fmt)
}
}
#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MsgIC(pub bool);
pub const MCTP_TYPE_CONTROL: MsgType = MsgType(0x00);
pub const MCTP_TYPE_PLDM: MsgType = MsgType(0x01);
pub const MCTP_TYPE_NCSI: MsgType = MsgType(0x02);
pub const MCTP_TYPE_ETHERNET: MsgType = MsgType(0x03);
pub const MCTP_TYPE_NVME: MsgType = MsgType(0x04);
pub const MCTP_TYPE_SPDM: MsgType = MsgType(0x05);
pub const MCTP_TYPE_SPDM_SECURED: MsgType = MsgType(0x06);
pub const MCTP_TYPE_CXL_FM: MsgType = MsgType(0x07);
pub const MCTP_TYPE_CXL_CCI: MsgType = MsgType(0x08);
pub const MCTP_TYPE_PCIE_MI: MsgType = MsgType(0x09);
pub const MCTP_TYPE_VENDOR_PCIE: MsgType = MsgType(0x7e);
pub const MCTP_TYPE_VENDOR_IANA: MsgType = MsgType(0x7f);
pub const MCTP_HEADER_VERSION_1: u8 = 1;
pub const MCTP_MIN_MTU: usize = 64;
pub const MCTP_SEQ_MASK: u8 = 0x03;
pub const MCTP_TAG_MAX: u8 = 7;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Tag {
Unowned(TagValue),
Owned(TagValue),
}
impl Tag {
pub fn tag(&self) -> TagValue {
match self {
Self::Unowned(tag) | Self::Owned(tag) => *tag,
}
}
pub fn is_owner(&self) -> bool {
matches!(self, Self::Owned(_))
}
}
impl core::fmt::Display for Tag {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
Tag::Owned(v) => write!(fmt, "TO,{:x}", v.0),
Tag::Unowned(v) => write!(fmt, "!TO,{:x}", v.0),
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
TxFailure,
RxFailure,
TimedOut,
BadArgument,
InvalidInput,
TagUnavailable,
Unreachable,
AddrInUse,
NoSpace,
Unsupported,
Other,
InternalError,
#[cfg(feature = "std")]
Io(std::io::Error),
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl core::fmt::Display for Error {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
#[cfg(feature = "std")]
Self::Io(i) => write!(fmt, "MCTP IO Error: {}", i),
_ => write!(fmt, "MCTP Error: {:?}", self),
}
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(e: Error) -> std::io::Error {
std::io::Error::other(e)
}
}
pub type Result<T> = core::result::Result<T, Error>;
pub trait ReqChannel {
fn send_vectored(
&mut self,
typ: MsgType,
integrity_check: MsgIC,
bufs: &[&[u8]],
) -> Result<()>;
fn send(&mut self, typ: MsgType, buf: &[u8]) -> Result<()> {
self.send_vectored(typ, MsgIC(false), &[buf])
}
fn recv<'f>(
&mut self,
buf: &'f mut [u8],
) -> Result<(MsgType, MsgIC, &'f mut [u8])>;
fn remote_eid(&self) -> Eid;
}
#[allow(missing_docs)]
pub trait AsyncReqChannel {
fn send_vectored(
&mut self,
typ: MsgType,
integrity_check: MsgIC,
bufs: &[&[u8]],
) -> impl Future<Output = Result<()>>;
fn send(
&mut self,
typ: MsgType,
buf: &[u8],
) -> impl Future<Output = Result<()>> {
async move { self.send_vectored(typ, MsgIC(false), &[buf]).await }
}
fn recv<'f>(
&mut self,
buf: &'f mut [u8],
) -> impl Future<Output = Result<(MsgType, MsgIC, &'f mut [u8])>>;
fn remote_eid(&self) -> Eid;
}
pub trait RespChannel {
type ReqChannel: ReqChannel;
fn send_vectored(
&mut self,
integrity_check: MsgIC,
bufs: &[&[u8]],
) -> Result<()>;
fn send(&mut self, buf: &[u8]) -> Result<()> {
self.send_vectored(MsgIC(false), &[buf])
}
fn remote_eid(&self) -> Eid;
fn req_channel(&self) -> Result<Self::ReqChannel>;
}
#[allow(missing_docs)]
pub trait AsyncRespChannel {
type ReqChannel<'a>: AsyncReqChannel
where
Self: 'a;
fn send_vectored(
&mut self,
integrity_check: MsgIC,
bufs: &[&[u8]],
) -> impl Future<Output = Result<()>>;
fn send(&mut self, buf: &[u8]) -> impl Future<Output = Result<()>> {
async move { self.send_vectored(MsgIC(false), &[buf]).await }
}
fn remote_eid(&self) -> Eid;
fn req_channel(&self) -> Result<Self::ReqChannel<'_>>;
}
pub trait Listener {
type RespChannel<'a>: RespChannel
where
Self: 'a;
fn recv<'f>(
&mut self,
buf: &'f mut [u8],
) -> Result<(MsgType, MsgIC, &'f mut [u8], Self::RespChannel<'_>)>;
}
#[allow(missing_docs)]
pub trait AsyncListener {
type RespChannel<'a>: AsyncRespChannel
where
Self: 'a;
fn recv<'f>(
&mut self,
buf: &'f mut [u8],
) -> impl Future<
Output = Result<(MsgType, MsgIC, &'f mut [u8], Self::RespChannel<'_>)>,
>;
}
const MCTP_IC_MASK: u8 = 0x80;
pub fn encode_type_ic(typ: MsgType, ic: MsgIC) -> u8 {
let ic_val = if ic.0 { MCTP_IC_MASK } else { 0 };
typ.0 & !MCTP_IC_MASK | ic_val
}
pub fn decode_type_ic(ic_typ: u8) -> (MsgType, MsgIC) {
(
MsgType(ic_typ & !MCTP_IC_MASK),
MsgIC((ic_typ & MCTP_IC_MASK) != 0),
)
}