blob: c3195d17fb3b08f21cad1956896f60b08714e1c2 [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 i2c_api::{BusIndex, I2cTargetClient, TargetMessage};
use i2c_client::IpcI2cClient;
use openprot_mctp_transport_i2c::{I2cSender, MctpI2cReceiver};
use pw_status::Result;
use userspace::entry;
use userspace::syscall::{self, Signals};
use userspace::time::Instant;
use app_mctp_server::handle;
const OWN_EID: u8 = 8;
const OWN_I2C_ADDR: u8 = 0x10;
// ---------------------------------------------------------------------------
// Server loop
// ---------------------------------------------------------------------------
fn mctp_server_loop() -> Result<()> {
pw_log::info!("MCTP server starting");
// I2C notification client: receives slave-mode interrupts via Signals::USER.
let mut i2c_notify = IpcI2cClient::new(handle::I2C);
i2c_notify
.register_notification(BusIndex::BUS_0, 0)
.map_err(|_| pw_status::Error::Internal)?;
// Separate handle for the sender — I2cSender takes ownership.
let sender = I2cSender::new(IpcI2cClient::new(handle::I2C), BusIndex::BUS_0, OWN_I2C_ADDR);
let receiver = MctpI2cReceiver::new(OWN_I2C_ADDR);
let mut server = openprot_mctp_server::Server::<_, 16>::new(
mctp::Eid(OWN_EID),
0,
sender,
);
let mut request_buf = [0u8; MAX_REQUEST_SIZE];
let mut response_buf = [0u8; MAX_RESPONSE_SIZE];
let mut recv_buf = [0u8; MAX_PAYLOAD_SIZE];
// Register both event sources with the WaitGroup.
// user_data=0 → IPC from a client (MCTP channel READABLE)
// user_data=1 → I2C slave notification (I2C channel USER)
syscall::wait_group_add(handle::WG, handle::MCTP, Signals::READABLE, 0usize)?;
syscall::wait_group_add(handle::WG, handle::I2C, Signals::USER, 1usize)?;
loop {
let ev = syscall::object_wait(handle::WG, Signals::READABLE, Instant::MAX)?;
if ev.user_data == 1 {
// Inbound I2C data: drain pending messages, decode I2C framing,
// feed raw MCTP packets into the router.
let mut msgs = [TargetMessage::default(); 1];
if let Ok(n) = i2c_notify.get_pending_messages(BusIndex::BUS_0, &mut msgs) {
for msg in &msgs[..n] {
if let Ok((pkt, _src_addr)) = receiver.decode(msg) {
let _ = server.inbound(pkt);
}
}
}
} else {
// IPC from a client — channel_read is non-blocking here because
// the WaitGroup only fires after READABLE is set.
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])?;
}
}
}
// ---------------------------------------------------------------------------
// 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 {}
}