aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-net-driver-channel/Cargo.toml11
-rw-r--r--embassy-net-driver-channel/README.md96
-rw-r--r--embassy-net-driver-channel/src/lib.rs1
-rw-r--r--embassy-net-driver/Cargo.toml11
-rw-r--r--embassy-net-driver/README.md16
-rw-r--r--embassy-net-w5500/src/lib.rs5
-rw-r--r--embassy-net/Cargo.toml9
-rw-r--r--embassy-net/README.md64
-rw-r--r--embassy-net/src/lib.rs24
-rw-r--r--embassy-usb/src/class/cdc_ncm/embassy_net.rs5
-rw-r--r--examples/std/README.md23
11 files changed, 238 insertions, 27 deletions
diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml
index e475551e1..bee2e3021 100644
--- a/embassy-net-driver-channel/Cargo.toml
+++ b/embassy-net-driver-channel/Cargo.toml
@@ -2,6 +2,14 @@
2name = "embassy-net-driver-channel" 2name = "embassy-net-driver-channel"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
6description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
7repository = "https://github.com/embassy-rs/embassy"
8categories = [
9 "embedded",
10 "no-std",
11 "asynchronous",
12]
5 13
6[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
@@ -9,6 +17,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
9features = ["defmt"] 17features = ["defmt"]
10target = "thumbv7em-none-eabi" 18target = "thumbv7em-none-eabi"
11 19
20[package.metadata.docs.rs]
21features = ["defmt"]
22
12[dependencies] 23[dependencies]
13defmt = { version = "0.3", optional = true } 24defmt = { version = "0.3", optional = true }
14log = { version = "0.4.14", optional = true } 25log = { version = "0.4.14", optional = true }
diff --git a/embassy-net-driver-channel/README.md b/embassy-net-driver-channel/README.md
new file mode 100644
index 000000000..dd90e7ad2
--- /dev/null
+++ b/embassy-net-driver-channel/README.md
@@ -0,0 +1,96 @@
1# embassy-net-driver-channel
2
3This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a
4higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.
5
6The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by
7hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
8knows when to poll your driver again to make more progress.
9
10With `embassy-net-driver-channel`
11
12## A note about deadlocks
13
14When implementing a driver using this crate, it might be tempting to write it in the most straightforward way:
15
16```rust,ignore
17loop {
18 // Wait for either..
19 match select(
20 // ... the chip signaling an interrupt, indicating a packet is available to receive, or
21 irq_pin.wait_for_low(),
22 // ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
23 tx_chan.tx_buf(),
24 ).await {
25 Either::First(_) => {
26 // a packet is ready to be received!
27 let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
28 let n = receive_packet_over_spi(buf).await;
29 rx_chan.rx_done(n);
30 }
31 Either::Second(buf) => {
32 // a packet is ready to be sent!
33 send_packet_over_spi(buf).await;
34 tx_chan.tx_done();
35 }
36 }
37}
38```
39
40However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.
41
42The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
43
44The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
45
46```rust,ignore
47loop {
48 // Wait for either..
49 match select(
50 async {
51 // ... the chip signaling an interrupt, indicating a packet is available to receive
52 irq_pin.wait_for_low().await;
53 // *AND* the buffer is ready...
54 rx_chan.rx_buf().await
55 },
56 // ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet
57 tx_chan.tx_buf(),
58 ).await {
59 Either::First(buf) => {
60 // a packet is ready to be received!
61 let n = receive_packet_over_spi(buf).await;
62 rx_chan.rx_done(n);
63 }
64 Either::Second(buf) => {
65 // a packet is ready to be sent!
66 send_packet_over_spi(buf).await;
67 tx_chan.tx_done();
68 }
69 }
70}
71```
72
73## Examples
74
75These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.
76
77- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
78- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
79- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
80- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
81
82
83## Interoperability
84
85This crate can run on any executor.
86
87
88## License
89
90This work is licensed under either of
91
92- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
93 http://www.apache.org/licenses/LICENSE-2.0)
94- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
95
96at your option.
diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs
index 0c8dcc22b..02a4c00d6 100644
--- a/embassy-net-driver-channel/src/lib.rs
+++ b/embassy-net-driver-channel/src/lib.rs
@@ -1,4 +1,5 @@
1#![no_std] 1#![no_std]
2#![doc = include_str!("../README.md")]
2 3
3// must go first! 4// must go first!
4mod fmt; 5mod fmt;
diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml
index ff6f29355..da6d9ad62 100644
--- a/embassy-net-driver/Cargo.toml
+++ b/embassy-net-driver/Cargo.toml
@@ -3,7 +3,13 @@ name = "embassy-net-driver"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6description = "Driver trait for the `embassy-net` async TCP/IP network stack."
7repository = "https://github.com/embassy-rs/embassy"
8categories = [
9 "embedded",
10 "no-std",
11 "asynchronous",
12]
7 13
8[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
@@ -11,5 +17,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
11features = ["defmt"] 17features = ["defmt"]
12target = "thumbv7em-none-eabi" 18target = "thumbv7em-none-eabi"
13 19
20[package.metadata.docs.rs]
21features = ["defmt"]
22
14[dependencies] 23[dependencies]
15defmt = { version = "0.3", optional = true } \ No newline at end of file 24defmt = { version = "0.3", optional = true } \ No newline at end of file
diff --git a/embassy-net-driver/README.md b/embassy-net-driver/README.md
index 84f25492d..6a757380d 100644
--- a/embassy-net-driver/README.md
+++ b/embassy-net-driver/README.md
@@ -1,5 +1,21 @@
1# embassy-net-driver 1# embassy-net-driver
2 2
3This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support
4for a new hardware platform.
5
6If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.
7
8If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate.
9This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,
10if the driver trait has not had breaking changes.
11
12See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API
13to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
14packet queues for RX and TX.
15
16## Interoperability
17
18This crate can run on any executor.
3 19
4## License 20## License
5 21
diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs
index 6821373e3..efd9bed66 100644
--- a/embassy-net-w5500/src/lib.rs
+++ b/embassy-net-w5500/src/lib.rs
@@ -1,5 +1,6 @@
1//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
1#![no_std] 2#![no_std]
2/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. 3
3mod device; 4mod device;
4mod socket; 5mod socket;
5mod spi; 6mod spi;
@@ -77,7 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
77 } 78 }
78} 79}
79 80
80/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). 81/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net).
81pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( 82pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
82 mac_addr: [u8; 6], 83 mac_addr: [u8; 6],
83 state: &'a mut State<N_RX, N_TX>, 84 state: &'a mut State<N_RX, N_TX>,
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index cef8247eb..e89039daa 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -3,7 +3,13 @@ name = "embassy-net"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6description = "Async TCP/IP network stack for embedded systems"
7repository = "https://github.com/embassy-rs/embassy"
8categories = [
9 "embedded",
10 "no-std",
11 "asynchronous",
12]
7 13
8[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
@@ -44,7 +50,6 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
44] } 50] }
45 51
46embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } 52embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
47embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
48embassy-time = { version = "0.1.0", path = "../embassy-time" } 53embassy-time = { version = "0.1.0", path = "../embassy-time" }
49embassy-sync = { version = "0.2.0", path = "../embassy-sync" } 54embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
50embedded-io = { version = "0.4.0", optional = true } 55embedded-io = { version = "0.4.0", optional = true }
diff --git a/embassy-net/README.md b/embassy-net/README.md
index 470926c58..48f9fd832 100644
--- a/embassy-net/README.md
+++ b/embassy-net/README.md
@@ -1,30 +1,56 @@
1# embassy-net 1# embassy-net
2 2
3embassy-net contains an async network API based on smoltcp and embassy, designed 3`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.
4for embedded systems.
5 4
6## Running the example 5It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
6API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
7memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
7 8
8First, create the tap0 interface. You only need to do this once. 9## Features
9 10
10```sh 11- IPv4, IPv6
11sudo ip tuntap add name tap0 mode tap user $USER 12- Ethernet and bare-IP mediums.
12sudo ip link set tap0 up 13- TCP, UDP, DNS, DHCPv4, IGMPv4
13sudo ip addr add 192.168.69.100/24 dev tap0 14- TCP sockets implement the `embedded-io` async traits.
14sudo ip -6 addr add fe80::100/64 dev tap0
15sudo ip -6 addr add fdaa::100/64 dev tap0
16sudo ip -6 route add fe80::/64 dev tap0
17sudo ip -6 route add fdaa::/64 dev tap0
18```
19 15
20Second, have something listening there. For example `nc -l 8000` 16See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and
17unimplemented features of the network protocols.
21 18
22Then run the example located in the `examples` folder: 19## Hardware support
23 20
24```sh 21- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.
25cd $EMBASSY_ROOT/examples/std/ 22- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
26cargo run --bin net -- --static-ip 23- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
27``` 24- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
25- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
26- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
27
28## Examples
29
30- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.
31- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.
32- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)
33
34## Adding support for new hardware
35
36To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or
37an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)
38traits.
39
40Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API
41to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
42packet queues for RX and TX.
43
44Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.
45This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver
46trait has not had breaking changes.
47
48## Interoperability
49
50This crate can run on any executor.
51
52[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must
53link an `embassy-time` driver in your project to use this crate.
28 54
29## License 55## License
30 56
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index 17a7a22a2..840d7a09a 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -419,7 +419,29 @@ impl<D: Driver + 'static> Stack<D> {
419 }) 419 })
420 .await?; 420 .await?;
421 421
422 use embassy_hal_common::drop::OnDrop; 422 #[must_use = "to delay the drop handler invocation to the end of the scope"]
423 struct OnDrop<F: FnOnce()> {
424 f: core::mem::MaybeUninit<F>,
425 }
426
427 impl<F: FnOnce()> OnDrop<F> {
428 fn new(f: F) -> Self {
429 Self {
430 f: core::mem::MaybeUninit::new(f),
431 }
432 }
433
434 fn defuse(self) {
435 core::mem::forget(self)
436 }
437 }
438
439 impl<F: FnOnce()> Drop for OnDrop<F> {
440 fn drop(&mut self) {
441 unsafe { self.f.as_ptr().read()() }
442 }
443 }
444
423 let drop = OnDrop::new(|| { 445 let drop = OnDrop::new(|| {
424 self.with_mut(|s, i| { 446 self.with_mut(|s, i| {
425 let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket); 447 let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index bc79b3671..670709021 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -1,4 +1,5 @@
1//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class. 1//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class.
2
2use embassy_futures::select::{select, Either}; 3use embassy_futures::select::{select, Either};
3use embassy_net_driver_channel as ch; 4use embassy_net_driver_channel as ch;
4use embassy_net_driver_channel::driver::LinkState; 5use embassy_net_driver_channel::driver::LinkState;
@@ -79,7 +80,7 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
79pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; 80pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
80 81
81impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 82impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
82 /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net). 83 /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](https://crates.io/crates/embassy-net).
83 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( 84 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
84 self, 85 self,
85 state: &'d mut State<MTU, N_RX, N_TX>, 86 state: &'d mut State<MTU, N_RX, N_TX>,
diff --git a/examples/std/README.md b/examples/std/README.md
new file mode 100644
index 000000000..adc795928
--- /dev/null
+++ b/examples/std/README.md
@@ -0,0 +1,23 @@
1
2## Running the `embassy-net` examples
3
4First, create the tap0 interface. You only need to do this once.
5
6```sh
7sudo ip tuntap add name tap0 mode tap user $USER
8sudo ip link set tap0 up
9sudo ip addr add 192.168.69.100/24 dev tap0
10sudo ip -6 addr add fe80::100/64 dev tap0
11sudo ip -6 addr add fdaa::100/64 dev tap0
12sudo ip -6 route add fe80::/64 dev tap0
13sudo ip -6 route add fdaa::/64 dev tap0
14```
15
16Second, have something listening there. For example `nc -l 8000`
17
18Then run the example located in the `examples` folder:
19
20```sh
21cd $EMBASSY_ROOT/examples/std/
22cargo run --bin net -- --static-ip
23``` \ No newline at end of file