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;