In-memory loopback transport for MCTP testing using fixed-size buffers (no dynamic allocation).
This crate provides a formalized loopback transport for MCTP that enables two endpoints to communicate entirely in-memory without any physical transport (I2C, PCIe, etc.).
Key feature: Uses fixed-size buffers to mirror the memory behavior of transport-i2c, making it compatible with no_std embedded environments without requiring a global allocator.
This is useful for:
The loopback transport consists of:
MctpClient implementation for each endpointPacketBufferMirroring transport-i2c:
no_std without requiring allocuse openprot_mctp_transport_loopback::{LoopbackPair, PacketBuffer}; use openprot_mctp_api::MctpClient; use core::cell::RefCell; // Create packet buffers (stack-allocated, no heap required) let packets_a = RefCell::new(PacketBuffer::new()); let packets_b = RefCell::new(PacketBuffer::new()); // Create a loopback pair with EIDs 8 and 42 let pair = LoopbackPair::<16>::new(8, 42, &packets_a, &packets_b); // Get client handles for each endpoint let client_a = pair.client_a(); let client_b = pair.client_b(); // Use clients normally let handle_a = client_a.req(42).unwrap(); client_a.send(Some(handle_a), 1, None, None, false, b"hello").unwrap(); // Transfer packets from A to B pair.transfer_a_to_b(); // Receive on B let handle_b = client_b.listener(1).unwrap(); let mut buf = [0u8; 256]; let meta = client_b.recv(handle_b, 0, &mut buf).unwrap(); assert_eq!(&buf[..meta.payload_size], b"hello");
This transport uses the same memory pattern as transport-i2c:
transport-i2c sender:
let mut pkt = [0u8; mctp_lib::serial::MTU_MAX]; // Stack-allocated
transport-loopback:
pub struct PacketBuffer { packets: [[u8; 255]; 16], // Stack-allocated lengths: [usize; 16], count: usize, }
Both avoid dynamic allocation entirely, making them suitable for embedded environments without heap allocation.
The original transport-loopback used Vec<Vec<u8>> which required:
extern crate alloc#[global_allocator]This version uses fixed-size arrays which:
no_std without alloctransport-i2cpub const MAX_PACKET_SIZE: usize = 255; // MCTP MTU pub const MAX_BUFFERED_PACKETS: usize = 16; // Max packets per endpoint
To change packet buffer depth, modify MAX_BUFFERED_PACKETS in lib.rs.
See examples/spdm_usage.md for full SPDM-over-MCTP testing examples.
Quick example:
use openprot_mctp_transport_loopback::{LoopbackPair, PacketBuffer}; use openprot_spdm_transport_mctp::MctpSpdmTransport; use core::cell::RefCell; // Create loopback infrastructure (no heap required) let packets_a = RefCell::new(PacketBuffer::new()); let packets_b = RefCell::new(PacketBuffer::new()); let pair = LoopbackPair::<16>::new(8, 42, &packets_a, &packets_b); // Create SPDM transports let mut spdm_requester = MctpSpdmTransport::new_requester(pair.client_a(), 42); let mut spdm_responder = MctpSpdmTransport::new_responder(pair.client_b()); // Initialize spdm_requester.init_sequence().unwrap(); spdm_responder.init_sequence().unwrap(); // Test SPDM protocol messages...
The implementation includes comprehensive unit tests:
# Run tests (uses std for test harness) bazel test //services/mctp/transport-loopback:transport_loopback_test
Tests verify:
Licensed under the Apache-2.0 license.