blob: 0f811edfa5f86e21deb6223044d65b625cf8cc77 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Reusable MCTP echo loop.
//!
//! This crate keeps the echo application policy separate from target wiring:
//! callers create an `openprot_mctp_api::Stack`, then hand it to the helpers
//! here to configure the local EID, open a listener, and echo received payloads
//! back to the sender.
//!
//! The loop can optionally send periodic test messages to a peer endpoint
//! to bootstrap communication when both endpoints are passive listeners.
#![no_std]
use openprot_mctp_api::{MctpClient, MctpError, MctpReqChannel, MctpRespChannel, Stack, StackListener};
/// Default MCTP message type used by the echo app.
pub const ECHO_MSG_TYPE: u8 = 1;
/// Default local EID used by the echo app.
pub const ECHO_EID: u8 = 8;
/// Prepare a stack for echoing by setting the local EID and opening a listener.
pub fn prepare_listener<C: MctpClient>(stack: &Stack<C>) -> Result<StackListener<'_, C>, MctpError> {
stack.set_eid(ECHO_EID)?;
stack.listener(ECHO_MSG_TYPE, 0)
}
/// Prepare a stack with a custom EID and timeout for echoing.
pub fn prepare_listener_with_eid_and_timeout<C: MctpClient>(
stack: &Stack<C>,
eid: u8,
timeout_millis: u32,
) -> Result<StackListener<'_, C>, MctpError> {
stack.set_eid(eid)?;
stack.listener(ECHO_MSG_TYPE, timeout_millis)
}
/// Process one echo receive/send cycle.
///
/// Returns an error from either receive or response send.
pub fn echo_once<L>(listener: &mut L, buf: &mut [u8]) -> Result<(), MctpError>
where
L: openprot_mctp_api::MctpListener,
{
let (_meta, msg, mut resp) = listener.recv(buf)?;
resp.send(msg)
}
/// Run the echo loop forever, echoing received messages.
pub fn run<L>(listener: &mut L) -> !
where
L: openprot_mctp_api::MctpListener,
{
let mut buf = [0u8; 255];
loop {
match echo_once(listener, &mut buf) {
Ok(()) => {}
Err(e) => {
if e.code as u32 != 4 {
// Suppress timeout (code 4) errors; only log other errors
pw_log::error!("echo recv failed: code={}", e.code as u32);
}
}
}
}
}
/// Run the echo loop with periodic sends to a peer endpoint.
///
/// This variant sends a test message every `send_interval` receive attempts,
/// allowing two passive listeners to bootstrap communication.
pub fn run_with_peer<C: MctpClient, L: openprot_mctp_api::MctpListener>(
stack: &Stack<C>,
peer_eid: u8,
listener: &mut L,
) -> ! {
run_with_peer_round_trip_limit(stack, peer_eid, listener, u32::MAX)
}
/// Run the echo loop with periodic sends to a peer endpoint, stopping after
/// `max_round_trips` successful request/response exchanges.
pub fn run_with_peer_round_trip_limit<C: MctpClient, L: openprot_mctp_api::MctpListener>(
stack: &Stack<C>,
peer_eid: u8,
listener: &mut L,
max_round_trips: u32,
) -> ! {
let mut buf = [0u8; 255];
let mut iteration: u32 = 0;
let mut completed_round_trips: u32 = 0;
let send_interval = 10; // Send every 10 iterations
loop {
iteration = iteration.wrapping_add(1);
// Periodically try to send a test message to the peer.
if iteration % send_interval == 0
&& completed_round_trips < max_round_trips
&& let Ok(mut req) = stack.req(peer_eid, 100)
{
let test_msg = b"echo_test";
let _ = req.send(ECHO_MSG_TYPE, test_msg);
// Try to receive response with a short timeout.
if req.recv(&mut buf).is_ok() {
completed_round_trips = completed_round_trips.saturating_add(1);
}
}
// Listen for incoming messages and echo them back.
match echo_once(listener, &mut buf) {
Ok(()) => {}
Err(e) => {
if e.code as u32 != 4 {
// Suppress timeout (code 4) errors; only log other errors
pw_log::error!("echo recv failed: code={}", e.code as u32);
}
}
}
}
}