blob: 6443064778979b31fdf2a1a1e8281ee93fdbe699 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! IPC abstraction over Pigweed kernel channels.
//!
//! `IpcInitiator` and `IpcHandler` split the two channel roles the kernel
//! itself distinguishes (`ChannelInitiatorObject` vs `ChannelHandlerObject`),
//! so a type only has to implement the operations its role can actually
//! serve. `AsyncTransaction` is a safe layer on top of `IpcInitiator`'s
//! unsafe async trio, tracking the one-transaction-per-channel invariant
//! locally instead of leaving it to the caller.
#![no_std]
use pw_status::Result;
/// Blocking and async operations available on the initiator side of a
/// channel (a `ChannelInitiatorObject` in the kernel).
pub trait IpcInitiator {
fn transact<BufSend, BufRecv>(
&self,
send_data: &BufSend,
recv_data: &mut BufRecv,
deadline: Instant,
) -> Result<usize>
where
BufSend: AsSyscallBuffer + ?Sized,
BufRecv: AsSyscallBuffer + ?Sized;
/// Starts a transaction and returns immediately; poll readiness via
/// `object_wait`/`wait_group_add` on this channel's handle
/// (`Signals::READABLE`), then call `async_transact_complete` or
/// `async_cancel`. Fails with `Error::Unavailable` if a transaction
/// (blocking or async) is already pending on this channel.
///
/// # Safety
/// The kernel holds raw pointers into `send_data`/`recv_data` (and
/// writes the response into `recv_data`) until the transaction is
/// completed or cancelled — callers must not read or write either
/// buffer, or let them be dropped or moved, until then. `recv_data`
/// must be large enough to hold the response; a response that
/// overflows it is a kernel error, not truncated silently.
unsafe fn async_transact_start<BufSend, BufRecv>(
&self,
send_data: &BufSend,
recv_data: &mut BufRecv,
) -> Result<()>
where
BufSend: AsSyscallBuffer + ?Sized,
BufRecv: AsSyscallBuffer + ?Sized;
fn async_transact_complete(&self) -> Result<usize>;
fn async_cancel(&self) -> Result<()>;
/// The raw channel handle, e.g. to register with a WaitGroup or pass to
/// `object_wait`.
fn as_raw(&self) -> u32;
}
/// Operations available on the handler side of a channel (a
/// `ChannelHandlerObject` in the kernel).
pub trait IpcHandler {
fn read<Buf>(&self, offset: usize, buffer: &mut Buf) -> Result<usize>
where
Buf: AsSyscallBuffer + ?Sized;
fn respond<Buf>(&self, buffer: &Buf) -> Result<()>
where
Buf: AsSyscallBuffer + ?Sized;
}
/// Transparent wrapper around a raw IPC handle.
///
/// `set_peer_user_signal` (defined in the target-only impl) is an inherent
/// method here rather than on `IpcInitiator`/`IpcHandler`, since it's usable
/// from either channel role and duplicating it onto both traits would just
/// mean two copies of the same forwarding call to keep in sync.
///
/// One transaction per channel is a kernel rule, not something this type
/// enforces: wrapping the same handle in two `AsyncTransaction`s just gets
/// you `Unavailable` on the second `start`, with the buffers handed back —
/// not anything unsound.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct IpcHandle {
pub handle: u32,
}
impl IpcHandle {
/// Wraps a raw channel handle. Trusts the caller to pass one the kernel
/// actually gave out.
pub const fn new(handle: u32) -> Self {
Self { handle }
}
}
mod async_transaction;
mod channel_transport;
mod target;
pub use async_transaction::{AsyncTransaction, Buffers, Completion, RecvError, StartError};
pub use channel_transport::AsyncChannelTransport;
pub use target::{AsSyscallBuffer, Instant};