aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-08-10 08:01:59 +0000
committerGitHub <[email protected]>2022-08-10 08:01:59 +0000
commitde22cb906567b1262f91398c82b6ed90803852fc (patch)
treeba3aa090ca446595fd041416d193299f9ff1eeb2
parentb7b4c84067e0e85fa641a540458438297176f2e4 (diff)
parent87401c49b71eb22a4f1a9ce4b318ebd20ea38000 (diff)
Merge #895
895: Implement embedded-nal-async traits for embassy-net r=lulf a=lulf Co-authored-by: Ulf Lilleengen <[email protected]>
-rw-r--r--embassy-net/Cargo.toml3
-rw-r--r--embassy-net/src/tcp.rs169
-rw-r--r--examples/stm32h7/Cargo.toml3
-rw-r--r--examples/stm32h7/src/bin/eth_client.rs125
4 files changed, 299 insertions, 1 deletions
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 64cb5bd8f..fface207b 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -31,6 +31,7 @@ pool-16 = []
31pool-32 = [] 31pool-32 = []
32pool-64 = [] 32pool-64 = []
33pool-128 = [] 33pool-128 = []
34unstable-traits = []
34 35
35[dependencies] 36[dependencies]
36 37
@@ -48,6 +49,8 @@ generic-array = { version = "0.14.4", default-features = false }
48stable_deref_trait = { version = "1.2.0", default-features = false } 49stable_deref_trait = { version = "1.2.0", default-features = false }
49futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } 50futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
50atomic-pool = "0.2.1" 51atomic-pool = "0.2.1"
52atomic-polyfill = "0.1.5"
53embedded-nal-async = "0.2.0"
51 54
52[dependencies.smoltcp] 55[dependencies.smoltcp]
53version = "0.8.0" 56version = "0.8.0"
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs
index c18391ace..2e276ecd3 100644
--- a/embassy-net/src/tcp.rs
+++ b/embassy-net/src/tcp.rs
@@ -328,3 +328,172 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
328 self.io.flush() 328 self.io.flush()
329 } 329 }
330} 330}
331
332#[cfg(feature = "unstable-traits")]
333pub mod client {
334 use core::mem::MaybeUninit;
335 use core::ptr::NonNull;
336
337 use atomic_polyfill::{AtomicBool, Ordering};
338 use embedded_nal_async::IpAddr;
339
340 use super::*;
341
342 /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ.
343 pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> {
344 stack: &'d Stack<D>,
345 state: &'d TcpClientState<N, TX_SZ, RX_SZ>,
346 }
347
348 impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> {
349 /// Create a new TcpClient
350 pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self {
351 Self { stack, state }
352 }
353 }
354
355 impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect
356 for TcpClient<'d, D, N, TX_SZ, RX_SZ>
357 {
358 type Error = Error;
359 type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm;
360 type ConnectFuture<'m> = impl Future<Output = Result<Self::Connection<'m>, Self::Error>> + 'm
361 where
362 Self: 'm;
363
364 fn connect<'m>(&'m self, remote: embedded_nal_async::SocketAddr) -> Self::ConnectFuture<'m> {
365 async move {
366 let addr: crate::IpAddress = match remote.ip() {
367 IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),
368 #[cfg(feature = "proto-ipv6")]
369 IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())),
370 #[cfg(not(feature = "proto-ipv6"))]
371 IpAddr::V6(_) => panic!("ipv6 support not enabled"),
372 };
373 let remote_endpoint = (addr, remote.port());
374 let mut socket = TcpConnection::new(&self.stack, self.state)?;
375 socket
376 .socket
377 .connect(remote_endpoint)
378 .await
379 .map_err(|_| Error::ConnectionReset)?;
380 Ok(socket)
381 }
382 }
383 }
384
385 pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> {
386 socket: TcpSocket<'d>,
387 state: &'d TcpClientState<N, TX_SZ, RX_SZ>,
388 bufs: NonNull<([u8; TX_SZ], [u8; RX_SZ])>,
389 }
390
391 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> {
392 fn new<D: Device>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> {
393 let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?;
394 Ok(Self {
395 socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) },
396 state,
397 bufs,
398 })
399 }
400 }
401
402 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> Drop for TcpConnection<'d, N, TX_SZ, RX_SZ> {
403 fn drop(&mut self) {
404 unsafe {
405 self.socket.close();
406 self.state.pool.free(self.bufs);
407 }
408 }
409 }
410
411 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::Io
412 for TcpConnection<'d, N, TX_SZ, RX_SZ>
413 {
414 type Error = Error;
415 }
416
417 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read
418 for TcpConnection<'d, N, TX_SZ, RX_SZ>
419 {
420 type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
421 where
422 Self: 'a;
423
424 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
425 self.socket.read(buf)
426 }
427 }
428
429 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write
430 for TcpConnection<'d, N, TX_SZ, RX_SZ>
431 {
432 type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
433 where
434 Self: 'a;
435
436 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
437 self.socket.write(buf)
438 }
439
440 type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
441 where
442 Self: 'a;
443
444 fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
445 self.socket.flush()
446 }
447 }
448
449 /// State for TcpClient
450 pub struct TcpClientState<const N: usize, const TX_SZ: usize, const RX_SZ: usize> {
451 pool: Pool<([u8; TX_SZ], [u8; RX_SZ]), N>,
452 }
453
454 impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClientState<N, TX_SZ, RX_SZ> {
455 pub const fn new() -> Self {
456 Self { pool: Pool::new() }
457 }
458 }
459
460 unsafe impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> Sync for TcpClientState<N, TX_SZ, RX_SZ> {}
461
462 struct Pool<T, const N: usize> {
463 used: [AtomicBool; N],
464 data: [UnsafeCell<MaybeUninit<T>>; N],
465 }
466
467 impl<T, const N: usize> Pool<T, N> {
468 const VALUE: AtomicBool = AtomicBool::new(false);
469 const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
470
471 const fn new() -> Self {
472 Self {
473 used: [Self::VALUE; N],
474 data: [Self::UNINIT; N],
475 }
476 }
477 }
478
479 impl<T, const N: usize> Pool<T, N> {
480 fn alloc(&self) -> Option<NonNull<T>> {
481 for n in 0..N {
482 if self.used[n].swap(true, Ordering::SeqCst) == false {
483 let p = self.data[n].get() as *mut T;
484 return Some(unsafe { NonNull::new_unchecked(p) });
485 }
486 }
487 None
488 }
489
490 /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet.
491 unsafe fn free(&self, p: NonNull<T>) {
492 let origin = self.data.as_ptr() as *mut T;
493 let n = p.as_ptr().offset_from(origin);
494 assert!(n >= 0);
495 assert!((n as usize) < N);
496 self.used[n as usize].store(false, Ordering::SeqCst);
497 }
498 }
499}
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 8b1999b30..896046759 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -7,7 +7,7 @@ version = "0.1.0"
7embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } 7embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] }
8embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } 8embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] }
9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } 9embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
10embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } 10embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] }
11embedded-io = { version = "0.3.0", features = ["async"] } 11embedded-io = { version = "0.3.0", features = ["async"] }
12 12
13defmt = "0.3" 13defmt = "0.3"
@@ -18,6 +18,7 @@ cortex-m-rt = "0.7.0"
18embedded-hal = "0.2.6" 18embedded-hal = "0.2.6"
19embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } 19embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
20embedded-hal-async = { version = "0.1.0-alpha.1" } 20embedded-hal-async = { version = "0.1.0-alpha.1" }
21embedded-nal-async = "0.2.0"
21panic-probe = { version = "0.3", features = ["print-defmt"] } 22panic-probe = { version = "0.3", features = ["print-defmt"] }
22futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 23futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
23heapless = { version = "0.7.5", default-features = false } 24heapless = { version = "0.7.5", default-features = false }
diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs
new file mode 100644
index 000000000..a66c6f196
--- /dev/null
+++ b/examples/stm32h7/src/bin/eth_client.rs
@@ -0,0 +1,125 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::executor::Spawner;
7use embassy_executor::time::{Duration, Timer};
8use embassy_net::tcp::client::{TcpClient, TcpClientState};
9use embassy_net::{Stack, StackResources};
10use embassy_stm32::eth::generic_smi::GenericSMI;
11use embassy_stm32::eth::{Ethernet, State};
12use embassy_stm32::peripherals::ETH;
13use embassy_stm32::rng::Rng;
14use embassy_stm32::time::mhz;
15use embassy_stm32::{interrupt, Config, Peripherals};
16use embassy_util::Forever;
17use embedded_io::asynch::Write;
18use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect};
19use rand_core::RngCore;
20use {defmt_rtt as _, panic_probe as _};
21
22macro_rules! forever {
23 ($val:expr) => {{
24 type T = impl Sized;
25 static FOREVER: Forever<T> = Forever::new();
26 FOREVER.put_with(move || $val)
27 }};
28}
29
30type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>;
31
32#[embassy_executor::task]
33async fn net_task(stack: &'static Stack<Device>) -> ! {
34 stack.run().await
35}
36
37pub fn config() -> Config {
38 let mut config = Config::default();
39 config.rcc.sys_ck = Some(mhz(400));
40 config.rcc.hclk = Some(mhz(200));
41 config.rcc.pll1.q_ck = Some(mhz(100));
42 config
43}
44
45#[embassy_executor::main(config = "config()")]
46async fn main(spawner: Spawner, p: Peripherals) -> ! {
47 info!("Hello World!");
48
49 // Generate random seed.
50 let mut rng = Rng::new(p.RNG);
51 let mut seed = [0; 8];
52 rng.fill_bytes(&mut seed);
53 let seed = u64::from_le_bytes(seed);
54
55 let eth_int = interrupt::take!(ETH);
56 let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
57
58 let device = unsafe {
59 Ethernet::new(
60 forever!(State::new()),
61 p.ETH,
62 eth_int,
63 p.PA1,
64 p.PA2,
65 p.PC1,
66 p.PA7,
67 p.PC4,
68 p.PC5,
69 p.PG13,
70 p.PB13,
71 p.PG11,
72 GenericSMI,
73 mac_addr,
74 0,
75 )
76 };
77
78 let config = embassy_net::ConfigStrategy::Dhcp;
79 //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
80 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
81 // dns_servers: Vec::new(),
82 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
83 //});
84
85 // Init network stack
86 let stack = &*forever!(Stack::new(
87 device,
88 config,
89 forever!(StackResources::<1, 2, 8>::new()),
90 seed
91 ));
92
93 // Launch network task
94 unwrap!(spawner.spawn(net_task(&stack)));
95
96 info!("Network task initialized");
97
98 // To ensure DHCP configuration before trying connect
99 Timer::after(Duration::from_secs(20)).await;
100
101 static STATE: TcpClientState<1, 1024, 1024> = TcpClientState::new();
102 let client = TcpClient::new(&stack, &STATE);
103
104 loop {
105 let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(10, 42, 0, 1), 8000));
106
107 info!("connecting...");
108 let r = client.connect(addr).await;
109 if let Err(e) = r {
110 info!("connect error: {:?}", e);
111 Timer::after(Duration::from_secs(1)).await;
112 continue;
113 }
114 let mut connection = r.unwrap();
115 info!("connected!");
116 loop {
117 let r = connection.write_all(b"Hello\n").await;
118 if let Err(e) = r {
119 info!("write error: {:?}", e);
120 return;
121 }
122 Timer::after(Duration::from_secs(1)).await;
123 }
124 }
125}