hadusos/lib.rs
1//! A half-duplex session over serial protocol.
2//!
3//! The client should provide a serial device instance and a timer instance,
4//! which respectively implements the [`Serial`] and [`Timer`] trait. Then, a
5//! session layer instance can be constructed.
6//!
7//! The [`Session`] supports half-duplex data transmission, i.e., a client
8//! operates as either the sender or the receiver at a time. When operating as
9//! the sender, the client should call [`send`](Session::send) to send data.
10//! When operating as the receiver, the client should call
11//! [`listen`](Session::listen) to learn the incoming data length to allocate
12//! a buffer, and subsequently call [`receive`](Session::receive) to receive
13//! the data. The receiver may also call [`reject`](Session::reject) if it does
14//! not want to proceed to receiving the data after [`listen`](Session::listen).
15//!
16//! Below shows an example.
17//!
18//! ```rust
19//! use crossbeam::channel::{self, Receiver, RecvError, SendError, Sender};
20//! use hadusos::{Serial, SerialError, Session, Timer};
21//! use std::{
22//! sync::Arc,
23//! thread,
24//! time::{Duration, SystemTime, UNIX_EPOCH},
25//! };
26//!
27//! fn main() {
28//! // Create a pair of connected serial devices.
29//! let (serial0, serial1) = MockSerial::new_pair();
30//!
31//! // Create two timers.
32//! let (timer0, timer1) = (MockTimer, MockTimer);
33//!
34//! const TIMEOUT: u32 = 1000;
35//!
36//! // Start a thread what will echo back whatever it receives.
37//! let thread_echo = thread::spawn(|| {
38//! // Create a session layer instance using the serial device and timer.
39//! let mut sess = Session::<_, _, 50, 3>::new(serial0, timer0);
40//!
41//! // Listen for incoming data.
42//! let data_len = sess.listen(TIMEOUT).unwrap();
43//!
44//! // Allocate a buffer and receive incoming data.
45//! let mut buffer = vec![0u8; data_len as usize].into_boxed_slice();
46//! sess.receive(&mut buffer, TIMEOUT).unwrap();
47//!
48//! // Echo the data back.
49//! sess.send(&buffer, TIMEOUT).unwrap();
50//! });
51//!
52//! // Start a thread that will send "hello world!" over the serial and verify
53//! // the echoed data.
54//! let thread_check = thread::spawn(|| {
55//! // Create a session layer instance using the serial device and timer.
56//! let mut sess = Session::<_, _, 50, 3>::new(serial1, timer1);
57//!
58//! // Send "hello world!".
59//! sess.send("hello world!".as_bytes(), TIMEOUT).unwrap();
60//!
61//! // Listen for echoed data.
62//! let data_len = sess.listen(TIMEOUT).unwrap();
63//!
64//! // Allocate a buffer and receive echoed data.
65//! let mut buffer = vec![0u8; data_len as usize].into_boxed_slice();
66//! sess.receive(&mut buffer, TIMEOUT).unwrap();
67//!
68//! // Verify echoed data.
69//! assert!(buffer.as_ref() == "hello world!".as_bytes());
70//! });
71//!
72//! thread_echo.join().unwrap();
73//! thread_check.join().unwrap();
74//! }
75//!
76//! /// Simulated timer.
77//! struct MockTimer;
78//!
79//! impl Timer for MockTimer {
80//! /// Get the timestamp from the std library.
81//! fn get_timestamp_ms(&mut self) -> u32 {
82//! SystemTime::now()
83//! .duration_since(UNIX_EPOCH)
84//! .unwrap()
85//! .as_millis() as u32
86//! }
87//! }
88//!
89//! /// Simulated serial device. It is simulated with `crossbeam`'s MPMC queues.
90//! struct MockSerial {
91//! send: Arc<Sender<u8>>,
92//! recv: Arc<Receiver<u8>>,
93//! }
94//!
95//! impl MockSerial {
96//! /// Get a pair of connected serial devices.
97//! fn new_pair() -> (Self, Self) {
98//! // Create channels to send in both directions.
99//! let (send0, recv0) = channel::unbounded();
100//! let (send1, recv1) = channel::unbounded();
101//!
102//! // Wrap the endpoints in `Arc`s.
103//! let send0 = Arc::new(send0);
104//! let send1 = Arc::new(send1);
105//! let recv0 = Arc::new(recv0);
106//! let recv1 = Arc::new(recv1);
107//!
108//! // Deliberately keep the reference counts to be always positive, so
109//! // that the channels will always be kept alive, otherwise we will get
110//! // channel disconnection errors.
111//! std::mem::forget(Arc::clone(&send0));
112//! std::mem::forget(Arc::clone(&send1));
113//! std::mem::forget(Arc::clone(&recv0));
114//! std::mem::forget(Arc::clone(&recv1));
115//!
116//! (
117//! Self {
118//! send: send0,
119//! recv: recv1,
120//! },
121//! Self {
122//! send: send1,
123//! recv: recv0,
124//! },
125//! )
126//! }
127//! }
128//!
129//! impl Serial for MockSerial {
130//! type ReadError = RecvError;
131//! type WriteError = SendError<u8>;
132//!
133//! /// Read a byte from the channel with a timeout.
134//! fn read_byte_with_timeout(
135//! &mut self,
136//! timeout_ms: u32,
137//! ) -> Result<u8, SerialError<Self::ReadError, Self::WriteError>> {
138//! // Read byte from the channel. MUST map the timeout error to the
139//! // specific `SerialError` variant because the protocol stack treats
140//! // timeout error in a special way as it is sometimes recoverable.
141//! self.recv
142//! .recv_timeout(Duration::from_millis(timeout_ms as u64))
143//! .map_err(|e| match e {
144//! channel::RecvTimeoutError::Timeout => SerialError::Timeout,
145//! channel::RecvTimeoutError::Disconnected => SerialError::ReadError(RecvError),
146//! })
147//! }
148//!
149//! /// Write a byte to the channel.
150//! fn write_byte(
151//! &mut self,
152//! byte: u8,
153//! ) -> Result<(), SerialError<Self::ReadError, Self::WriteError>> {
154//! self.send.send(byte).map_err(|e| SerialError::WriteError(e))
155//! }
156//! }
157//! ```
158
159#![cfg_attr(not(test), no_std)]
160
161mod link;
162mod packet;
163mod serial;
164mod session;
165mod timer;
166
167#[cfg(test)]
168mod tests;
169
170pub use serial::{Serial, SerialError};
171pub use session::{Session, SessionError};
172pub use timer::Timer;