diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-06-29 19:51:16 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-06-29 19:55:32 +0200 |
| commit | 6eac49186d5a5da4c310027e59adcd0bf44ae514 (patch) | |
| tree | 3cbd9c15c68c7ee3859a17ec6d6fc7150e569686 /embassy-net-driver-channel | |
| parent | 4feabb13bfbda46de74be09566118adc1ba49d5d (diff) | |
Release embassy-net v0.1
Diffstat (limited to 'embassy-net-driver-channel')
| -rw-r--r-- | embassy-net-driver-channel/Cargo.toml | 11 | ||||
| -rw-r--r-- | embassy-net-driver-channel/README.md | 96 | ||||
| -rw-r--r-- | embassy-net-driver-channel/src/lib.rs | 1 |
3 files changed, 108 insertions, 0 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 @@ | |||
| 2 | name = "embassy-net-driver-channel" | 2 | name = "embassy-net-driver-channel" |
| 3 | version = "0.1.0" | 3 | version = "0.1.0" |
| 4 | edition = "2021" | 4 | edition = "2021" |
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." | ||
| 7 | repository = "https://github.com/embassy-rs/embassy" | ||
| 8 | categories = [ | ||
| 9 | "embedded", | ||
| 10 | "no-std", | ||
| 11 | "asynchronous", | ||
| 12 | ] | ||
| 5 | 13 | ||
| 6 | [package.metadata.embassy_docs] | 14 | [package.metadata.embassy_docs] |
| 7 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" | 15 | src_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 | |||
| 9 | features = ["defmt"] | 17 | features = ["defmt"] |
| 10 | target = "thumbv7em-none-eabi" | 18 | target = "thumbv7em-none-eabi" |
| 11 | 19 | ||
| 20 | [package.metadata.docs.rs] | ||
| 21 | features = ["defmt"] | ||
| 22 | |||
| 12 | [dependencies] | 23 | [dependencies] |
| 13 | defmt = { version = "0.3", optional = true } | 24 | defmt = { version = "0.3", optional = true } |
| 14 | log = { version = "0.4.14", optional = true } | 25 | log = { 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 | |||
| 3 | This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a | ||
| 4 | higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly. | ||
| 5 | |||
| 6 | The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by | ||
| 7 | hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net` | ||
| 8 | knows when to poll your driver again to make more progress. | ||
| 9 | |||
| 10 | With `embassy-net-driver-channel` | ||
| 11 | |||
| 12 | ## A note about deadlocks | ||
| 13 | |||
| 14 | When implementing a driver using this crate, it might be tempting to write it in the most straightforward way: | ||
| 15 | |||
| 16 | ```rust,ignore | ||
| 17 | loop { | ||
| 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 | |||
| 40 | However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load. | ||
| 41 | |||
| 42 | The 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 | |||
| 44 | The 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 | ||
| 47 | loop { | ||
| 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 | |||
| 75 | These `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 | |||
| 85 | This crate can run on any executor. | ||
| 86 | |||
| 87 | |||
| 88 | ## License | ||
| 89 | |||
| 90 | This 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 | |||
| 96 | at 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! |
| 4 | mod fmt; | 5 | mod fmt; |
