consortium_ipc/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2// Copyright 2026 Ethan Wu
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16// SPDX-License-Identifier: Apache-2.0
17
18//! Core inter-processor communication primitives for Consortium.
19//!
20//! This crate is the abstraction layer that every Consortium link is built from.
21//! It defines the trait stack, the typed endpoints application and firmware code
22//! call, and the marker types that keep a message meaningful on both sides of a
23//! core boundary. It performs no hardware access, no allocation, and hosts no
24//! executor; concrete links live in `consortium-ipc-transport-*`, concrete
25//! signaling in `consortium-ipc-doorbell-*`, and serialization in
26//! `consortium-codec`.
27//!
28//! # Architecture
29//!
30//! Six traits compose into two user-facing types:
31//!
32//! ```text
33//! Doorbell wake the remote processor (no data)
34//! SendTransport / RecvTransport byte halves; ring or await the doorbell
35//! Transport marker for full-duplex links
36//! Connect one-shot readiness rendezvous with the peer
37//! CodecFor<T> serialization family for a message type
38//! Channel<D, T, Tr, C> typed endpoint over one transport half
39//! Transceiver<..> typed endpoint pairing a Tx and an Rx channel
40//! ```
41//!
42//! - [`Doorbell`] is pure signaling. Its ordering contract — writes before `ring`
43//! happen-before the peer's `wait`/`pending` — is what makes shared-memory
44//! payloads safe to read on the far side.
45//! - [`SendTransport`] and [`RecvTransport`] move bytes and report their MTU. Both
46//! return `impl Future<..> + MaybeSend`, so implementations may be plain
47//! `async fn`s awaiting unnameable futures.
48//! - [`Channel`] adds a message type and a [`consortium_codec::CodecFor`] to one
49//! transport half.
50//! It never allocates: the caller supplies a `&'static mut [u8]` scratch buffer
51//! sized for the transport MTU.
52//! - [`Transceiver`] pairs a [`Tx`] and an [`Rx`] channel, each over its own
53//! transport half, into one bidirectional endpoint.
54//!
55//! Supporting types: [`Chan`] (validated channel id, with per-platform
56//! [`ChanValidate`]), [`Side`] ([`Primary`] / [`Secondary`] / [`Tertiary`], the
57//! register view of a multi-ported peripheral), [`IpcSafe`] (ABI-portable message
58//! types), and [`MaybeSend`] (`Send` under `std`, vacuous under `no_std`).
59//!
60//! # Example
61//!
62//! ```rust,ignore
63//! use consortium_codec::PostcardCodec;
64//! use consortium_ipc::{Chan, Channel, Connect, Rx, Transceiver, Tx};
65//!
66//! // Rendezvous once, before any traffic and before splitting.
67//! transport.connect(params).await?;
68//!
69//! let (tx_half, rx_half) = transport.split();
70//! let tx = Channel::<Tx, Command, _, PostcardCodec>::new(chan, tx_half, tx_buf);
71//! let rx = Channel::<Rx, Telemetry, _, PostcardCodec>::new(chan, rx_half, rx_buf);
72//!
73//! let mut link = Transceiver::new(tx, rx);
74//! link.send(&Command::Start).await?;
75//! let reply = link.recv().await?;
76//! ```
77//!
78//! [`recv`](Channel::recv) yields a [`ReceivedMessage`] rather than a bare `T`, so
79//! owned codecs (`Decoded<'buf> = T`) and future zero-copy codecs
80//! (`Decoded<'buf> = &'buf T::Archived`) share one call-site shape.
81//!
82//! # Implementing a transport
83//!
84//! Implement [`TransportError`] to name the error type, then [`SendTransport`]
85//! and/or [`RecvTransport`], plus [`Transport`] as a marker for full-duplex links.
86//! Implement [`Connect`] when a peer must agree before traffic flows, using
87//! `Params = ()` if nothing is negotiated. Report honest `max_send_size` /
88//! `max_recv_size`: callers size their scratch buffers from them. If the link owns
89//! a channel-id namespace, implement [`ChanValidate`] for a platform marker so
90//! `Chan::new::<P>(id)` rejects out-of-range ids.
91//!
92//! # Features
93//!
94//! The crate is `no_std` unless `std` is enabled.
95//!
96//! - `alloc` — link `alloc`.
97//! - `futures` — futures-oriented helpers.
98//! - `tracing` — route internal logging to `consortium-log`'s `tracing` backend.
99//! - `defmt` — route internal logging to `defmt` and derive `defmt::Format`.
100//! - `std` — `alloc + futures + tracing`, and makes [`MaybeSend`] require `Send`
101//! (which Tokio's multi-threaded executor needs). Leave it off for Embassy.
102
103#[cfg(feature = "alloc")]
104extern crate alloc;
105
106pub(crate) mod chan;
107pub(crate) mod channel;
108pub(crate) mod connect;
109pub(crate) mod doorbell;
110pub(crate) mod future;
111pub(crate) mod sealed;
112pub(crate) mod side;
113pub(crate) mod transceiver;
114pub(crate) mod transport;
115pub(crate) mod wire;
116
117pub use {
118 chan::{Chan, ChanValidate},
119 channel::{Channel, ChannelError, Direction, ReceivedMessage, Rx, Tx},
120 connect::Connect,
121 doorbell::Doorbell,
122 future::MaybeSend,
123 side::{Primary, Secondary, Side, Tertiary},
124 transceiver::Transceiver,
125 transport::{RecvTransport, SendTransport, Transport, TransportError},
126 wire::IpcSafe,
127};
128
129pub use consortium_ipc_macros::IpcSafe;