blob: 15a11f60447c7d62582d22eb11087c8e20ea8d98 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! MCTP IPC request dispatch.
//!
//! Decodes wire-protocol requests and dispatches them to the [`Server`].
//! This is the server-side counterpart of `openprot-mctp-client`.
use openprot_mctp_api::wire::{
self, flags, MctpOp, MctpRequestHeader,
};
use openprot_mctp_api::ResponseCode;
use crate::{Sender, Server};
/// Dispatch an IPC request to the MCTP server.
///
/// Decodes the request header, calls the appropriate `Server` method,
/// and encodes the response into `response`. Returns the response length.
///
/// This is the MCTP equivalent of `dispatch_i2c_op` in the I2C server.
pub fn dispatch_mctp_op<S: Sender, const N: usize>(
request: &[u8],
response: &mut [u8],
server: &mut Server<S, N>,
recv_buf: &mut [u8],
) -> usize {
let header = match MctpRequestHeader::from_bytes(request) {
Some(h) => h,
None => return encode_error(response, ResponseCode::BadArgument),
};
let Some(op) = header.operation() else {
return encode_error(response, ResponseCode::BadArgument);
};
match op {
MctpOp::SetEid => match server.set_eid(header.eid) {
Ok(()) => encode_success(response),
Err(e) => encode_error(response, e.code),
},
MctpOp::GetEid => {
let eid = server.get_eid();
wire::encode_get_eid_response(response, eid)
.unwrap_or_else(|_| encode_error(response, ResponseCode::InternalError))
}
MctpOp::Listener => match server.listener(header.msg_type) {
Ok(handle) => wire::encode_handle_response(response, handle.0)
.unwrap_or_else(|_| encode_error(response, ResponseCode::InternalError)),
Err(e) => encode_error(response, e.code),
},
MctpOp::Req => match server.req(header.eid) {
Ok(handle) => wire::encode_handle_response(response, handle.0)
.unwrap_or_else(|_| encode_error(response, ResponseCode::InternalError)),
Err(e) => encode_error(response, e.code),
},
MctpOp::Recv => {
let handle = openprot_mctp_api::Handle(header.handle);
match server.try_recv(handle, recv_buf) {
Some(meta) => {
let payload = &recv_buf[..meta.payload_size];
wire::encode_recv_response(
response,
meta.msg_type,
meta.msg_ic,
meta.remote_eid,
meta.msg_tag,
payload,
)
.unwrap_or_else(|_| encode_error(response, ResponseCode::InternalError))
}
None => {
// No message available yet.
// In a real Pigweed server, we'd register a pending recv
// and respond later. For now, return TimedOut.
encode_error(response, ResponseCode::TimedOut)
}
}
}
MctpOp::Send => {
let handle = if header.flags & flags::HAS_HANDLE != 0 {
Some(openprot_mctp_api::Handle(header.handle))
} else {
None
};
let eid = if header.flags & flags::HAS_EID != 0 {
Some(header.eid)
} else {
None
};
let tag = if header.flags & flags::HAS_TAG != 0 {
Some(header.tag)
} else {
None
};
let ic = header.flags & flags::IC != 0;
let payload = wire::get_request_payload(request);
match server.send(handle, header.msg_type, eid, tag, ic, payload) {
Ok(tag_val) => wire::encode_send_response(response, tag_val)
.unwrap_or_else(|_| encode_error(response, ResponseCode::InternalError)),
Err(e) => encode_error(response, e.code),
}
}
MctpOp::Unbind => {
let handle = openprot_mctp_api::Handle(header.handle);
match server.unbind(handle) {
Ok(()) => encode_success(response),
Err(e) => encode_error(response, e.code),
}
}
}
}
fn encode_error(response: &mut [u8], code: ResponseCode) -> usize {
wire::encode_error_response(response, code).unwrap_or(0)
}
fn encode_success(response: &mut [u8]) -> usize {
wire::encode_success_response(response).unwrap_or(0)
}