ergot/interface_manager/
mod.rs

1//! The Interface Manager
2//!
3//! The [`NetStack`] is generic over a "Profile", which is how it handles
4//! any external interfaces of the current program or device.
5//!
6//! Different profiles may support a various number of external
7//! interfaces. The simplest profile is the "Null Profile",
8//! Which supports no external interfaces, meaning that messages may only be
9//! routed locally.
10//!
11//! The next simplest profile is one that only supports zero or one
12//! active interfaces, for example if a device is directly connected to a PC
13//! using USB. In this case, routing is again simple: if messages are not
14//! intended for the local device, they should be routed out of the one external
15//! interface. Similarly, if we support an interface, but it is not connected
16//! (e.g. the USB cable is unplugged), all packets with external destinations
17//! will fail to send.
18//!
19//! For more complex devices, a profile with multiple (bounded or
20//! unbounded) interfaces, and more complex routing capabilities, may be
21//! selected.
22//!
23//! Unlike Sockets, which might be various and diverse on all systems, a system
24//! is expected to have one statically-known profile, which may
25//! manage various and diverse interfaces.
26//!
27//! In general when sending a message, the [`NetStack`] will check if the
28//! message is definitively for the local device (e.g. Net ID = 0, Node ID = 0),
29//! and if not the NetStack will pass the message to the Interface Manager. If
30//! the profile can route this packet, it informs the NetStack it has
31//! done so. If the Interface Manager realizes that the packet is still for us
32//! (e.g. matching a Net ID and Node ID of the local device), it may bounce the
33//! message back to the NetStack to locally route.
34//!
35//! [`NetStack`]: crate::NetStack
36
37use crate::{Header, HeaderSeq, ProtocolError};
38use postcard_schema::Schema;
39use serde::{Deserialize, Serialize};
40
41pub mod interface_impls;
42pub mod profiles;
43pub mod utils;
44
45pub trait ConstInit {
46    const INIT: Self;
47}
48
49/// A successful Net ID assignment or refresh from a Seed Router
50#[derive(Serialize, Deserialize, Schema, Debug, PartialEq, Clone)]
51#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
52pub struct SeedNetAssignment {
53    /// The newly assigned net id
54    pub net_id: u16,
55    /// How many seconds from NOW does the assignment expire?
56    pub expires_seconds: u16,
57    /// What is the LONGEST time that this seed router will grant Net IDs for?
58    pub max_refresh_seconds: u16,
59    /// Don't ask to refresh this token until we are < this many seconds from the expiration time
60    pub min_refresh_seconds: u16,
61    /// The unique token to be used for later refresh requests.
62    pub refresh_token: [u8; 8],
63}
64
65/// An error occurred when assigning a net ID
66#[derive(Serialize, Deserialize, Schema, Debug, PartialEq, Clone)]
67pub enum SeedAssignmentError {
68    /// The current Profile is not a seed router
69    ProfileCantSeed,
70    /// The Profile is out of Net IDs
71    NetIdsExhausted,
72    /// The source ID requesting the Net ID is unknown to this seed router
73    UnknownSource,
74}
75
76/// An error occurred when refreshing a net ID
77#[derive(Serialize, Deserialize, Schema, Debug, PartialEq, Clone)]
78pub enum SeedRefreshError {
79    /// The current Profile is not a seed router
80    ProfileCantSeed,
81    /// The requested Net ID to be refreshed is unknown by the Seed Router
82    UnknownNetId,
83    /// The requested Net ID to be refreshed was not assigned as a Seed Router Net ID
84    /// (e.g. it is a Direct Connection and does not require refreshing)
85    NotAssigned,
86    /// The requested Net ID to refresh has already expired
87    AlreadyExpired,
88    /// The given data did not match the Seed Router table
89    BadRequest,
90    /// The request to refresh violated the min_refresh_seconds time
91    TooSoon,
92}
93
94// An interface send is very similar to a socket send, with the exception
95// that interface sends are ALWAYS a serializing operation (or required
96// serialization has already been done), which means we don't need to
97// differentiate between "send owned" and "send borrowed". The exception
98// to this is "send raw", where serialization has already been done, e.g.
99// if we are routing a packet.
100pub trait Profile {
101    /// The kind of type that is used to identify a single interface.
102    /// If a Profile only supports a single interface, this is often the `()` type.
103    /// If a Profile supports many interfaces, this could be an enum or integer type.
104    #[cfg(feature = "defmt-v1")]
105    type InterfaceIdent: Clone + core::fmt::Debug + defmt::Format;
106    #[cfg(not(feature = "defmt-v1"))]
107    type InterfaceIdent: Clone + core::fmt::Debug;
108
109    /// Send a serializable message to the Profile.
110    ///
111    /// This method should only be used for messages that originate locally
112    fn send<T: Serialize>(&mut self, hdr: &Header, data: &T) -> Result<(), InterfaceSendError>;
113
114    /// Send a protocol error to the Profile
115    ///
116    /// Errors may originate locally or remotely
117    fn send_err(
118        &mut self,
119        hdr: &Header,
120        err: ProtocolError,
121        source: Option<Self::InterfaceIdent>,
122    ) -> Result<(), InterfaceSendError>;
123
124    /// Send a pre-serialized message to the Profile.
125    ///
126    /// This method should only be used for messages that do NOT originate locally
127    fn send_raw(
128        &mut self,
129        hdr: &HeaderSeq,
130        data: &[u8],
131        source: Self::InterfaceIdent,
132    ) -> Result<(), InterfaceSendError>;
133
134    /// Obtain the interface state of the given interface ident
135    ///
136    /// Returns None if the given ident is unknown by the Profile
137    fn interface_state(&mut self, ident: Self::InterfaceIdent) -> Option<InterfaceState>;
138
139    /// Set the state of the given interface ident
140    fn set_interface_state(
141        &mut self,
142        ident: Self::InterfaceIdent,
143        state: InterfaceState,
144    ) -> Result<(), SetStateError>;
145
146    /// Request a Net ID assignment from this profile
147    ///
148    /// For Profiles that are not (currently acting as) a Seed Router, this method will always return
149    /// an error.
150    fn request_seed_net_assign(
151        &mut self,
152        source_net: u16,
153    ) -> Result<SeedNetAssignment, SeedAssignmentError> {
154        _ = source_net;
155        Err(SeedAssignmentError::ProfileCantSeed)
156    }
157
158    /// Request the refresh of a Net ID assignment from this profile
159    ///
160    /// For Profiles that are not (currently acting as) a Seed Router, this method will always return
161    /// an error.
162    fn refresh_seed_net_assignment(
163        &mut self,
164        source_net: u16,
165        refresh_net: u16,
166        refresh_token: [u8; 8],
167    ) -> Result<SeedNetAssignment, SeedRefreshError> {
168        _ = source_net;
169        _ = refresh_net;
170        _ = refresh_token;
171        Err(SeedRefreshError::ProfileCantSeed)
172    }
173}
174
175/// Interfaces define how messages are transported over the wire
176pub trait Interface {
177    /// The Sink is the type used to send messages out of the Profile
178    type Sink: InterfaceSink;
179}
180
181/// The "Sink" side of the interface.
182///
183/// This is typically held by a profile, and feeds data to the interface's
184/// TX worker.
185#[allow(clippy::result_unit_err)]
186pub trait InterfaceSink {
187    fn send_ty<T: Serialize>(&mut self, hdr: &HeaderSeq, body: &T) -> Result<(), ()>;
188    fn send_raw(&mut self, hdr: &HeaderSeq, body: &[u8]) -> Result<(), ()>;
189    fn send_err(&mut self, hdr: &HeaderSeq, err: ProtocolError) -> Result<(), ()>;
190}
191
192#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
193#[derive(Debug, PartialEq, Eq, Clone, Copy)]
194#[non_exhaustive]
195pub enum InterfaceSendError {
196    /// Refusing to send local destination remotely
197    DestinationLocal,
198    /// Profile does not know how to route to requested destination
199    NoRouteToDest,
200    /// Profile found a destination interface, but that interface
201    /// was full in space/slots
202    InterfaceFull,
203    /// An unhandled internal error occurred, this is a bug.
204    InternalError,
205    /// Destination was an "any" port, but a key was not provided
206    AnyPortMissingKey,
207    /// TTL has reached the terminal value
208    TtlExpired,
209    /// Interface detected that a packet should be routed back to its source
210    RoutingLoop,
211}
212
213/// An error when deregistering an interface
214#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
215#[derive(Debug, PartialEq, Eq)]
216#[non_exhaustive]
217pub enum DeregisterError {
218    NoSuchInterface,
219}
220
221#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
222#[derive(Clone, Copy, Debug, PartialEq)]
223pub enum InterfaceState {
224    // Missing sink, no net id
225    Down,
226    // Has sink, no net id
227    Inactive,
228    // Has sink, has node_id but not net_id
229    ActiveLocal { node_id: u8 },
230    // Has sink, has net id
231    Active { net_id: u16, node_id: u8 },
232}
233
234#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
235#[derive(Clone, Copy, Debug, PartialEq)]
236#[non_exhaustive]
237pub enum RegisterSinkError {
238    AlreadyActive,
239}
240
241#[cfg_attr(feature = "defmt-v1", derive(defmt::Format))]
242#[derive(Clone, Copy, Debug, PartialEq)]
243#[non_exhaustive]
244pub enum SetStateError {
245    InterfaceNotFound,
246    InvalidNodeId,
247}
248
249impl InterfaceSendError {
250    pub fn to_error(&self) -> ProtocolError {
251        match self {
252            InterfaceSendError::DestinationLocal => ProtocolError::ISE_DESTINATION_LOCAL,
253            InterfaceSendError::NoRouteToDest => ProtocolError::ISE_NO_ROUTE_TO_DEST,
254            InterfaceSendError::InterfaceFull => ProtocolError::ISE_INTERFACE_FULL,
255            InterfaceSendError::InternalError => ProtocolError::ISE_INTERNAL_ERROR,
256            InterfaceSendError::AnyPortMissingKey => ProtocolError::ISE_ANY_PORT_MISSING_KEY,
257            InterfaceSendError::TtlExpired => ProtocolError::ISE_TTL_EXPIRED,
258            InterfaceSendError::RoutingLoop => ProtocolError::ISE_ROUTING_LOOP,
259        }
260    }
261}