blob: 59a77677ac8c8876b2cdef6f052feb215ad5b57d [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Pigweed IPC channel implementations for [`FirmwareDevice`].
//!
//! Provides:
//! * [`IpcFdUaRspChannel`] – server-side channel that receives firmware-device
//! commands via `channel_read` and responds via `channel_respond`.
//! * [`IpcFdUaCmdChannel`] – client-side channel that performs a synchronous
//! firmware-update request/response round-trip via `channel_transact`.
//! * [`IpcUaFdRspChannel`] – server-side channel used by `PldmRequester` to
//! receive forwarded PLDM requests from `FirmwareDevice` and respond with
//! the MCTP result.
//!
//! ## Usage
//!
//! ```rust,ignore
//! use openprot_pldm_firmware_device_ipc::{IpcFdUaRspChannel, IpcFdUaCmdChannel};
//! use openprot_pldm_service::firmware_device::FirmwareDevice;
//!
//! let fd_channel = IpcFdUaRspChannel::new(handle::FD_CMD);
//! let fw_channel = IpcFdUaCmdChannel::new(handle::FW_REQ);
//! let mut fd = FirmwareDevice::new(&PROTOCOL_CAPS);
//! let mut buf = [0u8; openprot_pldm_service::firmware_device::FD_IPC_MAX_MSG];
//! loop {
//! let _ = fd.run_terminus(&fd_channel, &fw_channel, &mut buf, 0);
//! }
//! ```
//!
//! [`FirmwareDevice`]: openprot_pldm_service::firmware_device::FirmwareDevice
#![no_std]
#![warn(missing_docs)]
use openprot_pldm_service::error::PldmServiceError;
use openprot_pldm_service::firmware_device::{UaFdRspChannel, UaFdCmdChannel, FdUaRspChannel, FdUaCmdChannel};
use userspace::syscall::Signals;
use userspace::time::Instant;
/// IPC server-side channel for receiving PLDM firmware-device commands.
/// Meant to be used by [`FirmwareDevice`] to receive requests from the UA
/// and respond with the MCTP result.
///
/// Wraps a Pigweed IPC channel handle. Each call to [`FdUaRspChannel::recv`]
/// reads one incoming request with `channel_read`; [`FdUaRspChannel::respond`]
/// sends the response with `channel_respond`.
///
/// The handle comes from the application's generated `handle` module
/// (e.g. `handle::FD_CMD`).
pub struct IpcFdUaRspChannel {
handle: u32,
}
impl IpcFdUaRspChannel {
/// Create a new channel bound to `handle`.
pub fn new(handle: u32) -> Self {
Self { handle }
}
/// Return the underlying IPC channel handle.
pub fn channel_handle(&self) -> u32 {
self.handle
}
}
impl FdUaRspChannel for IpcFdUaRspChannel {
fn recv(&self, buf: &mut [u8], _timeout_millis: u32) -> Result<usize, PldmServiceError> {
userspace::syscall::channel_read(self.handle, 0, buf)
.map_err(|_| PldmServiceError::Ipc)
}
fn try_recv(&self, buf: &mut [u8]) -> Result<Option<usize>, PldmServiceError> {
// `channel_read` is non-blocking: it returns `Error::Unavailable` when
// no transaction is pending, which we map to "no message".
match userspace::syscall::channel_read(self.handle, 0, buf) {
Ok(len) => Ok(Some(len)),
Err(pw_status::Error::Unavailable) => Ok(None),
Err(_) => Err(PldmServiceError::Ipc),
}
}
fn respond(&self, buf: &[u8]) -> Result<(), PldmServiceError> {
userspace::syscall::channel_respond(self.handle, buf)
.map_err(|_| PldmServiceError::Ipc)
}
fn wait_readable(&self, _timeout_millis: u32) -> Result<(), PldmServiceError> {
// Park the task until the channel becomes readable. This is the yield
// point that lets `run_terminus` avoid busy-polling when idle.
//
// TODO: honor a finite `timeout_millis`. The kernel takes an absolute
// deadline; all current call sites block indefinitely, so we mirror
// that with `Instant::MAX` for now.
userspace::syscall::object_wait(self.handle, Signals::READABLE, Instant::MAX)
.map(|_| ())
.map_err(|_| PldmServiceError::Ipc)
}
}
/// IPC client-side channel for sending PLDM firmware-update requests.
/// Meant to be used by [`FirmwareDevice`] to send requests to the UA and receive
/// the MCTP response.
///
/// Wraps a Pigweed IPC channel handle. Each call to [`FdUaCmdChannel::transact`]
/// performs one synchronous `channel_transact`, blocking until the response
/// arrives.
///
/// The handle comes from the application's generated `handle` module
/// (e.g. `handle::FW_REQ`).
pub struct IpcFdUaCmdChannel {
handle: u32,
}
impl IpcFdUaCmdChannel {
/// Create a new channel bound to `handle`.
pub fn new(handle: u32) -> Self {
Self { handle }
}
/// Return the underlying IPC channel handle.
pub fn channel_handle(&self) -> u32 {
self.handle
}
}
impl FdUaCmdChannel for IpcFdUaCmdChannel {
fn transact(&self, req: &[u8], resp: &mut [u8]) -> Result<usize, PldmServiceError> {
userspace::syscall::channel_transact(self.handle, req, resp, Instant::MAX)
.map_err(|_| PldmServiceError::Ipc)
}
}
/// IPC client-side channel for sending PLDM firmware-command requests.
///
/// Wraps a Pigweed IPC channel handle. Each call to [`IpcUaFdCmdChannel::transact`]
/// performs one synchronous `channel_transact`, blocking until the response
/// arrives.
///
/// The handle comes from the application's generated `handle` module
/// (e.g. `handle::FW_REQ`).
pub struct IpcUaFdCmdChannel {
handle: u32,
}
impl IpcUaFdCmdChannel {
/// Create a new channel bound to `handle`.
pub fn new(handle: u32) -> Self {
Self { handle }
}
/// Return the underlying IPC channel handle.
pub fn channel_handle(&self) -> u32 {
self.handle
}
}
impl UaFdCmdChannel for IpcUaFdCmdChannel {
fn transact(&self, req: &[u8], resp: &mut [u8]) -> Result<usize, PldmServiceError> {
userspace::syscall::channel_transact(self.handle, req, resp, Instant::MAX)
.map_err(|_| PldmServiceError::Ipc)
}
}
/// IPC server-side channel used by [`PldmRequester`] to receive forwarded
/// PLDM requests from [`FirmwareDevice`] and respond with the MCTP result.
///
/// Wraps a Pigweed IPC channel handle. Each call to [`UaFdRspChannel::recv`]
/// reads one incoming request with `channel_read`; [`UaFdRspChannel::respond`]
/// sends the response with `channel_respond`.
///
/// The handle comes from the application's generated `handle` module
/// (e.g. `handle::FW_REQ`).
///
/// [`PldmRequester`]: openprot_pldm_service::requester::PldmRequester
/// [`FirmwareDevice`]: openprot_pldm_service::firmware_device::FirmwareDevice
pub struct IpcUaFdRspChannel {
handle: u32,
}
impl IpcUaFdRspChannel {
/// Create a new channel bound to `handle`.
pub fn new(handle: u32) -> Self {
Self { handle }
}
/// Return the underlying IPC channel handle.
pub fn channel_handle(&self) -> u32 {
self.handle
}
}
impl UaFdRspChannel for IpcUaFdRspChannel {
fn recv(&self, buf: &mut [u8]) -> Result<usize, PldmServiceError> {
userspace::syscall::channel_read(self.handle, 0, buf)
.map_err(|_| PldmServiceError::Ipc)
}
fn respond(&self, buf: &[u8]) -> Result<(), PldmServiceError> {
userspace::syscall::channel_respond(self.handle, buf)
.map_err(|_| PldmServiceError::Ipc)
}
}