Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ mctp-estack = { git = "https://github.com/OpenPRoT/mctp-rs.git", branch = "sync-
mctp = { git = "https://github.com/OpenPRoT/mctp-rs.git", branch = "sync-features", default-features = false }

[dev-dependencies]
mctp = { git = "https://github.com/OpenPRoT/mctp-rs.git", branch = "sync-features" }
standalone = { path = "standalone" }

[package.metadata.docs.rs]
all-features = true
65 changes: 65 additions & 0 deletions examples/echo/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2025
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Example that listens for a request and echoes the payload in the response.
//!
//! Uses the standalone std implementation for the Stack and attaches to a specified serial port.
//! (Use a tool like _socat_ to attach to the linux MCTP stack through PTYs)
//!
//! Errors after the specified timeout.

const MSG_TYPE: MsgType = MsgType(1);
const OWN_EID: Eid = Eid(8);
const TIMEOUT_SECS: u64 = 10;
const TTY_PATH: &str = "pts1";

use std::{fs::File, thread::spawn, time::Duration};

use mctp::{Eid, Listener, MsgType, RespChannel};
use standalone::{
Stack,
serial_sender::IoSerialSender,
util::{inbound_loop, update_loop},
};

fn main() {
let serial = File::options()
.write(true)
.read(true)
.open(TTY_PATH)
.unwrap();

let serial_sender = IoSerialSender::new(serial.try_clone().unwrap());

let mut stack = Stack::new(serial_sender);

stack.set_eid(OWN_EID).unwrap();

let update_stack = stack.clone();
spawn(move || update_loop(update_stack));

let driver_stack = stack.clone();
spawn(move || inbound_loop(driver_stack, serial));

let mut listener = stack
.listener(MSG_TYPE, Some(Duration::from_secs(TIMEOUT_SECS)))
.unwrap();

let mut buf = [0; 256];
let (_, _, msg, mut rsp) = listener.recv(&mut buf).unwrap();

println!("Got message: {:#x?}", msg);

rsp.send(msg).unwrap();
}
33 changes: 22 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,20 @@ impl<S: Sender, const MAX_LISTENER_HANDLES: usize, const MAX_REQ_HANDLES: usize>
/// Provide an incoming packet to the router.
///
/// This expects a single MCTP packet, without a transport binding header.
pub fn inbound(&mut self, pkt: &[u8]) -> Result<()> {
///
/// Returns `Ok(Some(AppCookie))` for a associated listener or request,
/// or `Ok(None)` if the message was discarded.
pub fn inbound(&mut self, pkt: &[u8]) -> Result<Option<AppCookie>> {
let own_eid = self.stack.eid();
let Some(mut msg) = self.stack.receive(pkt)? else {
return Ok(());
return Ok(None);
};

if msg.dest != own_eid {
// Drop messages if eid does not match (for now)
return Ok(());
if msg.dest != own_eid && msg.dest != Eid(0) {
// Drop messages if eid does not match (for now).
// EID 0 messages are used for physical addressing
// and will thus be processed.
return Ok(None);
}

match msg.tag {
Expand All @@ -113,7 +118,7 @@ impl<S: Sender, const MAX_LISTENER_HANDLES: usize, const MAX_REQ_HANDLES: usize>
.is_some_and(|i| self.requests.get(i).is_some_and(|r| r.is_some()))
{
msg.retain();
return Ok(());
return Ok(Some(cookie));
}
// In this case an unowned message not associated with a request was received.
// This might happen if this endpoint was intended to route the packet to a different
Expand All @@ -124,16 +129,17 @@ impl<S: Sender, const MAX_LISTENER_HANDLES: usize, const MAX_REQ_HANDLES: usize>
// check for matching listeners and retain with cookie
for i in 0..self.listeners.len() {
if self.listeners.get(i).ok_or(Error::InternalError)? == &Some(msg.typ) {
msg.set_cookie(Some(Self::listener_cookie_from_index(i)));
let cookie = Some(Self::listener_cookie_from_index(i));
msg.set_cookie(cookie);
msg.retain();
return Ok(());
return Ok(cookie);
}
}
}
}

// Return Ok(()) even if a message has been discarded
Ok(())
Ok(None)
}

/// Allocate a new request "_Handle_"
Expand Down Expand Up @@ -221,12 +227,14 @@ impl<S: Sender, const MAX_LISTENER_HANDLES: usize, const MAX_REQ_HANDLES: usize>
Some(cookie),
)?;

self.sender.send_vectored(frag, bufs)
self.sender.send_vectored(eid, frag, bufs)
}

/// Receive a message associated with a [`AppCookie`]
///
/// Returns `None` when no message is available for the listener/request.
///
/// The message can be retained and received at a later point again (see [MctpMessage::retain()]).
pub fn recv(&mut self, cookie: AppCookie) -> Option<mctp_estack::MctpMessage<'_>> {
self.stack.get_deferred_bycookie(&[cookie])
}
Expand Down Expand Up @@ -326,7 +334,8 @@ impl<S: Sender, const MAX_LISTENER_HANDLES: usize, const MAX_REQ_HANDLES: usize>
/// Implemented by a transport binding for sending packets.
pub trait Sender {
/// Send a packet fragmented by `fragmenter` with the payload `payload`
fn send_vectored(&mut self, fragmenter: Fragmenter, payload: &[&[u8]]) -> Result<Tag>;
fn send_vectored(&mut self, eid: Eid, fragmenter: Fragmenter, payload: &[&[u8]])
-> Result<Tag>;
/// Get the MTU of a MCTP packet fragment (without transport headers)
fn get_mtu(&self) -> usize;
}
Expand All @@ -345,6 +354,7 @@ mod test {
impl Sender for DoNothingSender {
fn send_vectored(
&mut self,
_eid: Eid,
fragmenter: mctp_estack::fragment::Fragmenter,
payload: &[&[u8]],
) -> core::result::Result<mctp::Tag, mctp::Error> {
Expand All @@ -364,6 +374,7 @@ mod test {
impl<const MTU: usize> Sender for BufferSender<'_, MTU> {
fn send_vectored(
&mut self,
_eid: Eid,
mut fragmenter: mctp_estack::fragment::Fragmenter,
payload: &[&[u8]],
) -> core::result::Result<mctp::Tag, mctp::Error> {
Expand Down
13 changes: 13 additions & 0 deletions standalone/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "standalone"
version = "0.1.0"
edition = "2024"
authors = ["OpenPRoT Contributors"]
license = "Apache-2.0"
repository = "https://github.com/OpenPRoT/mctp-lib"
description = "Standalone std implementation of mctp-lib"

[dependencies]
mctp-lib = { path = "../" }
mctp = { git = "https://github.com/OpenPRoT/mctp-rs.git", branch = "sync-features" }
embedded-io-adapters = { version = "0.6.0", features = ["std"] }
Loading
Loading