blob: 80242dcf73b1c481b5f9871953c85e44348afdbb [file]
// Licensed under the Apache-2.0 license
//! MCTP Server — IPC Dispatch Loop
//!
//! Userspace service that receives MCTP requests over a Pigweed IPC channel,
//! dispatches them to the MCTP server core, and responds with results.
//!
//! # Architecture
//!
//! ```text
//! ┌─ Client ──────────────────────────┐
//! │ channel_transact(request) │
//! └──────────────┬────────────────────┘
//! │ IPC channel
//! ▼
//! ┌─ This Server ─────────────────────┐
//! │ object_wait(READABLE) │
//! │ channel_read → MctpRequestHeader │
//! │ dispatch_mctp_op(op, server) │
//! │ channel_respond ← MctpRespHeader │
//! └──────────────┬────────────────────┘
//! │ mctp-stack Router
//! ▼
//! ┌─ I2C Transport ──────────────────┐
//! │ I2cSender → I2C Server IPC │
//! └──────────────────────────────────┘
//! ```
//!
//! # IPC Pattern
//!
//! Follows the same loop as `services/i2c/server/src/main.rs`:
//!
//! 1. `object_wait(handle, READABLE)` — block until a client sends a request
//! 2. `channel_read(handle)` — read the raw request bytes
//! 3. Parse `MctpRequestHeader`, dispatch via `dispatch_mctp_op`
//! 4. `channel_respond(handle)` — send response header + data
//!
//! # Handle Binding
//!
//! The IPC handle is provided by the `app_package` Bazel rule, which generates
//! `app_mctp_server::handle::MCTP` from the system configuration.
#![no_main]
#![no_std]
use openprot_mctp_api::wire::{MctpRequestHeader, MAX_REQUEST_SIZE, MAX_RESPONSE_SIZE, MAX_PAYLOAD_SIZE};
use openprot_mctp_api::ResponseCode;
use openprot_mctp_server::dispatch;
use pw_status::Result;
use userspace::entry;
use userspace::syscall::{self, Signals};
use userspace::time::Instant;
use app_mctp_server::handle;
// ---------------------------------------------------------------------------
// Server loop
// ---------------------------------------------------------------------------
fn mctp_server_loop() -> Result<()> {
pw_log::info!("MCTP server starting");
// TODO(Phase 6): Initialize transport binding.
// The I2C sender needs an IpcI2cClient bound to the I2C server channel.
// For now, we create a stub sender that will be replaced with a real
// I2cSender<IpcI2cClient> once the I2C channel handle is wired up.
//
// let i2c_client = i2c_client::IpcI2cClient::new(handle::I2C);
// let sender = I2cSender::new(i2c_client, BusIndex::BUS_0, OWN_I2C_ADDR);
// let mut server = Server::new(Eid(OWN_EID), 0, sender);
// For initial bring-up, use a no-op sender so the IPC dispatch loop
// can be tested without I2C hardware.
let mut server = openprot_mctp_server::Server::<NoopSender, 16>::new(
mctp::Eid(8), // default EID
0,
NoopSender,
);
let mut request_buf = [0u8; MAX_REQUEST_SIZE];
let mut response_buf = [0u8; MAX_RESPONSE_SIZE];
let mut recv_buf = [0u8; MAX_PAYLOAD_SIZE];
loop {
// Block until a client sends a request
syscall::object_wait(handle::MCTP, Signals::READABLE, Instant::MAX)?;
// Read the request
let len = syscall::channel_read(handle::MCTP, 0, &mut request_buf)?;
if len < MctpRequestHeader::SIZE {
// Truncated request — respond with error
let resp = openprot_mctp_api::wire::MctpResponseHeader::error(ResponseCode::BadArgument);
response_buf[..openprot_mctp_api::wire::MctpResponseHeader::SIZE]
.copy_from_slice(&resp.to_bytes());
syscall::channel_respond(
handle::MCTP,
&response_buf[..openprot_mctp_api::wire::MctpResponseHeader::SIZE],
)?;
continue;
}
// Dispatch and respond
let response_len = dispatch::dispatch_mctp_op(
&request_buf[..len],
&mut response_buf,
&mut server,
&mut recv_buf,
);
syscall::channel_respond(handle::MCTP, &response_buf[..response_len])?;
}
}
// ---------------------------------------------------------------------------
// No-op sender for initial bring-up
// ---------------------------------------------------------------------------
/// Stub sender that does nothing. Used for IPC dispatch testing before
/// the I2C transport is wired up.
struct NoopSender;
impl mctp_stack::Sender for NoopSender {
fn send_vectored(
&mut self,
_fragmenter: mctp_stack::fragment::Fragmenter,
_payload: &[&[u8]],
) -> mctp::Result<mctp::Tag> {
Err(mctp::Error::TxFailure)
}
fn get_mtu(&self) -> usize {
64
}
}
// ---------------------------------------------------------------------------
// Entry point
// ---------------------------------------------------------------------------
#[entry]
fn entry() -> ! {
if let Err(e) = mctp_server_loop() {
pw_log::error!("MCTP server error: {}", e as u32);
let _ = syscall::debug_shutdown(Err(e));
}
loop {}
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}