mctp_estack/
i2c.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2/*
3 * Copyright (c) 2024 Code Construct
4 */
5
6//! MCTP over I2C transport binding
7
8#[allow(unused)]
9use crate::fmt::{debug, error, info, trace, warn};
10
11use crate::{
12    AppCookie, Fragmenter, MctpMessage, MsgIC, ReceiveHandle, SendOutput,
13    Stack, MAX_PAYLOAD,
14};
15use mctp::{Eid, Error, MsgType, Result, Tag};
16
17use heapless::Vec;
18
19pub const MCTP_I2C_COMMAND_CODE: u8 = 0x0f;
20
21const MCTP_I2C_HEADER: usize = 4;
22// bytecount is limited to u8, includes MCTP payload + 1 byte i2c source
23pub const MCTP_I2C_MAXMTU: usize = u8::MAX as usize - 1;
24
25type MctpI2cHeader =
26    libmctp::smbus_proto::MCTPSMBusHeader<[u8; MCTP_I2C_HEADER]>;
27
28/// Simple packet processing to add/remove the 4 byte MCTP-I2C header.
29#[derive(Debug, Clone)]
30pub struct MctpI2cEncap {
31    own_addr: u8,
32}
33
34impl MctpI2cEncap {
35    pub fn new(own_addr: u8) -> Self {
36        Self { own_addr }
37    }
38
39    pub fn own_addr(&self) -> u8 {
40        self.own_addr
41    }
42
43    pub fn decode<'f>(
44        &self,
45        mut packet: &'f [u8],
46        pec: bool,
47    ) -> Result<(&'f [u8], u8)> {
48        if pec {
49            // Remove the pec byte, check it.
50            if packet.is_empty() {
51                return Err(Error::InvalidInput);
52            }
53            let packet_pec;
54            (packet_pec, packet) = packet.split_last().unwrap();
55            let calc_pec = smbus_pec::pec(packet);
56            if calc_pec != *packet_pec {
57                trace!("Incorrect PEC");
58                return Err(Error::InvalidInput);
59            }
60        }
61
62        if packet.len() < MCTP_I2C_HEADER {
63            return Err(Error::InvalidInput);
64        }
65
66        let (i2c, packet) = packet.split_at(MCTP_I2C_HEADER);
67        // OK unwrap, size matches
68        let header = MctpI2cHeader::new_from_buf(i2c.try_into().unwrap());
69        // +1 for i2c source address field
70        if header.byte_count() as usize != packet.len() + 1 {
71            return Err(Error::InvalidInput);
72        }
73
74        if header.command_code() != MCTP_I2C_COMMAND_CODE {
75            return Err(Error::InvalidInput);
76        }
77        Ok((packet, header.source_slave_addr()))
78    }
79
80    /// Handles a MCTP fragment with the PEC already validated
81    ///
82    /// `packet` should omit the PEC byte.
83    /// Returns the MCTP message and the i2c source address.
84    pub fn receive_done_pec<'f>(
85        &self,
86        packet: &[u8],
87        mctp: &'f mut Stack,
88    ) -> Result<Option<(MctpMessage<'f>, u8, ReceiveHandle)>> {
89        let (mctp_packet, i2c_src) = self.decode(packet, false)?;
90
91        // Pass to MCTP stack
92        let m = mctp.receive(mctp_packet)?;
93
94        // Return a (message, i2c_source) tuple on completion
95        Ok(m.map(|(msg, handle)| (msg, i2c_src, handle)))
96    }
97
98    /// `out` must be sized to hold 8+mctp_mtu, to allow for MCTP and I2C headers
99    ///
100    /// TODO: optionally add PEC.
101    pub fn send<'f>(
102        &self,
103        i2c_dest: u8,
104        payload: &[u8],
105        out: &'f mut [u8],
106        fragmenter: &mut Fragmenter,
107    ) -> SendOutput<'f> {
108        if out.len() < MCTP_I2C_HEADER {
109            return SendOutput::failure(Error::InvalidInput, fragmenter);
110        }
111
112        let (i2chead, packet) = out.split_at_mut(MCTP_I2C_HEADER);
113
114        // Get a packet from the fragmenter
115        let r = fragmenter.fragment(payload, packet);
116        let packet = match r {
117            SendOutput::Packet(packet) => packet,
118            // Just return on Complete or Error
119            SendOutput::Complete { .. } | SendOutput::Error { .. } => {
120                return r.unborrowed().unwrap()
121            }
122        };
123
124        // Write the i2c header and return the whole packet
125        let mut header = MctpI2cHeader::new();
126        header.set_dest_slave_addr(i2c_dest);
127        header.set_source_slave_addr(self.own_addr);
128        header.set_source_read_write(1);
129        header.set_command_code(MCTP_I2C_COMMAND_CODE);
130        debug_assert!(packet.len() <= MCTP_I2C_MAXMTU);
131        header.set_byte_count((packet.len() + 1) as u8);
132
133        i2chead.copy_from_slice(&header.0);
134
135        let out_len = MCTP_I2C_HEADER + packet.len();
136        let out = &mut out[..out_len];
137        SendOutput::Packet(out)
138    }
139
140    pub fn encode<'f>(
141        &self,
142        i2c_dest: u8,
143        inp: &[u8],
144        out: &'f mut [u8],
145        pec: bool,
146    ) -> Result<&'f mut [u8]> {
147        let pec_extra = pec as usize;
148        let out_len = MCTP_I2C_HEADER + inp.len() + pec_extra;
149        if out.len() < out_len {
150            return Err(Error::BadArgument);
151        }
152        if inp.len() > MCTP_I2C_MAXMTU {
153            return Err(Error::BadArgument);
154        }
155
156        let (i2chead, packet) = out.split_at_mut(MCTP_I2C_HEADER);
157        let mut header = MctpI2cHeader::new();
158        header.set_dest_slave_addr(i2c_dest);
159        header.set_source_slave_addr(self.own_addr);
160        header.set_source_read_write(1);
161        header.set_command_code(MCTP_I2C_COMMAND_CODE);
162        // Include i2c source address byte in bytecount. No PEC.
163        header.set_byte_count((1 + inp.len()) as u8);
164        i2chead.copy_from_slice(&header.0);
165        packet[..inp.len()].copy_from_slice(inp);
166
167        if pec {
168            let pec_content = &out[..MCTP_I2C_HEADER + inp.len()];
169            out[MCTP_I2C_HEADER + inp.len()] = smbus_pec::pec(pec_content);
170        }
171        Ok(&mut out[..out_len])
172    }
173}
174
175/// A handler for I2C MCTP
176///
177/// One instance should exist for each I2C bus with a MCTP transport.
178pub struct MctpI2cHandler {
179    encap: MctpI2cEncap,
180
181    send_message: &'static mut Vec<u8, MAX_PAYLOAD>,
182    send_state: HandlerSendState,
183}
184
185impl core::fmt::Debug for MctpI2cHandler {
186    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187        f.debug_struct("MctpI2cHandler")
188            .field("send_state", &self.send_state)
189            .finish_non_exhaustive()
190    }
191}
192
193impl MctpI2cHandler {
194    /// Constructs a new `MctpI2cHandler`.
195    ///
196    /// Note that the `heapless::Vec` for `fill_msg` is the version
197    /// specified by `mctp-estack` crate. The re-export
198    /// [`mctp_estack::Vec`](crate::Vec) can be used for API compatibility.
199    pub fn new(
200        own_addr: u8,
201        send_message: &'static mut Vec<u8, MAX_PAYLOAD>,
202    ) -> Self {
203        Self {
204            encap: MctpI2cEncap::new(own_addr),
205            send_message,
206            send_state: HandlerSendState::Idle,
207        }
208    }
209
210    /// Handles receiving an I2C target write
211    ///
212    /// Expects to be passed a `packet` starting from the MCTP I2C header
213    /// (first byte is destination address).
214    /// `buf` should have the PEC byte removed, already checked by callers.
215    ///
216    /// *TODO:* provide separate software PEC check function?
217    pub fn receive<'f>(
218        &mut self,
219        packet: &[u8],
220        mctp: &'f mut Stack,
221    ) -> Result<Option<(MctpMessage<'f>, u8, ReceiveHandle)>> {
222        self.encap.receive_done_pec(packet, mctp)
223    }
224
225    /// Indicates whether data is pending to send
226    pub fn is_send_ready(&self) -> bool {
227        matches!(self.send_state, HandlerSendState::Sending { .. })
228    }
229
230    /// Indicates whether the send queue is idle, ready for an application to enqueue a new message
231    pub fn is_send_idle(&self) -> bool {
232        matches!(self.send_state, HandlerSendState::Idle)
233    }
234
235    /// Fill a buffer with a packet to send over the i2c bus.
236    ///
237    /// The `send_complete` closure is called when an entire message completes sending.
238    /// It is called with `Some(Tag)` on success (with the tag that was sent)
239    /// or `None` on failure. The cookie is the one provided to [`send_enqueue`](Self::send_enqueue).
240    pub fn send_fill<'f>(&mut self, buf: &'f mut [u8]) -> SendOutput<'f> {
241        let HandlerSendState::Sending {
242            fragmenter,
243            i2c_dest,
244        } = &mut self.send_state
245        else {
246            debug_assert!(false, "called when not !is_send_ready()");
247            return SendOutput::bare_failure(Error::Other);
248        };
249
250        let r = self
251            .encap
252            .send(*i2c_dest, self.send_message, buf, fragmenter);
253        match r {
254            SendOutput::Complete { .. } | SendOutput::Error { .. } => {
255                self.send_message.clear();
256                self.send_state = HandlerSendState::Idle;
257            }
258            SendOutput::Packet(_) => (),
259        };
260        r
261    }
262
263    pub fn cancel_send(&mut self) -> Option<AppCookie> {
264        let mut cookie = None;
265        if let HandlerSendState::Sending { fragmenter, .. } =
266            &mut self.send_state
267        {
268            cookie = fragmenter.cookie();
269        }
270        self.send_message.clear();
271        self.send_state = HandlerSendState::Idle;
272        cookie
273    }
274
275    /// Provides a MCTP message to send.
276    ///
277    /// The provided closure will fill out the message buffer, returning
278    /// `Some(())` on success. If the closure fails it returns `None`, and
279    /// `send_enqueue()` will return `mctp::Error::InvalidInput`.
280    ///
281    /// `send_enqueue()` must only be called when `is_send_idle()` is true.
282    /// TODO `fill_msg` will take something that isn't a Vec.
283    ///
284    /// Note that the `heapless::Vec` for `fill_msg` is the version
285    /// specified by `mctp-estack` crate. The re-export
286    /// [`mctp_estack::Vec`](crate::Vec) can be used for API compatibility.
287    pub fn send_enqueue<F>(
288        &mut self,
289        eid: Eid,
290        typ: MsgType,
291        tag: Option<Tag>,
292        ic: MsgIC,
293        i2c_dest: u8,
294        cookie: Option<AppCookie>,
295        mctp: &mut Stack,
296        fill_msg: F,
297    ) -> Result<()>
298    where
299        F: FnOnce(&mut Vec<u8, MAX_PAYLOAD>) -> Option<()>,
300    {
301        if !self.is_send_idle() {
302            return Err(Error::Other);
303        }
304        // debug_assert!(self.send_message.is_empty());
305        if !self.send_message.is_empty() {
306            trace!("sendmsg not empty");
307        }
308
309        // Retrieve data from the app. This may be a simple copy, or
310        // going through a kernel to fetch the message.
311        fill_msg(self.send_message).ok_or(Error::InvalidInput)?;
312
313        let fragmenter = mctp.start_send(
314            eid,
315            typ,
316            tag,
317            true,
318            ic,
319            Some(MCTP_I2C_MAXMTU),
320            cookie,
321        )?;
322        self.send_state = HandlerSendState::Sending {
323            fragmenter,
324            i2c_dest,
325        };
326        Ok(())
327    }
328}
329
330#[derive(Debug)]
331enum HandlerSendState {
332    Idle,
333    Sending {
334        fragmenter: Fragmenter,
335        i2c_dest: u8,
336    },
337}