aboutsummaryrefslogtreecommitdiff
path: root/embassy-net-ppp/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-net-ppp/src')
-rw-r--r--embassy-net-ppp/src/fmt.rs258
-rw-r--r--embassy-net-ppp/src/lib.rs185
2 files changed, 443 insertions, 0 deletions
diff --git a/embassy-net-ppp/src/fmt.rs b/embassy-net-ppp/src/fmt.rs
new file mode 100644
index 000000000..78e583c1c
--- /dev/null
+++ b/embassy-net-ppp/src/fmt.rs
@@ -0,0 +1,258 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9macro_rules! assert {
10 ($($x:tt)*) => {
11 {
12 #[cfg(not(feature = "defmt"))]
13 ::core::assert!($($x)*);
14 #[cfg(feature = "defmt")]
15 ::defmt::assert!($($x)*);
16 }
17 };
18}
19
20macro_rules! assert_eq {
21 ($($x:tt)*) => {
22 {
23 #[cfg(not(feature = "defmt"))]
24 ::core::assert_eq!($($x)*);
25 #[cfg(feature = "defmt")]
26 ::defmt::assert_eq!($($x)*);
27 }
28 };
29}
30
31macro_rules! assert_ne {
32 ($($x:tt)*) => {
33 {
34 #[cfg(not(feature = "defmt"))]
35 ::core::assert_ne!($($x)*);
36 #[cfg(feature = "defmt")]
37 ::defmt::assert_ne!($($x)*);
38 }
39 };
40}
41
42macro_rules! debug_assert {
43 ($($x:tt)*) => {
44 {
45 #[cfg(not(feature = "defmt"))]
46 ::core::debug_assert!($($x)*);
47 #[cfg(feature = "defmt")]
48 ::defmt::debug_assert!($($x)*);
49 }
50 };
51}
52
53macro_rules! debug_assert_eq {
54 ($($x:tt)*) => {
55 {
56 #[cfg(not(feature = "defmt"))]
57 ::core::debug_assert_eq!($($x)*);
58 #[cfg(feature = "defmt")]
59 ::defmt::debug_assert_eq!($($x)*);
60 }
61 };
62}
63
64macro_rules! debug_assert_ne {
65 ($($x:tt)*) => {
66 {
67 #[cfg(not(feature = "defmt"))]
68 ::core::debug_assert_ne!($($x)*);
69 #[cfg(feature = "defmt")]
70 ::defmt::debug_assert_ne!($($x)*);
71 }
72 };
73}
74
75macro_rules! todo {
76 ($($x:tt)*) => {
77 {
78 #[cfg(not(feature = "defmt"))]
79 ::core::todo!($($x)*);
80 #[cfg(feature = "defmt")]
81 ::defmt::todo!($($x)*);
82 }
83 };
84}
85
86#[cfg(not(feature = "defmt"))]
87macro_rules! unreachable {
88 ($($x:tt)*) => {
89 ::core::unreachable!($($x)*)
90 };
91}
92
93#[cfg(feature = "defmt")]
94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 ::defmt::unreachable!($($x)*)
97 };
98}
99
100macro_rules! panic {
101 ($($x:tt)*) => {
102 {
103 #[cfg(not(feature = "defmt"))]
104 ::core::panic!($($x)*);
105 #[cfg(feature = "defmt")]
106 ::defmt::panic!($($x)*);
107 }
108 };
109}
110
111macro_rules! trace {
112 ($s:literal $(, $x:expr)* $(,)?) => {
113 {
114 #[cfg(feature = "log")]
115 ::log::trace!($s $(, $x)*);
116 #[cfg(feature = "defmt")]
117 ::defmt::trace!($s $(, $x)*);
118 #[cfg(not(any(feature = "log", feature="defmt")))]
119 let _ = ($( & $x ),*);
120 }
121 };
122}
123
124macro_rules! debug {
125 ($s:literal $(, $x:expr)* $(,)?) => {
126 {
127 #[cfg(feature = "log")]
128 ::log::debug!($s $(, $x)*);
129 #[cfg(feature = "defmt")]
130 ::defmt::debug!($s $(, $x)*);
131 #[cfg(not(any(feature = "log", feature="defmt")))]
132 let _ = ($( & $x ),*);
133 }
134 };
135}
136
137macro_rules! info {
138 ($s:literal $(, $x:expr)* $(,)?) => {
139 {
140 #[cfg(feature = "log")]
141 ::log::info!($s $(, $x)*);
142 #[cfg(feature = "defmt")]
143 ::defmt::info!($s $(, $x)*);
144 #[cfg(not(any(feature = "log", feature="defmt")))]
145 let _ = ($( & $x ),*);
146 }
147 };
148}
149
150macro_rules! warn {
151 ($s:literal $(, $x:expr)* $(,)?) => {
152 {
153 #[cfg(feature = "log")]
154 ::log::warn!($s $(, $x)*);
155 #[cfg(feature = "defmt")]
156 ::defmt::warn!($s $(, $x)*);
157 #[cfg(not(any(feature = "log", feature="defmt")))]
158 let _ = ($( & $x ),*);
159 }
160 };
161}
162
163macro_rules! error {
164 ($s:literal $(, $x:expr)* $(,)?) => {
165 {
166 #[cfg(feature = "log")]
167 ::log::error!($s $(, $x)*);
168 #[cfg(feature = "defmt")]
169 ::defmt::error!($s $(, $x)*);
170 #[cfg(not(any(feature = "log", feature="defmt")))]
171 let _ = ($( & $x ),*);
172 }
173 };
174}
175
176#[cfg(feature = "defmt")]
177macro_rules! unwrap {
178 ($($x:tt)*) => {
179 ::defmt::unwrap!($($x)*)
180 };
181}
182
183#[cfg(not(feature = "defmt"))]
184macro_rules! unwrap {
185 ($arg:expr) => {
186 match $crate::fmt::Try::into_result($arg) {
187 ::core::result::Result::Ok(t) => t,
188 ::core::result::Result::Err(e) => {
189 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
190 }
191 }
192 };
193 ($arg:expr, $($msg:expr),+ $(,)? ) => {
194 match $crate::fmt::Try::into_result($arg) {
195 ::core::result::Result::Ok(t) => t,
196 ::core::result::Result::Err(e) => {
197 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
198 }
199 }
200 }
201}
202
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204pub struct NoneError;
205
206pub trait Try {
207 type Ok;
208 type Error;
209 fn into_result(self) -> Result<Self::Ok, Self::Error>;
210}
211
212impl<T> Try for Option<T> {
213 type Ok = T;
214 type Error = NoneError;
215
216 #[inline]
217 fn into_result(self) -> Result<T, NoneError> {
218 self.ok_or(NoneError)
219 }
220}
221
222impl<T, E> Try for Result<T, E> {
223 type Ok = T;
224 type Error = E;
225
226 #[inline]
227 fn into_result(self) -> Self {
228 self
229 }
230}
231
232#[allow(unused)]
233pub(crate) struct Bytes<'a>(pub &'a [u8]);
234
235impl<'a> Debug for Bytes<'a> {
236 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
237 write!(f, "{:#02x?}", self.0)
238 }
239}
240
241impl<'a> Display for Bytes<'a> {
242 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243 write!(f, "{:#02x?}", self.0)
244 }
245}
246
247impl<'a> LowerHex for Bytes<'a> {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 write!(f, "{:#02x?}", self.0)
250 }
251}
252
253#[cfg(feature = "defmt")]
254impl<'a> defmt::Format for Bytes<'a> {
255 fn format(&self, fmt: defmt::Formatter) {
256 defmt::write!(fmt, "{:02x}", self.0)
257 }
258}
diff --git a/embassy-net-ppp/src/lib.rs b/embassy-net-ppp/src/lib.rs
new file mode 100644
index 000000000..66496ee0a
--- /dev/null
+++ b/embassy-net-ppp/src/lib.rs
@@ -0,0 +1,185 @@
1#![no_std]
2#![warn(missing_docs)]
3#![doc = include_str!("../README.md")]
4
5// must be first
6mod fmt;
7
8use core::convert::Infallible;
9use core::mem::MaybeUninit;
10
11use embassy_futures::select::{select, Either};
12use embassy_net_driver_channel as ch;
13use embassy_net_driver_channel::driver::LinkState;
14use embedded_io_async::{BufRead, Write, WriteAllError};
15use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
16pub use ppproto::{Config, Ipv4Status};
17
18const MTU: usize = 1500;
19
20/// Type alias for the embassy-net driver.
21pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
22
23/// Internal state for the embassy-net integration.
24pub struct State<const N_RX: usize, const N_TX: usize> {
25 ch_state: ch::State<MTU, N_RX, N_TX>,
26}
27
28impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
29 /// Create a new `State`.
30 pub const fn new() -> Self {
31 Self {
32 ch_state: ch::State::new(),
33 }
34 }
35}
36
37/// Background runner for the driver.
38///
39/// You must call `.run()` in a background task for the driver to operate.
40pub struct Runner<'d> {
41 ch: ch::Runner<'d, MTU>,
42}
43
44/// Error returned by [`Runner::run`].
45#[derive(Debug)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47pub enum RunError<E> {
48 /// Reading from the serial port failed.
49 Read(E),
50 /// Writing to the serial port failed.
51 Write(E),
52 /// Writing to the serial port wrote zero bytes, indicating it can't accept more data.
53 WriteZero,
54 /// Writing to the serial got EOF.
55 Eof,
56 /// PPP protocol was terminated by the peer
57 Terminated,
58}
59
60impl<E> From<WriteAllError<E>> for RunError<E> {
61 fn from(value: WriteAllError<E>) -> Self {
62 match value {
63 WriteAllError::Other(e) => Self::Write(e),
64 WriteAllError::WriteZero => Self::WriteZero,
65 }
66 }
67}
68
69impl<'d> Runner<'d> {
70 /// You must call this in a background task for the driver to operate.
71 ///
72 /// If reading/writing to the underlying serial port fails, the link state
73 /// is set to Down and the error is returned.
74 ///
75 /// It is allowed to cancel this function's future (i.e. drop it). This will terminate
76 /// the PPP connection and set the link state to Down.
77 ///
78 /// After this function returns or is canceled, you can call it again to establish
79 /// a new PPP connection.
80 pub async fn run<RW: BufRead + Write>(
81 &mut self,
82 mut rw: RW,
83 config: ppproto::Config<'_>,
84 mut on_ipv4_up: impl FnMut(Ipv4Status),
85 ) -> Result<Infallible, RunError<RW::Error>> {
86 let mut ppp = PPPoS::new(config);
87 ppp.open().unwrap();
88
89 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.borrow_split();
90 state_chan.set_link_state(LinkState::Down);
91 let _ondrop = OnDrop::new(|| state_chan.set_link_state(LinkState::Down));
92
93 let mut rx_buf = [0; 2048];
94 let mut tx_buf = [0; 2048];
95
96 let mut needs_poll = true;
97 let mut was_up = false;
98
99 loop {
100 let rx_fut = async {
101 let buf = rx_chan.rx_buf().await;
102 let rx_data = match needs_poll {
103 true => &[][..],
104 false => match rw.fill_buf().await {
105 Ok(rx_data) if rx_data.len() == 0 => return Err(RunError::Eof),
106 Ok(rx_data) => rx_data,
107 Err(e) => return Err(RunError::Read(e)),
108 },
109 };
110 Ok((buf, rx_data))
111 };
112 let tx_fut = tx_chan.tx_buf();
113 match select(rx_fut, tx_fut).await {
114 Either::First(r) => {
115 needs_poll = false;
116
117 let (buf, rx_data) = r?;
118 let n = ppp.consume(rx_data, &mut rx_buf);
119 rw.consume(n);
120
121 match ppp.poll(&mut tx_buf, &mut rx_buf) {
122 PPPoSAction::None => {}
123 PPPoSAction::Received(rg) => {
124 let pkt = &rx_buf[rg];
125 buf[..pkt.len()].copy_from_slice(pkt);
126 rx_chan.rx_done(pkt.len());
127 }
128 PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await?,
129 }
130
131 let status = ppp.status();
132 match status.phase {
133 ppproto::Phase::Dead => {
134 return Err(RunError::Terminated);
135 }
136 ppproto::Phase::Open => {
137 if !was_up {
138 on_ipv4_up(status.ipv4.unwrap());
139 }
140 was_up = true;
141 state_chan.set_link_state(LinkState::Up);
142 }
143 _ => {
144 was_up = false;
145 state_chan.set_link_state(LinkState::Down);
146 }
147 }
148 }
149 Either::Second(pkt) => {
150 match ppp.send(pkt, &mut tx_buf) {
151 Ok(n) => rw.write_all(&tx_buf[..n]).await?,
152 Err(BufferFullError) => unreachable!(),
153 }
154 tx_chan.tx_done();
155 }
156 }
157 }
158 }
159}
160
161/// Create a PPP embassy-net driver instance.
162///
163/// This returns two structs:
164/// - a `Device` that you must pass to the `embassy-net` stack.
165/// - a `Runner`. You must call `.run()` on it in a background task.
166pub fn new<'a, const N_RX: usize, const N_TX: usize>(state: &'a mut State<N_RX, N_TX>) -> (Device<'a>, Runner<'a>) {
167 let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ip);
168 (device, Runner { ch: runner })
169}
170
171struct OnDrop<F: FnOnce()> {
172 f: MaybeUninit<F>,
173}
174
175impl<F: FnOnce()> OnDrop<F> {
176 fn new(f: F) -> Self {
177 Self { f: MaybeUninit::new(f) }
178 }
179}
180
181impl<F: FnOnce()> Drop for OnDrop<F> {
182 fn drop(&mut self) {
183 unsafe { self.f.as_ptr().read()() }
184 }
185}