mctp_estack/
usb.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2/*
3 * MCTP common types and traits.
4 *
5 * Copyright (c) 2024 Code Construct
6 */
7
8//! MCTP over USB transport binding
9//!
10//! This implements DSP0283 1.0.1
11
12#![allow(unused)]
13
14use crate::{
15    AppCookie, MctpMessage, ReceiveHandle, SendOutput, Stack, MAX_PAYLOAD,
16};
17use heapless::Vec;
18use mctp::{Eid, Error, MsgIC, MsgType, Result, Tag};
19
20#[cfg(feature = "defmt")]
21#[allow(unused)]
22use defmt::{debug, error, info, trace, warn};
23
24#[cfg(feature = "log")]
25#[allow(unused)]
26use log::{debug, error, info, trace, warn};
27
28const HDR_LEN: usize = 4;
29const MCTP_USB_MTU_MAX: usize = u8::MAX as usize - HDR_LEN;
30const TX_XFER_SIZE: usize = 512;
31
32pub struct MctpUsbHandler {
33    tx_msg: Vec<u8, MAX_PAYLOAD>,
34    tx_xfer: [u8; TX_XFER_SIZE],
35}
36
37pub trait MctpUsbXfer {
38    fn send_xfer(&mut self, buf: &[u8]) -> Result<()>;
39}
40
41impl MctpUsbHandler {
42    pub fn new() -> Self {
43        Self {
44            tx_msg: Vec::new(),
45            tx_xfer: [0u8; TX_XFER_SIZE],
46        }
47    }
48
49    /// Returns (mctp_packet, remainder).
50    ///
51    /// `xfer` an input buffer, starting at a MCTP over USB header
52    /// and containing at least one MCTP packet.
53    /// `mctp_packet` is the portion after the MCTP over USB header.
54    /// `remainder` is the remaining portion of xfer (which can be
55    /// passed to a subsequent `decode()` call).
56    pub fn decode(xfer: &[u8]) -> Result<(&[u8], &[u8])> {
57        let (hdr, data) =
58            xfer.split_at_checked(HDR_LEN).ok_or(Error::RxFailure)?;
59
60        if hdr[0..2] != [0x1a, 0xb4] {
61            debug!("mismatch: {:x} {:x}", hdr[0], hdr[1]);
62            return Err(Error::RxFailure);
63        }
64
65        let Some(len) = (hdr[3] as usize).checked_sub(HDR_LEN) else {
66            trace!("Mismatch mctp usb len");
67            return Err(Error::RxFailure);
68        };
69
70        let Some(data) = data.split_at_checked(len) else {
71            trace!("Short mctp usb packet");
72            return Err(Error::RxFailure);
73        };
74        Ok(data)
75    }
76
77    pub fn receive<'f>(
78        xfer: &[u8],
79        mctp: &'f mut Stack,
80    ) -> Result<Option<(MctpMessage<'f>, ReceiveHandle)>> {
81        // debug!("xfer: {xfer:02x?}");
82        // TODO remainder in case of multiple MCTP per USB packet
83        let (data, _rem) = Self::decode(xfer)?;
84        mctp.receive(data)
85    }
86
87    pub fn send_fill<F>(
88        &mut self,
89        eid: Eid,
90        typ: MsgType,
91        tag: Option<Tag>,
92        ic: MsgIC,
93        cookie: Option<AppCookie>,
94        xfer: &mut impl MctpUsbXfer,
95        mctp: &mut Stack,
96        fill_msg: F,
97    ) -> SendOutput<'_>
98    where
99        F: FnOnce(&mut Vec<u8, MAX_PAYLOAD>) -> Option<()>,
100    {
101        self.tx_msg.clear();
102        if fill_msg(&mut self.tx_msg).is_none() {
103            return SendOutput::Error {
104                err: Error::Other,
105                cookie: None,
106            };
107        }
108
109        let res = mctp.start_send(
110            eid,
111            typ,
112            tag,
113            true,
114            ic,
115            Some(MCTP_USB_MTU_MAX),
116            cookie,
117        );
118        let mut fragmenter = match res {
119            Ok(f) => f,
120            Err(err) => return SendOutput::Error { err, cookie: None },
121        };
122
123        loop {
124            let (mut hdr, mut data) = self.tx_xfer.split_at_mut(HDR_LEN);
125            let r = fragmenter.fragment(&self.tx_msg, data);
126            let len = match r {
127                SendOutput::Packet(p) => p.len(),
128                SendOutput::Complete { .. } | SendOutput::Error { .. } => {
129                    return r.unborrowed().unwrap()
130                }
131            };
132            if Self::header(len, hdr).is_err() {
133                return SendOutput::Error {
134                    err: Error::InternalError,
135                    cookie: None,
136                };
137            }
138            let slice = &self.tx_xfer[0..len + 4];
139            let res = xfer.send_xfer(slice);
140            if let Err(_e) = res {
141                trace!("USB transfer error");
142                return SendOutput::Error {
143                    err: Error::TxFailure,
144                    cookie: None,
145                };
146            }
147        }
148    }
149
150    /// Creates a MCTP over USB Header.
151    ///
152    /// `mctplen` is the length of the remaining MCTP packet
153    /// after the header.
154    /// `hdr` must be a 4 byte slice.
155    pub fn header(mctp_len: usize, hdr: &mut [u8]) -> Result<()> {
156        if hdr.len() != 4 {
157            return Err(Error::BadArgument);
158        }
159
160        let usb_len: u8 = mctp_len
161            .checked_add(4)
162            .ok_or(Error::BadArgument)?
163            .try_into()
164            .map_err(|_| Error::BadArgument)?;
165
166        hdr[0] = 0x1a;
167        hdr[1] = 0xb4;
168        hdr[2] = 0;
169        hdr[3] = usb_len;
170        Ok(())
171    }
172}
173
174impl Default for MctpUsbHandler {
175    fn default() -> Self {
176        Self::new()
177    }
178}