diff options
| author | Ulf Lilleengen <[email protected]> | 2022-08-08 16:51:34 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2022-08-08 16:51:34 +0200 |
| commit | 18671b94ba173d6b5c2d2ec5e3569e39a03b61bb (patch) | |
| tree | 23ec7aa298e049957c307b7c1b973201bf7f5f7d | |
| parent | b7b4c84067e0e85fa641a540458438297176f2e4 (diff) | |
Implement embedded-nal-async traits for embassy-net
| -rw-r--r-- | embassy-net/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-net/src/tcp.rs | 167 | ||||
| -rw-r--r-- | examples/stm32h7/Cargo.toml | 2 |
3 files changed, 171 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 = [] | |||
| 31 | pool-32 = [] | 31 | pool-32 = [] |
| 32 | pool-64 = [] | 32 | pool-64 = [] |
| 33 | pool-128 = [] | 33 | pool-128 = [] |
| 34 | unstable-traits = [] | ||
| 34 | 35 | ||
| 35 | [dependencies] | 36 | [dependencies] |
| 36 | 37 | ||
| @@ -48,6 +49,8 @@ generic-array = { version = "0.14.4", default-features = false } | |||
| 48 | stable_deref_trait = { version = "1.2.0", default-features = false } | 49 | stable_deref_trait = { version = "1.2.0", default-features = false } |
| 49 | futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } | 50 | futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } |
| 50 | atomic-pool = "0.2.1" | 51 | atomic-pool = "0.2.1" |
| 52 | atomic-polyfill = "0.1.5" | ||
| 53 | embedded-nal-async = "0.2.0" | ||
| 51 | 54 | ||
| 52 | [dependencies.smoltcp] | 55 | [dependencies.smoltcp] |
| 53 | version = "0.8.0" | 56 | version = "0.8.0" |
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c18391ace..96a6dfe28 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs | |||
| @@ -328,3 +328,170 @@ 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")] | ||
| 333 | pub 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 | pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { | ||
| 343 | stack: &'d Stack<D>, | ||
| 344 | tx: &'d BufferPool<TX_SZ, N>, | ||
| 345 | rx: &'d BufferPool<RX_SZ, N>, | ||
| 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 | pub fn new(stack: &'d Stack<D>, tx: &'d BufferPool<TX_SZ, N>, rx: &'d BufferPool<RX_SZ, N>) -> Self { | ||
| 350 | Self { stack, tx, rx } | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect | ||
| 355 | for TcpClient<'d, D, N, TX_SZ, RX_SZ> | ||
| 356 | { | ||
| 357 | type Error = Error; | ||
| 358 | type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm; | ||
| 359 | type ConnectFuture<'m> = impl Future<Output = Result<Self::Connection<'m>, Self::Error>> + 'm | ||
| 360 | where | ||
| 361 | Self: 'm; | ||
| 362 | |||
| 363 | fn connect<'m>(&'m self, remote: embedded_nal_async::SocketAddr) -> Self::ConnectFuture<'m> { | ||
| 364 | async move { | ||
| 365 | let addr: crate::IpAddress = match remote.ip() { | ||
| 366 | IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), | ||
| 367 | #[cfg(feature = "proto-ipv6")] | ||
| 368 | IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), | ||
| 369 | #[cfg(not(feature = "proto-ipv6"))] | ||
| 370 | IpAddr::V6(_) => panic!("ipv6 support not enabled"), | ||
| 371 | }; | ||
| 372 | let remote_endpoint = (addr, remote.port()); | ||
| 373 | let mut socket = TcpConnection::new(&self.stack, self.tx, self.rx)?; | ||
| 374 | socket | ||
| 375 | .socket | ||
| 376 | .connect(remote_endpoint) | ||
| 377 | .await | ||
| 378 | .map_err(|_| Error::ConnectionReset)?; | ||
| 379 | Ok(socket) | ||
| 380 | } | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> { | ||
| 385 | socket: TcpSocket<'d>, | ||
| 386 | tx: &'d BufferPool<TX_SZ, N>, | ||
| 387 | rx: &'d BufferPool<RX_SZ, N>, | ||
| 388 | txb: NonNull<[u8; TX_SZ]>, | ||
| 389 | rxb: NonNull<[u8; RX_SZ]>, | ||
| 390 | } | ||
| 391 | |||
| 392 | impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> { | ||
| 393 | fn new<D: Device>( | ||
| 394 | stack: &'d Stack<D>, | ||
| 395 | tx: &'d BufferPool<TX_SZ, N>, | ||
| 396 | rx: &'d BufferPool<RX_SZ, N>, | ||
| 397 | ) -> Result<Self, Error> { | ||
| 398 | let mut txb = tx.alloc().ok_or(Error::ConnectionReset)?; | ||
| 399 | let mut rxb = rx.alloc().ok_or(Error::ConnectionReset)?; | ||
| 400 | Ok(Self { | ||
| 401 | socket: unsafe { TcpSocket::new(stack, rxb.as_mut(), txb.as_mut()) }, | ||
| 402 | tx, | ||
| 403 | rx, | ||
| 404 | txb, | ||
| 405 | rxb, | ||
| 406 | }) | ||
| 407 | } | ||
| 408 | } | ||
| 409 | |||
| 410 | impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> Drop for TcpConnection<'d, N, TX_SZ, RX_SZ> { | ||
| 411 | fn drop(&mut self) { | ||
| 412 | unsafe { | ||
| 413 | self.socket.close(); | ||
| 414 | self.rx.free(self.rxb); | ||
| 415 | self.tx.free(self.txb); | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::Io | ||
| 421 | for TcpConnection<'d, N, TX_SZ, RX_SZ> | ||
| 422 | { | ||
| 423 | type Error = Error; | ||
| 424 | } | ||
| 425 | |||
| 426 | impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read | ||
| 427 | for TcpConnection<'d, N, TX_SZ, RX_SZ> | ||
| 428 | { | ||
| 429 | type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 430 | where | ||
| 431 | Self: 'a; | ||
| 432 | |||
| 433 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 434 | self.socket.read(buf) | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write | ||
| 439 | for TcpConnection<'d, N, TX_SZ, RX_SZ> | ||
| 440 | { | ||
| 441 | type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 442 | where | ||
| 443 | Self: 'a; | ||
| 444 | |||
| 445 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 446 | self.socket.write(buf) | ||
| 447 | } | ||
| 448 | |||
| 449 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | ||
| 450 | where | ||
| 451 | Self: 'a; | ||
| 452 | |||
| 453 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | ||
| 454 | self.socket.flush() | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | pub type BufferPool<const BUFSZ: usize, const N: usize> = Pool<[u8; BUFSZ], N>; | ||
| 459 | |||
| 460 | pub struct Pool<T, const N: usize> { | ||
| 461 | used: [AtomicBool; N], | ||
| 462 | data: [UnsafeCell<MaybeUninit<T>>; N], | ||
| 463 | } | ||
| 464 | |||
| 465 | impl<T, const N: usize> Pool<T, N> { | ||
| 466 | const VALUE: AtomicBool = AtomicBool::new(false); | ||
| 467 | const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit()); | ||
| 468 | |||
| 469 | pub const fn new() -> Self { | ||
| 470 | Self { | ||
| 471 | used: [Self::VALUE; N], | ||
| 472 | data: [Self::UNINIT; N], | ||
| 473 | } | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | impl<T, const N: usize> Pool<T, N> { | ||
| 478 | fn alloc(&self) -> Option<NonNull<T>> { | ||
| 479 | for n in 0..N { | ||
| 480 | if self.used[n].swap(true, Ordering::SeqCst) == false { | ||
| 481 | let p = self.data[n].get() as *mut T; | ||
| 482 | return Some(unsafe { NonNull::new_unchecked(p) }); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | None | ||
| 486 | } | ||
| 487 | |||
| 488 | /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet. | ||
| 489 | unsafe fn free(&self, p: NonNull<T>) { | ||
| 490 | let origin = self.data.as_ptr() as *mut T; | ||
| 491 | let n = p.as_ptr().offset_from(origin); | ||
| 492 | assert!(n >= 0); | ||
| 493 | assert!((n as usize) < N); | ||
| 494 | self.used[n as usize].store(false, Ordering::SeqCst); | ||
| 495 | } | ||
| 496 | } | ||
| 497 | } | ||
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 8b1999b30..07b7e4931 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -7,7 +7,7 @@ version = "0.1.0" | |||
| 7 | embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } | 7 | embassy-util = { version = "0.1.0", path = "../../embassy-util", features = ["defmt"] } |
| 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } | 8 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "time-tick-32768hz"] } |
| 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | 9 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } |
| 10 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 10 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } |
| 11 | embedded-io = { version = "0.3.0", features = ["async"] } | 11 | embedded-io = { version = "0.3.0", features = ["async"] } |
| 12 | 12 | ||
| 13 | defmt = "0.3" | 13 | defmt = "0.3" |
