aboutsummaryrefslogtreecommitdiff
path: root/embassy-net-ppp/src/lib.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-08-25 01:04:51 +0200
committerDario Nieuwenhuis <[email protected]>2023-08-25 20:45:23 +0200
commitaacf14b62a28277599871edf74106b1cd2e01795 (patch)
tree13b8a9b77bd1bb13c8c17ed020c5fafa95967276 /embassy-net-ppp/src/lib.rs
parent100200d021bbf650f7dd569414ee52b2d5ac10f0 (diff)
net-ppp: Add it.
Diffstat (limited to 'embassy-net-ppp/src/lib.rs')
-rw-r--r--embassy-net-ppp/src/lib.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/embassy-net-ppp/src/lib.rs b/embassy-net-ppp/src/lib.rs
new file mode 100644
index 000000000..7853e04ab
--- /dev/null
+++ b/embassy-net-ppp/src/lib.rs
@@ -0,0 +1,165 @@
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::{select3, Either3};
12use embassy_net_driver_channel as ch;
13use embassy_net_driver_channel::driver::LinkState;
14use embassy_sync::blocking_mutex::raw::NoopRawMutex;
15use embassy_sync::signal::Signal;
16use embedded_io_async::{BufRead, Write, WriteAllError};
17use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
18
19const MTU: usize = 1500;
20
21/// Type alias for the embassy-net driver.
22pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
23
24/// Internal state for the embassy-net integration.
25pub struct State<const N_RX: usize, const N_TX: usize> {
26 ch_state: ch::State<MTU, N_RX, N_TX>,
27}
28
29impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
30 /// Create a new `State`.
31 pub const fn new() -> Self {
32 Self {
33 ch_state: ch::State::new(),
34 }
35 }
36}
37
38/// Background runner for the driver.
39///
40/// You must call `.run()` in a background task for the driver to operate.
41pub struct Runner<'d, R: BufRead, W: Write> {
42 ch: ch::Runner<'d, MTU>,
43 r: R,
44 w: W,
45}
46
47/// Error returned by [`Runner::run`].
48#[derive(Debug)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum RunError<RE, WE> {
51 /// Reading from the serial port failed.
52 Read(RE),
53 /// Writing to the serial port failed.
54 Write(WE),
55 /// Writing to the serial port wrote zero bytes, indicating it can't accept more data.
56 WriteZero,
57 /// Writing to the serial got EOF.
58 Eof,
59}
60
61impl<'d, R: BufRead, W: Write> Runner<'d, R, W> {
62 /// You must call this in a background task for the driver to operate.
63 pub async fn run(mut self) -> Result<Infallible, RunError<R::Error, W::Error>> {
64 let config = ppproto::Config {
65 username: b"myuser",
66 password: b"mypass",
67 };
68 let mut ppp = PPPoS::new(config);
69 ppp.open().unwrap();
70
71 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
72 state_chan.set_link_state(LinkState::Down);
73 let _ondrop = OnDrop::new(|| state_chan.set_link_state(LinkState::Down));
74
75 let mut rx_buf = [0; 2048];
76 let mut tx_buf = [0; 2048];
77
78 let poll_signal: Signal<NoopRawMutex, ()> = Signal::new();
79 poll_signal.signal(());
80
81 loop {
82 let mut poll = false;
83 match select3(self.r.fill_buf(), tx_chan.tx_buf(), poll_signal.wait()).await {
84 Either3::First(r) => {
85 let data = r.map_err(RunError::Read)?;
86 if data.is_empty() {
87 return Err(RunError::Eof);
88 }
89 let n = ppp.consume(data, &mut rx_buf);
90 self.r.consume(n);
91 poll = true;
92 }
93 Either3::Second(pkt) => {
94 match ppp.send(pkt, &mut tx_buf) {
95 Ok(n) => match self.w.write_all(&tx_buf[..n]).await {
96 Ok(()) => {}
97 Err(WriteAllError::WriteZero) => return Err(RunError::WriteZero),
98 Err(WriteAllError::Other(e)) => return Err(RunError::Write(e)),
99 },
100 Err(BufferFullError) => unreachable!(),
101 }
102 tx_chan.tx_done();
103 }
104 Either3::Third(_) => poll = true,
105 }
106
107 if poll {
108 match ppp.poll(&mut tx_buf, &mut rx_buf) {
109 PPPoSAction::None => {}
110 PPPoSAction::Received(rg) => {
111 let pkt = &rx_buf[rg];
112 let buf = rx_chan.rx_buf().await; // TODO: fix possible deadlock
113 buf[..pkt.len()].copy_from_slice(pkt);
114 rx_chan.rx_done(pkt.len());
115
116 poll_signal.signal(());
117 }
118 PPPoSAction::Transmit(n) => {
119 match self.w.write_all(&tx_buf[..n]).await {
120 Ok(()) => {}
121 Err(WriteAllError::WriteZero) => return Err(RunError::WriteZero),
122 Err(WriteAllError::Other(e)) => return Err(RunError::Write(e)),
123 }
124 poll_signal.signal(());
125 }
126 }
127
128 match ppp.status().phase {
129 ppproto::Phase::Open => state_chan.set_link_state(LinkState::Up),
130 _ => state_chan.set_link_state(LinkState::Down),
131 }
132 }
133 }
134 }
135}
136
137/// Create a PPP embassy-net driver instance.
138///
139/// This returns two structs:
140/// - a `Device` that you must pass to the `embassy-net` stack.
141/// - a `Runner`. You must call `.run()` on it in a background task.
142pub fn new<'a, const N_RX: usize, const N_TX: usize, R: BufRead, W: Write>(
143 state: &'a mut State<N_RX, N_TX>,
144 r: R,
145 w: W,
146) -> (Device<'a>, Runner<'a, R, W>) {
147 let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ip);
148 (device, Runner { ch: runner, r, w })
149}
150
151struct OnDrop<F: FnOnce()> {
152 f: MaybeUninit<F>,
153}
154
155impl<F: FnOnce()> OnDrop<F> {
156 fn new(f: F) -> Self {
157 Self { f: MaybeUninit::new(f) }
158 }
159}
160
161impl<F: FnOnce()> Drop for OnDrop<F> {
162 fn drop(&mut self) {
163 unsafe { self.f.as_ptr().read()() }
164 }
165}