Compare commits

..

2 Commits

Author SHA1 Message Date
03b27cc395 Update h7 example. 2023-06-27 11:30:50 +02:00
a9c061bc5d usb: add high-level byte-oriented USB serial implementation.
Fixes #1588

The previous packet-level implementation is moved to `embassy_usb::class::cdc_acm::low_level`.
2023-06-27 11:29:56 +02:00
468 changed files with 5757 additions and 14206 deletions

41
.gitattributes vendored
View File

@ -1,41 +0,0 @@
* text=auto
*.adoc text
*.html text
*.in text
*.json text
*.md text
*.proto text
*.py text
*.rs text
*.service text
*.sh text
*.toml text
*.txt text
*.x text
*.yml text
*.raw binary
*.bin binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary

17
.github/ci/crlf.sh vendored
View File

@ -1,17 +0,0 @@
#!/bin/bash
## on push branch~=gh-readonly-queue/main/.*
## on pull_request
set -euo pipefail
FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
if [ -z "$FILES_WITH_CRLF" ]; then
echo -e "No files with CRLF endings found."
exit 0
else
NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l)
echo -e "ERROR: Found ${NR_FILES} files with CRLF endings."
echo "$FILES_WITH_CRLF"
exit "$NR_FILES"
fi

52
.github/ci/doc.sh vendored
View File

@ -6,7 +6,7 @@ set -euo pipefail
export RUSTUP_HOME=/ci/cache/rustup
export CARGO_HOME=/ci/cache/cargo
export CARGO_TARGET_DIR=/ci/cache/target
export BUILDER_THREADS=4
export BUILDER_THREADS=6
export BUILDER_COMPRESS=true
# force rustup to download the toolchain before starting building.
@ -15,32 +15,30 @@ export BUILDER_COMPRESS=true
# which makes rustup very sad
rustc --version > /dev/null
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup
docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup
docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup
export KUBECONFIG=/ci/secrets/kubeconfig.yml
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp webroot/crates $POD:/data
kubectl cp webroot/static $POD:/data
kubectl cp crates $POD:/data

2
.github/ci/test.sh vendored
View File

@ -13,7 +13,7 @@ hashtime save /ci/cache/filetime.json
cargo test --manifest-path ./embassy-sync/Cargo.toml
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
cargo test --manifest-path ./embassy-hal-common/Cargo.toml
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml

View File

@ -6,16 +6,16 @@
"rust-analyzer.check.allTargets": false,
"rust-analyzer.check.noDefaultFeatures": true,
"rust-analyzer.cargo.noDefaultFeatures": true,
"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
"rust-analyzer.cargo.features": [
///"nightly",
"nightly",
],
"rust-analyzer.linkedProjects": [
// Declare for the target you wish to develop
// "embassy-executor/Cargo.toml",
// "embassy-sync/Cargo.toml",
"examples/stm32wl/Cargo.toml",
"examples/nrf52840/Cargo.toml",
// "examples/nrf5340/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml",
@ -25,7 +25,6 @@
// "examples/stm32f1/Cargo.toml",
// "examples/stm32f2/Cargo.toml",
// "examples/stm32f3/Cargo.toml",
// "examples/stm32f334/Cargo.toml",
// "examples/stm32f4/Cargo.toml",
// "examples/stm32f7/Cargo.toml",
// "examples/stm32g0/Cargo.toml",
@ -41,5 +40,4 @@
// "examples/stm32wl/Cargo.toml",
// "examples/wasm/Cargo.toml",
],
"rust-analyzer.showUnlinkedFileNotification": false,
}

View File

@ -33,7 +33,6 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
- **Bluetooth** -
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
- **LoRa** -
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
@ -100,10 +99,10 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer
### Running examples
- Install `probe-rs`.
- Install `probe-rs-cli` with defmt support.
```bash
cargo install probe-rs --features cli
cargo install probe-rs-cli
```
- Change directory to the sample's base directory. For example:
@ -112,12 +111,6 @@ cargo install probe-rs --features cli
cd examples/nrf52840
```
- Ensure `Cargo.toml` sets the right feature for the name of the chip you are programming.
If this name is incorrect, the example may fail to run or immediately crash
after being programmed.
- Ensure `.cargo/config.toml` contains the name of the chip you are programming.
- Run the example
For example:
@ -126,8 +119,6 @@ For example:
cargo run --release --bin blinky
```
For more help getting started, see [Getting Started][1] and [Running the Examples][2].
## Developing Embassy with Rust Analyzer based editors
The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/)
@ -160,5 +151,3 @@ This work is licensed under either of
at your option.
[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started
[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples

20
ci.sh
View File

@ -5,6 +5,10 @@ set -euo pipefail
export RUSTFLAGS=-Dwarnings
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
# needed by wifi examples
export WIFI_NETWORK=x
export WIFI_PASSWORD=x
TARGET=$(rustc -vV | sed -n 's|host: ||p')
BUILD_EXTRA=""
@ -20,19 +24,20 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
@ -52,7 +57,6 @@ cargo batch \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \
@ -86,7 +90,6 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
@ -116,7 +119,6 @@ cargo batch \
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
--- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \

View File

@ -16,7 +16,9 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
@ -34,7 +36,6 @@ cargo batch \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,3 @@
Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439
Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)
## Changelog
* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 ot 7.95.62

View File

@ -8,6 +8,7 @@ use cyw43::SpiBusCyw43;
use embassy_rp::dma::Channel;
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
use embassy_rp::relocate::RelocatedProgram;
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
use fixed::FixedU32;
use pio_proc::pio_asm;
@ -87,6 +88,8 @@ where
".wrap"
);
let relocated = RelocatedProgram::new(&program.program);
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
pin_io.set_pull(Pull::None);
pin_io.set_schmitt(true);
@ -99,8 +102,7 @@ where
pin_clk.set_slew_rate(SlewRate::Fast);
let mut cfg = Config::default();
let loaded_program = common.load_program(&program.program);
cfg.use_program(&loaded_program, &[&pin_clk]);
cfg.use_program(&common.load_program(&relocated), &[&pin_clk]);
cfg.set_out_pins(&[&pin_io]);
cfg.set_in_pins(&[&pin_io]);
cfg.set_set_pins(&[&pin_io]);
@ -140,7 +142,7 @@ where
sm,
irq,
dma: dma.into_ref(),
wrap_target: loaded_program.wrap.target,
wrap_target: relocated.wrap().target,
}
}

View File

@ -11,7 +11,7 @@ log = ["dep:log"]
firmware-logs = []
[dependencies]
embassy-time = { version = "0.1.2", path = "../embassy-time"}
embassy-time = { version = "0.1.0", path = "../embassy-time"}
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
@ -24,7 +24,7 @@ cortex-m = "0.7.6"
cortex-m-rt = "0.7.0"
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" }
num_enum = { version = "0.5.7", default-features = false }
[package.metadata.embassy_docs]

View File

@ -1,6 +1,6 @@
# cyw43
Rust driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
## Current status
@ -19,18 +19,18 @@ Working:
TODO:
- Setting a custom MAC address.
- Bus sleep (for power consumption optimization)
- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?)
## Running the examples
- `cargo install probe-rs --features cli`
- `cd examples/rp`
- `cargo install probe-rs-cli`
- `cd examples/rpi-pico-w`
### Example 1: Scan the wifi stations
- `cargo run --release --bin wifi_scan`
### Example 2: Create an access point (IP and credentials in the code)
- `cargo run --release --bin wifi_ap_tcp_server`
- `cargo run --release --bin tcp_server_ap`
### Example 3: Connect to an existing network and create a server
- `cargo run --release --bin wifi_tcp_server`
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release`
After a few seconds, you should see that DHCP picks up an IP address like this
```

View File

@ -216,7 +216,7 @@ where
PWR: OutputPin,
SPI: SpiBusCyw43,
{
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
let state_ch = ch_runner.state_runner();
let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);

View File

@ -345,9 +345,7 @@ where
}
fn rx(&mut self, packet: &mut [u8]) {
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else {
return;
};
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return };
self.update_credit(&sdpcm_header);
@ -355,9 +353,7 @@ where
match channel {
CHANNEL_TYPE_CONTROL => {
let Some((cdc_header, response)) = CdcHeader::parse(payload) else {
return;
};
let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; };
trace!(" {:?}", cdc_header);
if cdc_header.id == self.ioctl_id {
@ -421,12 +417,8 @@ where
let status = event_packet.msg.status;
let event_payload = match evt_type {
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
return;
};
let Some(bss_info) = BssInfo::parse(bss_info) else {
return;
};
let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return };
let Some(bss_info) = BssInfo::parse(bss_info) else { return };
events::Payload::BssInfo(*bss_info)
}
Event::ESCAN_RESULT => events::Payload::None,
@ -447,9 +439,7 @@ where
}
}
CHANNEL_TYPE_DATA => {
let Some((_, packet)) = BdcHeader::parse(payload) else {
return;
};
let Some((_, packet)) = BdcHeader::parse(payload) else { return };
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
match self.ch.try_rx_buf() {

View File

@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
/// 'mess up' the internal bootloader state
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
dfu: DFU,
state: FirmwareState<'d, STATE>,
state: STATE,
}
#[cfg(target_os = "none")]
@ -47,12 +47,22 @@ impl<'a, FLASH: NorFlash>
}
}
impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
/// Create a firmware updater instance with partition ranges for the update and state partitions.
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
Self {
dfu: config.dfu,
state: FirmwareState::new(config.state, aligned),
state: config.state,
}
}
// Make sure we are running a booted firmware to avoid reverting to a bad state.
async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
if self.get_state(aligned).await? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
@ -61,8 +71,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.get_state().await
pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, aligned).await?;
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
}
/// Verify the DFU given a public key. If there is an error then DO NOT
@ -76,16 +92,23 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
///
/// If no signature feature is set then this method will always return a
/// signature error.
///
/// # Safety
///
/// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
#[cfg(feature = "_verify")]
pub async fn verify_and_mark_updated(
&mut self,
_public_key: &[u8],
_signature: &[u8],
_update_len: u32,
_aligned: &mut [u8],
) -> Result<(), FirmwareUpdaterError> {
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
assert!(_update_len <= self.dfu.capacity() as u32);
self.state.verify_booted().await?;
self.verify_booted(_aligned).await?;
#[cfg(feature = "ed25519-dalek")]
{
@ -98,9 +121,8 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
let mut chunk_buf = [0; 2];
let mut message = [0; 64];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;
self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
public_key.verify(&message, &signature).map_err(into_signature_error)?
}
@ -121,8 +143,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
let mut message = [0; 64];
let mut chunk_buf = [0; 2];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;
self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
let r = public_key.verify(&message, &signature);
trace!(
@ -135,7 +156,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
r.map_err(into_signature_error)?
}
self.state.mark_updated().await
self.set_magic(_aligned, SWAP_MAGIC).await
}
/// Verify the update in DFU with any digest.
@ -156,14 +177,49 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
}
/// Mark to trigger firmware swap on next boot.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
#[cfg(not(feature = "_verify"))]
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_updated().await
pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, SWAP_MAGIC).await
}
/// Mark firmware boot successful and stop rollback on reset.
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_booted().await
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, BOOT_MAGIC).await
}
async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, aligned).await?;
if aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, aligned).await?;
if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, aligned).await?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32).await?;
// Set magic
aligned.fill(magic);
self.state.write(0, aligned).await?;
}
Ok(())
}
/// Write data to a flash page.
@ -173,10 +229,16 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
/// # Safety
///
/// Failing to meet alignment and size requirements may result in a panic.
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
pub async fn write_firmware(
&mut self,
aligned: &mut [u8],
offset: usize,
data: &[u8],
) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= DFU::ERASE_SIZE);
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.state.verify_booted().await?;
self.verify_booted(aligned).await?;
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
@ -190,91 +252,17 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
///
/// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity.
pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
self.state.verify_booted().await?;
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
Ok(&mut self.dfu)
}
}
/// Manages the state partition of the firmware update.
///
/// Can be used standalone for more fine grained control, or as part of the updater.
pub struct FirmwareState<'d, STATE> {
state: STATE,
aligned: &'d mut [u8],
}
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
/// Create a firmware state instance with a buffer for magic content and state partition.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
Self { state, aligned }
}
self.verify_booted(aligned).await?;
// Make sure we are running a booted firmware to avoid reverting to a bad state.
async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
if self.get_state().await? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
/// Obtain the current state.
///
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned).await?;
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
}
/// Mark to trigger firmware swap on next boot.
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(SWAP_MAGIC).await
}
/// Mark firmware boot successful and stop rollback on reset.
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(BOOT_MAGIC).await
}
async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned).await?;
if self.aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
self.aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32).await?;
// Set magic
self.aligned.fill(magic);
self.state.write(0, &self.aligned).await?;
}
Ok(())
Ok(&mut self.dfu)
}
}
@ -300,8 +288,8 @@ mod tests {
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap();
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();

View File

@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
/// 'mess up' the internal bootloader state
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
dfu: DFU,
state: BlockingFirmwareState<'d, STATE>,
state: STATE,
}
#[cfg(target_os = "none")]
@ -49,17 +49,22 @@ impl<'a, FLASH: NorFlash>
}
}
impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> {
impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
/// Create a firmware updater instance with partition ranges for the update and state partitions.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
Self {
dfu: config.dfu,
state: BlockingFirmwareState::new(config.state, aligned),
state: config.state,
}
}
// Make sure we are running a booted firmware to avoid reverting to a bad state.
fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
if self.get_state(aligned)? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
@ -68,8 +73,14 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.get_state()
pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, aligned)?;
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
}
/// Verify the DFU given a public key. If there is an error then DO NOT
@ -83,16 +94,23 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
///
/// If no signature feature is set then this method will always return a
/// signature error.
///
/// # Safety
///
/// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
#[cfg(feature = "_verify")]
pub fn verify_and_mark_updated(
&mut self,
_public_key: &[u8],
_signature: &[u8],
_update_len: u32,
_aligned: &mut [u8],
) -> Result<(), FirmwareUpdaterError> {
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
assert!(_update_len <= self.dfu.capacity() as u32);
self.state.verify_booted()?;
self.verify_booted(_aligned)?;
#[cfg(feature = "ed25519-dalek")]
{
@ -106,8 +124,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
let mut message = [0; 64];
let mut chunk_buf = [0; 2];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;
self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
public_key.verify(&message, &signature).map_err(into_signature_error)?
}
@ -128,8 +145,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
let mut message = [0; 64];
let mut chunk_buf = [0; 2];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;
self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
let r = public_key.verify(&message, &signature);
trace!(
@ -142,7 +158,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
r.map_err(into_signature_error)?
}
self.state.mark_updated()
self.set_magic(_aligned, SWAP_MAGIC)
}
/// Verify the update in DFU with any digest.
@ -163,14 +179,49 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
}
/// Mark to trigger firmware swap on next boot.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
#[cfg(not(feature = "_verify"))]
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_updated()
pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, SWAP_MAGIC)
}
/// Mark firmware boot successful and stop rollback on reset.
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_booted()
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, BOOT_MAGIC)
}
fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, aligned)?;
if aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, aligned)?;
if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, aligned)?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32)?;
// Set magic
aligned.fill(magic);
self.state.write(0, aligned)?;
}
Ok(())
}
/// Write data to a flash page.
@ -180,9 +231,15 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
/// # Safety
///
/// Failing to meet alignment and size requirements may result in a panic.
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
pub fn write_firmware(
&mut self,
aligned: &mut [u8],
offset: usize,
data: &[u8],
) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= DFU::ERASE_SIZE);
self.state.verify_booted()?;
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned)?;
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
@ -196,91 +253,16 @@ impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE>
///
/// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity.
pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
self.state.verify_booted()?;
self.dfu.erase(0, self.dfu.capacity() as u32)?;
Ok(&mut self.dfu)
}
}
/// Manages the state partition of the firmware update.
///
/// Can be used standalone for more fine grained control, or as part of the updater.
pub struct BlockingFirmwareState<'d, STATE> {
state: STATE,
aligned: &'d mut [u8],
}
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
/// Create a firmware state instance with a buffer for magic content and state partition.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
Self { state, aligned }
}
self.verify_booted(aligned)?;
self.dfu.erase(0, self.dfu.capacity() as u32)?;
// Make sure we are running a booted firmware to avoid reverting to a bad state.
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
if self.get_state()? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
/// Obtain the current state.
///
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned)?;
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
}
/// Mark to trigger firmware swap on next boot.
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(SWAP_MAGIC)
}
/// Mark firmware boot successful and stop rollback on reset.
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(BOOT_MAGIC)
}
fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned)?;
if self.aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?;
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
self.aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32)?;
// Set magic
self.aligned.fill(magic);
self.state.write(0, &self.aligned)?;
}
Ok(())
Ok(&mut self.dfu)
}
}
@ -301,14 +283,14 @@ mod tests {
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
let state = BlockingPartition::new(&flash, 0, 4096);
let dfu = BlockingPartition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
updater.write_firmware(0, to_write.as_slice()).unwrap();
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
let mut aligned = [0; 8];
updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap();
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
updater

View File

@ -3,8 +3,8 @@ mod asynch;
mod blocking;
#[cfg(feature = "nightly")]
pub use asynch::{FirmwareState, FirmwareUpdater};
pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};
pub use asynch::FirmwareUpdater;
pub use blocking::BlockingFirmwareUpdater;
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
/// Firmware updater flash configuration holding the two flashes used by the updater

View File

@ -16,11 +16,9 @@ mod test_flash;
// TODO: Use the value provided by NorFlash when available
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
pub use firmware_updater::{
BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError,
};
#[cfg(feature = "nightly")]
pub use firmware_updater::{FirmwareState, FirmwareUpdater};
pub use firmware_updater::FirmwareUpdater;
pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
@ -120,18 +118,15 @@ mod tests {
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
block_on(updater.mark_updated()).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap();
// Writing after marking updated is not allowed until marked as booted.
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE));
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE));
assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
let flash = flash.into_blocking();
@ -163,14 +158,11 @@ mod tests {
// Mark as booted
let flash = flash.into_async();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.mark_booted()).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.mark_booted(&mut aligned)).unwrap();
let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig {
@ -198,15 +190,12 @@ mod tests {
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
block_on(updater.mark_updated()).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap();
let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig {
@ -243,15 +232,12 @@ mod tests {
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
block_on(updater.mark_updated()).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap();
let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig {
@ -307,19 +293,18 @@ mod tests {
// On with the test
let flash = flash.into_async();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
let mut aligned = [0; 4];
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
assert!(block_on(updater.verify_and_mark_updated(
&public_key.to_bytes(),
&signature.to_bytes(),
firmware_len as u32,
&mut aligned,
))
.is_ok());
}

View File

@ -6,7 +6,7 @@ An adaptation of `embassy-boot` for nRF.
## Features
* Load applications with or without the softdevice.
* Load applications with our without the softdevice.
* Configure bootloader partitions based on linker script.
* Using watchdog timer to detect application failure.

View File

@ -3,12 +3,10 @@
#![doc = include_str!("../README.md")]
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig,
};
#[cfg(feature = "nightly")]
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embassy_nrf::nvmc::PAGE_SIZE;
pub use embassy_boot::FirmwareUpdater;
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig};
use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
use embassy_nrf::peripherals::WDT;
use embassy_nrf::wdt;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
@ -106,15 +104,15 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
}
}
/// A flash implementation that wraps any flash and will pet a watchdog when touching flash.
pub struct WatchdogFlash<FLASH> {
flash: FLASH,
/// A flash implementation that wraps NVMC and will pet a watchdog when touching flash.
pub struct WatchdogFlash<'d> {
flash: Nvmc<'d>,
wdt: wdt::WatchdogHandle,
}
impl<FLASH> WatchdogFlash<FLASH> {
impl<'d> WatchdogFlash<'d> {
/// Start a new watchdog with a given flash and WDT peripheral and a timeout
pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self {
pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self {
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
Ok(x) => x,
Err(_) => {
@ -129,13 +127,13 @@ impl<FLASH> WatchdogFlash<FLASH> {
}
}
impl<FLASH: ErrorType> ErrorType for WatchdogFlash<FLASH> {
type Error = FLASH::Error;
impl<'d> ErrorType for WatchdogFlash<'d> {
type Error = <Nvmc<'d> as ErrorType>::Error;
}
impl<FLASH: NorFlash> NorFlash for WatchdogFlash<FLASH> {
const WRITE_SIZE: usize = FLASH::WRITE_SIZE;
const ERASE_SIZE: usize = FLASH::ERASE_SIZE;
impl<'d> NorFlash for WatchdogFlash<'d> {
const WRITE_SIZE: usize = <Nvmc<'d> as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Nvmc<'d> as NorFlash>::ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.wdt.pet();
@ -147,8 +145,8 @@ impl<FLASH: NorFlash> NorFlash for WatchdogFlash<FLASH> {
}
}
impl<FLASH: ReadNorFlash> ReadNorFlash for WatchdogFlash<FLASH> {
const READ_SIZE: usize = FLASH::READ_SIZE;
impl<'d> ReadNorFlash for WatchdogFlash<'d> {
const READ_SIZE: usize = <Nvmc<'d> as ReadNorFlash>::READ_SIZE;
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.wdt.pet();
self.flash.read(offset, data)

View File

@ -3,12 +3,10 @@
#![doc = include_str!("../README.md")]
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
};
#[cfg(feature = "nightly")]
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
pub use embassy_boot::FirmwareUpdater;
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
use embassy_rp::flash::{Flash, ERASE_SIZE};
use embassy_rp::peripherals::{FLASH, WATCHDOG};
use embassy_rp::watchdog::Watchdog;
use embassy_time::Duration;
@ -60,14 +58,14 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// A flash implementation that will feed a watchdog when touching flash.
pub struct WatchdogFlash<'d, const SIZE: usize> {
flash: Flash<'d, FLASH, Blocking, SIZE>,
flash: Flash<'d, FLASH, SIZE>,
watchdog: Watchdog,
}
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
let flash = Flash::<_, Blocking, SIZE>::new(flash);
let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash);
let mut watchdog = Watchdog::new(watchdog);
watchdog.start(timeout);
Self { flash, watchdog }
@ -75,12 +73,12 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
}
impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> {
type Error = <Flash<'d, FLASH, Blocking, SIZE> as ErrorType>::Error;
type Error = <Flash<'d, FLASH, SIZE> as ErrorType>::Error;
}
impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
const WRITE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::ERASE_SIZE;
const WRITE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.watchdog.feed();
@ -93,7 +91,7 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
}
impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {
const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE;
const READ_SIZE: usize = <Flash<'d, FLASH, SIZE> as ReadNorFlash>::READ_SIZE;
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.watchdog.feed();
self.flash.read(offset, data)

View File

@ -3,11 +3,9 @@
#![doc = include_str!("../README.md")]
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
};
#[cfg(feature = "nightly")]
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
pub use embassy_boot::FirmwareUpdater;
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
use embedded_storage::nor_flash::NorFlash;
/// A bootloader for STM32 devices.

View File

@ -15,18 +15,15 @@ target = "x86_64-unknown-linux-gnu"
std = []
# Enable nightly-only features
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
time = ["dep:embassy-time"]
default = ["time"]
[dependencies]
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
"unproven",
] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
nb = "1.0.0"

View File

@ -74,21 +74,7 @@ where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data)?;
Ok(())
}
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(data)?;
Ok(())
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
// Ensure we write the expected bytes
for i in 0..core::cmp::min(read.len(), write.len()) {
read[i] = write[i].clone();
@ -97,7 +83,38 @@ where
Ok(())
}
async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
todo!()
}
}
impl<T, E> embedded_hal_async::spi::SpiBusFlush for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<T, E> embedded_hal_async::spi::SpiBusWrite<u8> for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data)?;
Ok(())
}
}
impl<T, E> embedded_hal_async::spi::SpiBusRead<u8> for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(data)?;
Ok(())
}

View File

@ -69,39 +69,54 @@ where
type Error = T::Error;
}
impl<T, Word: 'static + Copy> embedded_hal_async::spi::SpiBus<Word> for YieldingAsync<T>
impl<T> embedded_hal_async::spi::SpiBus<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBus<Word>,
T: embedded_hal_async::spi::SpiBus,
{
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(read, write).await?;
yield_now().await;
Ok(())
}
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer_in_place(words).await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusFlush for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusFlush,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
self.wrapped.flush().await?;
yield_now().await;
Ok(())
}
}
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusWrite<u8>,
{
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data).await?;
yield_now().await;
Ok(())
}
}
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusRead<u8>,
{
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.read(data).await?;
yield_now().await;
Ok(())
}
async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.wrapped.transfer(read, write).await?;
yield_now().await;
Ok(())
}
async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.wrapped.transfer_in_place(words).await?;
yield_now().await;
Ok(())
}
}
///

View File

@ -56,6 +56,62 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusRead,
CS: OutputPin,
{
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.read(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusWrite,
CS: OutputPin,
{
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.write(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
@ -73,12 +129,6 @@ where
Operation::Write(buf) => bus.write(buf).await?,
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
}
}
}
};
@ -122,6 +172,64 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusWrite + SetConfig,
CS: OutputPin,
{
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.write(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusRead + SetConfig,
CS: OutputPin,
{
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.read(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
@ -140,12 +248,6 @@ where
Operation::Write(buf) => bus.write(buf).await?,
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
}
}
}
};

View File

@ -22,7 +22,7 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi::{self, Operation, SpiBus};
use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite};
use crate::shared_bus::SpiDeviceError;
use crate::SetConfig;
@ -48,6 +48,58 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusRead,
CS: OutputPin,
{
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusWrite,
CS: OutputPin,
{
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
@ -64,13 +116,6 @@ where
Operation::Write(buf) => bus.write(buf),
Operation::Transfer(read, write) => bus.transfer(read, write),
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
Ok(())
}
});
// On failure, it's important to still flush and deassert CS.
@ -154,6 +199,58 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusRead + SetConfig,
CS: OutputPin,
{
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusWrite + SetConfig,
CS: OutputPin,
{
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
@ -171,13 +268,6 @@ where
Operation::Write(buf) => bus.write(buf),
Operation::Transfer(read, write) => bus.transfer(read, write),
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
Ok(())
}
});
// On failure, it's important to still flush and deassert CS.

View File

@ -30,14 +30,11 @@ where
/// Error returned by SPI device implementations in this crate.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum SpiDeviceError<BUS, CS> {
/// An operation on the inner SPI bus failed.
Spi(BUS),
/// Setting the value of the Chip Select (CS) pin failed.
Cs(CS),
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
DelayUsNotSupported,
}
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
@ -49,7 +46,6 @@ where
match self {
Self::Spi(e) => e.kind(),
Self::Cs(_) => spi::ErrorKind::Other,
Self::DelayUsNotSupported => spi::ErrorKind::Other,
}
}
}

View File

@ -5,13 +5,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.2.1 - 2023-08-10
- Avoid calling `pend()` when waking expired timers
- Properly reset finished task state with `integrated-timers` enabled
- Introduce `InterruptExecutor::spawner()`
- Fix incorrect critical section in Xtensa executor
## 0.2.0 - 2023-04-27
- Replace unnecessary atomics in runqueue

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-executor"
version = "0.2.1"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage"
@ -61,8 +61,8 @@ log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true }
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true}
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
atomic-polyfill = "1.0.1"
critical-section = "1.1"
static_cell = "1.1"

View File

@ -165,9 +165,6 @@ impl<F: Future + 'static> TaskStorage<F> {
Poll::Ready(_) => {
this.future.drop_in_place();
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
#[cfg(feature = "integrated-timers")]
this.raw.expires_at.set(Instant::MAX);
}
Poll::Pending => {}
}
@ -409,7 +406,7 @@ impl SyncExecutor {
#[allow(clippy::never_loop)]
loop {
#[cfg(feature = "integrated-timers")]
self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend);
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
self.run_queue.dequeue_all(|p| {
let task = p.header();
@ -573,31 +570,6 @@ pub fn wake_task(task: TaskRef) {
}
}
/// Wake a task by `TaskRef` without calling pend.
///
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
pub fn wake_task_no_pend(task: TaskRef) {
let header = task.header();
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
// If already scheduled, or if not started,
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
None
} else {
// Mark it as scheduled
Some(state | STATE_RUN_QUEUED)
}
});
if res.is_ok() {
// We have just marked the task as scheduled, so enqueue it.
unsafe {
let executor = header.executor.get().unwrap_unchecked();
executor.run_queue.enqueue(task);
}
}
}
#[cfg(feature = "integrated-timers")]
struct TimerQueue;

View File

@ -1,5 +1,5 @@
[package]
name = "embassy-hal-internal"
name = "embassy-hal-common"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"

View File

@ -1,6 +1,5 @@
#![no_std]
#![allow(clippy::new_without_default)]
#![doc = include_str!("../README.md")]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;

View File

@ -116,7 +116,6 @@ macro_rules! impl_peripheral {
#[inline]
unsafe fn clone_unchecked(&self) -> Self::P {
#[allow(clippy::needless_update)]
$type { ..*self }
}
}

View File

@ -161,7 +161,7 @@ pub trait Peripheral: Sized {
}
}
impl<'b, T: DerefMut> Peripheral for T
impl<'b, T: Deref> Peripheral for T
where
T::Target: Peripheral,
{

View File

@ -1,16 +0,0 @@
# embassy-macros
An [Embassy](https://embassy.dev) project.
Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport
everything you need to use them effectively.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

View File

@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi"
[features]
stm32wl = ["dep:embassy-stm32"]
time = ["embassy-time", "lorawan-device"]
time = []
defmt = ["dep:defmt", "lorawan-device/defmt"]
[dependencies]
@ -20,12 +20,15 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embedded-hal = { version = "0.2", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
embedded-hal = { version = "0.2", features = ["unproven"] }
bit_field = { version = "0.10" }
lora-phy = { version = "1" }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }

View File

@ -2,14 +2,6 @@
name = "embassy-net-driver-channel"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
@ -17,9 +9,6 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
features = ["defmt"]
target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["defmt"]
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }

View File

@ -1,96 +0,0 @@
# embassy-net-driver-channel
This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a
higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.
The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
knows when to poll your driver again to make more progress.
With `embassy-net-driver-channel`
## A note about deadlocks
When implementing a driver using this crate, it might be tempting to write it in the most straightforward way:
```rust,ignore
loop {
// Wait for either..
match select(
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
irq_pin.wait_for_low(),
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
tx_chan.tx_buf(),
).await {
Either::First(_) => {
// a packet is ready to be received!
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
let n = receive_packet_over_spi(buf).await;
rx_chan.rx_done(n);
}
Either::Second(buf) => {
// a packet is ready to be sent!
send_packet_over_spi(buf).await;
tx_chan.tx_done();
}
}
}
```
However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.
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.
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":
```rust,ignore
loop {
// Wait for either..
match select(
async {
// ... the chip signaling an interrupt, indicating a packet is available to receive
irq_pin.wait_for_low().await;
// *AND* the buffer is ready...
rx_chan.rx_buf().await
},
// ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet
tx_chan.tx_buf(),
).await {
Either::First(buf) => {
// a packet is ready to be received!
let n = receive_packet_over_spi(buf).await;
rx_chan.rx_done(n);
}
Either::Second(buf) => {
// a packet is ready to be sent!
send_packet_over_spi(buf).await;
tx_chan.tx_done();
}
}
}
```
## Examples
These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
- [`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.
## Interoperability
This crate can run on any executor.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -1,5 +1,4 @@
#![no_std]
#![doc = include_str!("../README.md")]
// must go first!
mod fmt;
@ -42,7 +41,7 @@ struct StateInner<'d, const MTU: usize> {
struct Shared {
link_state: LinkState,
waker: WakerRegistration,
hardware_address: driver::HardwareAddress,
ethernet_address: [u8; 6],
}
pub struct Runner<'d, const MTU: usize> {
@ -85,10 +84,10 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
});
}
pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) {
pub fn set_ethernet_address(&mut self, address: [u8; 6]) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.hardware_address = address;
s.ethernet_address = address;
s.waker.wake();
});
}
@ -150,15 +149,7 @@ impl<'d> StateRunner<'d> {
pub fn set_ethernet_address(&self, address: [u8; 6]) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.hardware_address = driver::HardwareAddress::Ethernet(address);
s.waker.wake();
});
}
pub fn set_ieee802154_address(&self, address: [u8; 8]) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.hardware_address = driver::HardwareAddress::Ieee802154(address);
s.ethernet_address = address;
s.waker.wake();
});
}
@ -214,7 +205,7 @@ impl<'d, const MTU: usize> TxRunner<'d, MTU> {
pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
state: &'d mut State<MTU, N_RX, N_TX>,
hardware_address: driver::HardwareAddress,
ethernet_address: [u8; 6],
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
@ -230,7 +221,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
shared: Mutex::new(RefCell::new(Shared {
link_state: LinkState::Down,
hardware_address,
ethernet_address,
waker: WakerRegistration::new(),
})),
});
@ -297,8 +288,8 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
self.caps.clone()
}
fn hardware_address(&self) -> driver::HardwareAddress {
self.shared.lock(|s| s.borrow().hardware_address)
fn ethernet_address(&self) -> [u8; 6] {
self.shared.lock(|s| s.borrow().ethernet_address)
}
fn link_state(&mut self, cx: &mut Context) -> LinkState {

View File

@ -3,13 +3,7 @@ name = "embassy-net-driver"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
@ -17,8 +11,5 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
features = ["defmt"]
target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["defmt"]
[dependencies]
defmt = { version = "0.3", optional = true }

View File

@ -1,21 +1,5 @@
# embassy-net-driver
This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support
for a new hardware platform.
If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.
If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate.
This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,
if the driver trait has not had breaking changes.
See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
packet queues for RX and TX.
## Interoperability
This crate can run on any executor.
## License

View File

@ -4,18 +4,6 @@
use core::task::Context;
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HardwareAddress {
/// A six-octet Ethernet address
Ethernet([u8; 6]),
/// An eight-octet IEEE802.15.4 address
Ieee802154([u8; 8]),
/// Indicates that a Driver is IP-native, and has no hardware address
Ip,
}
/// Main `embassy-net` driver API.
///
/// This is essentially an interface for sending and receiving raw network frames.
@ -63,8 +51,8 @@ pub trait Driver {
/// Get a description of device capabilities.
fn capabilities(&self) -> Capabilities;
/// Get the device's hardware address.
fn hardware_address(&self) -> HardwareAddress;
/// Get the device's Ethernet address.
fn ethernet_address(&self) -> [u8; 6];
}
impl<T: ?Sized + Driver> Driver for &mut T {
@ -87,8 +75,8 @@ impl<T: ?Sized + Driver> Driver for &mut T {
fn link_state(&mut self, cx: &mut Context) -> LinkState {
T::link_state(self, cx)
}
fn hardware_address(&self) -> HardwareAddress {
T::hardware_address(self)
fn ethernet_address(&self) -> [u8; 6] {
T::ethernet_address(self)
}
}
@ -176,9 +164,6 @@ pub enum Medium {
///
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
Ip,
/// IEEE 802_15_4 medium
Ieee802154,
}
impl Default for Medium {

View File

@ -7,20 +7,14 @@ edition = "2021"
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
target = "thumbv7em-none-eabi"
features = ["defmt"]

View File

@ -1,4 +1,5 @@
use ch::driver::LinkState;
use defmt::Debug2Format;
use embassy_net_driver_channel as ch;
use heapless::String;
@ -53,9 +54,8 @@ impl<'a> Control<'a> {
})),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
debug!("======= {:?}", Debug2Format(&resp));
assert_eq!(resp.resp, 0);
self.state_ch.set_link_state(LinkState::Up);
}
@ -71,9 +71,7 @@ impl<'a> Control<'a> {
)),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
assert_eq!(resp.resp, 0);
// WHY IS THIS A STRING? WHYYYY
@ -102,9 +100,7 @@ impl<'a> Control<'a> {
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
assert_eq!(resp.resp, 0);
}

View File

@ -124,7 +124,7 @@ where
IN: InputPin + Wait,
OUT: OutputPin,
{
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
let state_ch = ch_runner.state_runner();
let mut runner = Runner {
@ -311,14 +311,14 @@ where
fn handle_event(&self, data: &[u8]) {
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
warn!("failed to parse event");
return;
return
};
debug!("event: {:?}", &event);
let Some(payload) = &event.payload else {
warn!("event without payload?");
return;
return
};
match payload {

View File

@ -1,19 +0,0 @@
[package]
name = "embassy-net-tuntap"
version = "0.1.0"
description = "embassy-net driver for Linux TUN/TAP interfaces."
keywords = ["embedded", "tuntap", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
async-io = "1.6.0"
log = "0.4.14"
libc = "0.2.101"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/"
target = "thumbv7em-none-eabi"

View File

@ -1,17 +0,0 @@
# `embassy-net` integration for Linux TUN/TAP interfaces.
[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces.
## Interoperability
This crate can run on any executor.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embassy-time = { version = "0.1.0" }
embassy-futures = { version = "0.1.0" }
defmt = { version = "0.3", optional = true }
[package.metadata.embassy_docs]

View File

@ -1,6 +1,5 @@
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
#![no_std]
/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
mod device;
mod socket;
mod spi;
@ -78,7 +77,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
}
}
/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net).
/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net).
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
mac_addr: [u8; 6],
state: &'a mut State<N_RX, N_TX>,
@ -96,7 +95,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT:
let mac = W5500::new(spi_dev, mac_addr).await.unwrap();
let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));
let (runner, device) = ch::new(&mut state.ch_state, mac_addr);
(
device,
Runner {

View File

@ -22,11 +22,7 @@ impl<SPI: SpiDevice> SpiInterface<SPI> {
let address_phase = address.to_be_bytes();
let control_phase = [(block as u8) << 3 | 0b0000_0100];
let data_phase = data;
let operations = &mut [
Operation::Write(&address_phase[..]),
Operation::Write(&control_phase),
Operation::Write(&data_phase),
];
self.0.transaction(operations).await
let operations = &[&address_phase[..], &control_phase, &data_phase];
self.0.write_transaction(operations).await
}
}

View File

@ -3,22 +3,16 @@ name = "embassy-net"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Async TCP/IP network stack for embedded systems"
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"]
target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"]
[features]
default = []
@ -26,7 +20,8 @@ std = []
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"]
nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"]
unstable-traits = []
udp = ["smoltcp/socket-udp"]
tcp = ["smoltcp/socket-tcp"]
@ -36,7 +31,6 @@ proto-ipv4 = ["smoltcp/proto-ipv4"]
proto-ipv6 = ["smoltcp/proto-ipv6"]
medium-ethernet = ["smoltcp/medium-ethernet"]
medium-ip = ["smoltcp/medium-ip"]
medium-ieee802154 = ["smoltcp/medium-ieee802154"]
igmp = ["smoltcp/proto-igmp"]
[dependencies]
@ -50,9 +44,10 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
] }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embedded-io-async = { version = "0.5.0", optional = true }
embedded-io = { version = "0.4.0", optional = true }
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
heapless = { version = "0.7.5", default-features = false }
@ -61,5 +56,5 @@ generic-array = { version = "0.14.4", default-features = false }
stable_deref_trait = { version = "1.2.0", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
atomic-pool = "1.0"
embedded-nal-async = { version = "0.5.0", optional = true }
embedded-nal-async = { version = "0.4.0", optional = true }
atomic-polyfill = { version = "1.0" }

View File

@ -1,56 +1,30 @@
# embassy-net
`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.
embassy-net contains an async network API based on smoltcp and embassy, designed
for embedded systems.
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
## Running the example
## Features
First, create the tap0 interface. You only need to do this once.
- IPv4, IPv6
- Ethernet and bare-IP mediums.
- TCP, UDP, DNS, DHCPv4, IGMPv4
- TCP sockets implement the `embedded-io` async traits.
```sh
sudo ip tuntap add name tap0 mode tap user $USER
sudo ip link set tap0 up
sudo ip addr add 192.168.69.100/24 dev tap0
sudo ip -6 addr add fe80::100/64 dev tap0
sudo ip -6 addr add fdaa::100/64 dev tap0
sudo ip -6 route add fe80::/64 dev tap0
sudo ip -6 route add fdaa::/64 dev tap0
```
See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and
unimplemented features of the network protocols.
Second, have something listening there. For example `nc -l 8000`
## Hardware support
Then run the example located in the `examples` folder:
- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
- [`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).
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
- [`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.
## Examples
- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.
- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.
- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)
## Adding support for new hardware
To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or
an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)
traits.
Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
packet queues for RX and TX.
Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.
This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver
trait has not had breaking changes.
## Interoperability
This crate can run on any executor.
[`embassy-time`](https://crates.io/crates/embassy-time) is used for timekeeping and timeouts. You must
link an `embassy-time` driver in your project to use this crate.
```sh
cd $EMBASSY_ROOT/examples/std/
cargo run --bin net -- --static-ip
```
## License

View File

@ -51,8 +51,6 @@ where
Medium::Ethernet => phy::Medium::Ethernet,
#[cfg(feature = "medium-ip")]
Medium::Ip => phy::Medium::Ip,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => phy::Medium::Ieee802154,
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",

View File

@ -68,7 +68,7 @@ where
}
}
#[cfg(feature = "nightly")]
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D>
where
D: Driver + 'static,

View File

@ -24,23 +24,17 @@ use embassy_net_driver::{Driver, LinkState, Medium};
use embassy_sync::waitqueue::WakerRegistration;
use embassy_time::{Instant, Timer};
use futures::pin_mut;
#[allow(unused_imports)]
use heapless::Vec;
#[cfg(feature = "igmp")]
pub use smoltcp::iface::MulticastError;
#[allow(unused_imports)]
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
#[cfg(feature = "dhcpv4")]
use smoltcp::socket::dhcpv4::{self, RetryConfig};
#[cfg(feature = "medium-ethernet")]
pub use smoltcp::wire::EthernetAddress;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
pub use smoltcp::wire::HardwareAddress;
#[cfg(feature = "udp")]
pub use smoltcp::wire::IpListenEndpoint;
#[cfg(feature = "medium-ieee802154")]
pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};
#[cfg(feature = "medium-ethernet")]
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
pub use smoltcp::wire::{IpAddress, IpCidr};
#[cfg(feature = "proto-ipv4")]
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")]
@ -63,7 +57,7 @@ pub struct StackResources<const SOCK: usize> {
impl<const SOCK: usize> StackResources<SOCK> {
/// Create a new set of stack resources.
pub const fn new() -> Self {
pub fn new() -> Self {
#[cfg(feature = "dns")]
const INIT: Option<dns::DnsQuery> = None;
Self {
@ -230,20 +224,6 @@ pub(crate) struct SocketStack {
next_local_port: u16,
}
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress {
match addr {
#[cfg(feature = "medium-ethernet")]
driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)),
#[cfg(feature = "medium-ieee802154")]
driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
#[cfg(feature = "medium-ip")]
driver::HardwareAddress::Ip => HardwareAddress::Ip,
#[allow(unreachable_patterns)]
_ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr),
}
}
impl<D: Driver + 'static> Stack<D> {
/// Create a new network stack.
pub fn new<const SOCK: usize>(
@ -252,7 +232,21 @@ impl<D: Driver + 'static> Stack<D> {
resources: &'static mut StackResources<SOCK>,
random_seed: u64,
) -> Self {
let mut iface_cfg = smoltcp::iface::Config::new(to_smoltcp_hardware_address(device.hardware_address()));
#[cfg(feature = "medium-ethernet")]
let medium = device.capabilities().medium;
let hardware_addr = match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
#[cfg(feature = "medium-ip")]
Medium::Ip => HardwareAddress::Ip,
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
medium
),
};
let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr);
iface_cfg.random_seed = random_seed;
let iface = Interface::new(
@ -268,7 +262,6 @@ impl<D: Driver + 'static> Stack<D> {
let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
let mut socket = SocketStack {
sockets,
iface,
@ -276,7 +269,6 @@ impl<D: Driver + 'static> Stack<D> {
next_local_port,
};
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
let mut inner = Inner {
device,
link_up: false,
@ -295,9 +287,6 @@ impl<D: Driver + 'static> Stack<D> {
dns_waker: WakerRegistration::new(),
};
#[cfg(feature = "medium-ieee802154")]
let _ = config;
#[cfg(feature = "proto-ipv4")]
match config.ipv4 {
ConfigV4::Static(config) => {
@ -334,9 +323,9 @@ impl<D: Driver + 'static> Stack<D> {
f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut())
}
/// Get the hardware address of the network interface.
pub fn hardware_address(&self) -> HardwareAddress {
self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()))
/// Get the MAC address of the network interface.
pub fn ethernet_address(&self) -> [u8; 6] {
self.with(|_s, i| i.device.ethernet_address())
}
/// Get whether the link is up.
@ -430,29 +419,7 @@ impl<D: Driver + 'static> Stack<D> {
})
.await?;
#[must_use = "to delay the drop handler invocation to the end of the scope"]
struct OnDrop<F: FnOnce()> {
f: core::mem::MaybeUninit<F>,
}
impl<F: FnOnce()> OnDrop<F> {
fn new(f: F) -> Self {
Self {
f: core::mem::MaybeUninit::new(f),
}
}
fn defuse(self) {
core::mem::forget(self)
}
}
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
unsafe { self.f.as_ptr().read()() }
}
}
use embassy_hal_common::drop::OnDrop;
let drop = OnDrop::new(|| {
self.with_mut(|s, i| {
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
@ -490,78 +457,30 @@ impl<D: Driver + 'static> Stack<D> {
}
#[cfg(feature = "igmp")]
impl<D: Driver + 'static> Stack<D> {
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
/// Join a multicast group.
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
let addr = addr.into();
poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await
}
/// Join a multicast group.
///
/// When the send queue is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the queue has space available.
pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
let addr = addr.into();
self.with_mut(|s, i| {
let mut smoldev = DriverAdapter {
cx: Some(cx),
inner: &mut i.device,
};
match s
.iface
.join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
{
Ok(announce_sent) => Poll::Ready(Ok(announce_sent)),
Err(MulticastError::Exhausted) => Poll::Pending,
Err(other) => Poll::Ready(Err(other)),
}
s.iface
.join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
})
}
/// Leave a multicast group.
pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
let addr = addr.into();
poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await
}
/// Leave a multicast group.
///
/// When the send queue is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the queue has space available.
pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
let addr = addr.into();
self.with_mut(|s, i| {
let mut smoldev = DriverAdapter {
cx: Some(cx),
inner: &mut i.device,
};
match s
.iface
.leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
{
Ok(leave_sent) => Poll::Ready(Ok(leave_sent)),
Err(MulticastError::Exhausted) => Poll::Pending,
Err(other) => Poll::Ready(Err(other)),
}
s.iface
.leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
})
}
@ -583,26 +502,22 @@ impl SocketStack {
impl<D: Driver + 'static> Inner<D> {
#[cfg(feature = "proto-ipv4")]
fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) {
#[cfg(feature = "medium-ethernet")]
let medium = self.device.capabilities().medium;
debug!("Acquired IP configuration:");
debug!(" IP address: {}", config.address);
s.iface.update_ip_addrs(|addrs| {
if let Some((index, _)) = addrs
.iter()
.enumerate()
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
{
addrs.remove(index);
if addrs.is_empty() {
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
} else {
addrs[0] = IpCidr::Ipv4(config.address);
}
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
});
#[cfg(feature = "medium-ip")]
let skip_gateway = self.device.capabilities().medium != Medium::Ip;
#[cfg(not(feature = "medium-ip"))]
let skip_gateway = false;
if !skip_gateway {
#[cfg(feature = "medium-ethernet")]
if medium == Medium::Ethernet {
if let Some(gateway) = config.gateway {
debug!(" Default gateway: {}", gateway);
s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
@ -633,14 +548,11 @@ impl<D: Driver + 'static> Inner<D> {
debug!(" IP address: {}", config.address);
s.iface.update_ip_addrs(|addrs| {
if let Some((index, _)) = addrs
.iter()
.enumerate()
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
{
addrs.remove(index);
if addrs.is_empty() {
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
} else {
addrs[0] = IpCidr::Ipv6(config.address);
}
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
});
#[cfg(feature = "medium-ethernet")]
@ -708,21 +620,13 @@ impl<D: Driver + 'static> Inner<D> {
socket.set_retry_config(config.retry_config);
}
#[cfg(feature = "dhcpv4")]
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
#[allow(unused)] // used only with dhcp
fn unapply_config(&mut self, s: &mut SocketStack) {
#[cfg(feature = "medium-ethernet")]
let medium = self.device.capabilities().medium;
debug!("Lost IP configuration");
s.iface.update_ip_addrs(|ip_addrs| {
#[cfg(feature = "proto-ipv4")]
if let Some((index, _)) = ip_addrs
.iter()
.enumerate()
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
{
ip_addrs.remove(index);
}
});
s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear());
#[cfg(feature = "medium-ethernet")]
if medium == Medium::Ethernet {
#[cfg(feature = "proto-ipv4")]
@ -739,12 +643,11 @@ impl<D: Driver + 'static> Inner<D> {
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
s.waker.register(cx.waker());
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
if self.device.capabilities().medium == Medium::Ethernet
|| self.device.capabilities().medium == Medium::Ieee802154
{
s.iface
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
#[cfg(feature = "medium-ethernet")]
if self.device.capabilities().medium == Medium::Ethernet {
s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress(
self.device.ethernet_address(),
)));
}
let timestamp = instant_to_smoltcp(Instant::now());
@ -770,7 +673,7 @@ impl<D: Driver + 'static> Inner<D> {
if self.link_up {
match socket.poll() {
None => {}
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
Some(dhcpv4::Event::Configured(config)) => {
let config = StaticConfigV4 {
address: config.address,
@ -782,7 +685,7 @@ impl<D: Driver + 'static> Inner<D> {
}
} else if old_link_up {
socket.reset();
self.unapply_config_v4(s);
self.unapply_config(s);
}
}
//if old_link_up || self.link_up {

View File

@ -382,29 +382,29 @@ impl<'d> TcpIo<'d> {
mod embedded_io_impls {
use super::*;
impl embedded_io_async::Error for ConnectError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
impl embedded_io::Error for ConnectError {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl embedded_io_async::Error for Error {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
embedded_io::ErrorKind::Other
}
}
impl<'d> embedded_io_async::ErrorType for TcpSocket<'d> {
impl<'d> embedded_io::Io for TcpSocket<'d> {
type Error = Error;
}
impl<'d> embedded_io_async::Read for TcpSocket<'d> {
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.io.read(buf).await
}
}
impl<'d> embedded_io_async::Write for TcpSocket<'d> {
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.io.write(buf).await
}
@ -414,21 +414,21 @@ mod embedded_io_impls {
}
}
impl<'d> embedded_io_async::ErrorType for TcpReader<'d> {
impl<'d> embedded_io::Io for TcpReader<'d> {
type Error = Error;
}
impl<'d> embedded_io_async::Read for TcpReader<'d> {
impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.io.read(buf).await
}
}
impl<'d> embedded_io_async::ErrorType for TcpWriter<'d> {
impl<'d> embedded_io::Io for TcpWriter<'d> {
type Error = Error;
}
impl<'d> embedded_io_async::Write for TcpWriter<'d> {
impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.io.write(buf).await
}
@ -440,7 +440,7 @@ mod embedded_io_impls {
}
/// TCP client compatible with `embedded-nal-async` traits.
#[cfg(feature = "nightly")]
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
pub mod client {
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
@ -527,13 +527,13 @@ pub mod client {
}
}
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::ErrorType
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::Io
for TcpConnection<'d, N, TX_SZ, RX_SZ>
{
type Error = Error;
}
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Read
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read
for TcpConnection<'d, N, TX_SZ, RX_SZ>
{
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
@ -541,7 +541,7 @@ pub mod client {
}
}
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Write
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write
for TcpConnection<'d, N, TX_SZ, RX_SZ>
{
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {

View File

@ -3,7 +3,7 @@
use core::cell::RefCell;
use core::future::poll_fn;
use core::mem;
use core::task::{Context, Poll};
use core::task::Poll;
use embassy_net_driver::Driver;
use smoltcp::iface::{Interface, SocketHandle};
@ -102,61 +102,37 @@ impl<'a> UdpSocket<'a> {
///
/// Returns the number of bytes received and the remote endpoint.
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
poll_fn(move |cx| self.poll_recv_from(buf, cx)).await
}
/// Receive a datagram.
///
/// When no datagram is available, this method will return `Poll::Pending` and
/// register the current task to be notified when a datagram is received.
///
/// When a datagram is received, this method will return `Poll::Ready` with the
/// number of bytes received and the remote endpoint.
pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll<Result<(usize, IpEndpoint), Error>> {
self.with_mut(|s, _| match s.recv_slice(buf) {
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
// No data ready
Err(udp::RecvError::Exhausted) => {
s.register_recv_waker(cx.waker());
Poll::Pending
}
poll_fn(move |cx| {
self.with_mut(|s, _| match s.recv_slice(buf) {
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
// No data ready
Err(udp::RecvError::Exhausted) => {
s.register_recv_waker(cx.waker());
Poll::Pending
}
})
})
.await
}
/// Send a datagram to the specified remote endpoint.
///
/// This method will wait until the datagram has been sent.
///
/// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)`
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error>
where
T: Into<IpEndpoint>,
{
let remote_endpoint: IpEndpoint = remote_endpoint.into();
poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await
}
/// Send a datagram to the specified remote endpoint.
///
/// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.
///
/// When the socket's send buffer is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the buffer has space available.
///
/// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.
pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), Error>>
where
T: Into<IpEndpoint>,
{
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
// Entire datagram has been sent
Ok(()) => Poll::Ready(Ok(())),
Err(udp::SendError::BufferFull) => {
s.register_send_waker(cx.waker());
Poll::Pending
}
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
let remote_endpoint = remote_endpoint.into();
poll_fn(move |cx| {
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
// Entire datagram has been sent
Ok(()) => Poll::Ready(Ok(())),
Err(udp::SendError::BufferFull) => {
s.register_send_waker(cx.waker());
Poll::Pending
}
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
})
})
.await
}
/// Returns the local endpoint of the socket.

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/"
features = ["nightly", "time", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
flavors = [
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
@ -32,10 +32,10 @@ rt = [
time = ["dep:embassy-time"]
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"]
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
# Enable nightly-only features
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"]
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
@ -91,23 +91,22 @@ _dppi = []
_gpio-p1 = []
[dependencies]
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-io = { version = "0.5.0" }
embedded-io-async = { version = "0.5.0", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
cortex-m-rt = ">=0.6.15,<0.8"
cortex-m = "0.7.6"
futures = { version = "0.3.17", default-features = false }
futures = { version = "0.3.17", default-features = false }
critical-section = "1.1"
rand_core = "0.6.3"
fixed = "1.10.0"
@ -115,13 +114,13 @@ embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
cfg-if = "1.0.0"
nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true }
nrf52820-pac = { version = "0.12.0", optional = true }
nrf52832-pac = { version = "0.12.0", optional = true }
nrf52833-pac = { version = "0.12.0", optional = true }
nrf52840-pac = { version = "0.12.0", optional = true }
nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true }
nrf52820-pac = { version = "0.12.0", optional = true }
nrf52832-pac = { version = "0.12.0", optional = true }
nrf52833-pac = { version = "0.12.0", optional = true }
nrf52840-pac = { version = "0.12.0", optional = true }
nrf5340-app-pac = { version = "0.12.0", optional = true }
nrf5340-net-pac = { version = "0.12.0", optional = true }
nrf9160-pac = { version = "0.12.0", optional = true }

View File

@ -15,8 +15,8 @@ use core::slice;
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
use core::task::Poll;
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::atomic_ring_buffer::RingBuffer;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
// Re-export SVD variants to allow user to directly set values
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
@ -572,37 +572,37 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> {
mod _embedded_io {
use super::*;
impl embedded_io_async::Error for Error {
fn kind(&self) -> embedded_io_async::ErrorKind {
impl embedded_io::Error for Error {
fn kind(&self) -> embedded_io::ErrorKind {
match *self {}
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U, T> {
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
type Error = Error;
}
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'u, 'd, U, T> {
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
type Error = Error;
}
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteTx<'u, 'd, U, T> {
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
type Error = Error;
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarte<'d, U, T> {
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.inner_read(buf).await
}
}
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'u, 'd, U, T> {
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.inner.inner_read(buf).await
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> {
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
self.inner_fill_buf().await
}
@ -612,7 +612,7 @@ mod _embedded_io {
}
}
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'u, 'd, U, T> {
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
self.inner.inner_fill_buf().await
}
@ -622,7 +622,7 @@ mod _embedded_io {
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarte<'d, U, T> {
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.inner_write(buf).await
}
@ -632,7 +632,7 @@ mod _embedded_io {
}
}
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarteTx<'u, 'd, U, T> {
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.inner.inner_write(buf).await
}

View File

@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// RTC
RTC0,
RTC1,
@ -208,7 +208,7 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// RTC
RTC0,
RTC1,
@ -234,7 +234,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// RTC
RTC0,
RTC1,
@ -236,7 +236,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 256 * 1024;
pub const RESET_PIN: u32 = 18;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// USB
USBD,
@ -224,7 +224,7 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -12,7 +12,7 @@ pub const FLASH_SIZE: usize = 512 * 1024;
pub const RESET_PIN: u32 = 21;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// RTC
RTC0,
RTC1,
@ -263,7 +263,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 512 * 1024;
pub const RESET_PIN: u32 = 18;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// USB
USBD,
@ -306,7 +306,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024;
pub const RESET_PIN: u32 = 18;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// USB
USBD,
@ -311,7 +311,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
POWER_CLOCK,
RADIO,
UARTE0_UART0,

View File

@ -218,7 +218,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// USB
USBD,
@ -506,7 +506,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
FPU,
CACHE,
SPU,

View File

@ -109,7 +109,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 256 * 1024;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// RTC
RTC0,
RTC1,
@ -342,7 +342,7 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
impl_ppi_channel!(PPI_CH30, 30 => configurable);
impl_ppi_channel!(PPI_CH31, 31 => configurable);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
CLOCK_POWER,
RADIO,
RNG,

View File

@ -169,7 +169,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_internal::peripherals! {
embassy_hal_common::peripherals! {
// RTC
RTC0,
RTC1,
@ -368,7 +368,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
embassy_hal_internal::interrupt_mod!(
embassy_hal_common::interrupt_mod!(
SPU,
CLOCK_POWER,
UARTE0_SPIM0_SPIS0_TWIM0_TWIS0,

View File

@ -5,7 +5,7 @@ use core::convert::Infallible;
use core::hint::unreachable_unchecked;
use cfg_if::cfg_if;
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use self::sealed::Pin as _;
use crate::pac::p0 as gpio;

View File

@ -4,7 +4,7 @@ use core::convert::Infallible;
use core::future::{poll_fn, Future};
use core::task::{Context, Poll};
use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::sealed::Pin as _;
@ -221,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
}
/// Returns the IN event, for use with PPI.
pub fn event_in(&self) -> Event<'d> {
pub fn event_in(&self) -> Event {
let g = regs();
Event::from_reg(&g.events_in[self.ch.number()])
}
@ -292,21 +292,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
}
/// Returns the OUT task, for use with PPI.
pub fn task_out(&self) -> Task<'d> {
pub fn task_out(&self) -> Task {
let g = regs();
Task::from_reg(&g.tasks_out[self.ch.number()])
}
/// Returns the CLR task, for use with PPI.
#[cfg(not(feature = "nrf51"))]
pub fn task_clr(&self) -> Task<'d> {
pub fn task_clr(&self) -> Task {
let g = regs();
Task::from_reg(&g.tasks_clr[self.ch.number()])
}
/// Returns the SET task, for use with PPI.
#[cfg(not(feature = "nrf51"))]
pub fn task_set(&self) -> Task<'d> {
pub fn task_set(&self) -> Task {
let g = regs();
Task::from_reg(&g.tasks_set[self.ch.number()])
}

View File

@ -9,8 +9,8 @@ use core::ops::{Deref, DerefMut};
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;

View File

@ -98,7 +98,7 @@ mod chip;
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
@ -127,7 +127,7 @@ pub use chip::pac;
#[cfg(not(feature = "unstable-pac"))]
pub(crate) use chip::pac;
pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
pub use crate::chip::interrupt;
pub use crate::pac::NVIC_PRIO_BITS;
@ -410,13 +410,13 @@ pub fn init(config: config::Config) -> Peripherals {
warn!(
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
);
#[cfg(feature = "reset-pin-as-gpio")]
warn!(
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
);
}
}
@ -432,7 +432,7 @@ pub fn init(config: config::Config) -> Peripherals {
warn!(
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
);
}
}

View File

@ -2,7 +2,7 @@
use core::{ptr, slice};
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
use embedded_storage::nor_flash::{
ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
};

View File

@ -6,24 +6,14 @@ use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use fixed::types::I7F1;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn;
use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
use crate::{interrupt, Peripheral};
/// Interrupt handler.
@ -33,20 +23,7 @@ pub struct InterruptHandler<T: Instance> {
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
if r.events_end.read().bits() != 0 {
r.intenclr.write(|w| w.end().clear());
}
if r.events_started.read().bits() != 0 {
r.intenclr.write(|w| w.started().clear());
}
if r.events_stopped.read().bits() != 0 {
r.intenclr.write(|w| w.stopped().clear());
}
T::regs().intenclr.write(|w| w.end().clear());
T::state().waker.wake();
}
}
@ -67,24 +44,10 @@ pub enum Error {
BufferZeroLength,
/// PDM is not running
NotRunning,
/// PDM is already running
AlreadyRunning,
}
static DUMMY_BUFFER: [i16; 1] = [0; 1];
/// The state of a continuously running sampler. While it reflects
/// the progress of a sampler, it also signals what should be done
/// next. For example, if the sampler has stopped then the Pdm implementation
/// can then tear down its infrastructure.
#[derive(PartialEq)]
pub enum SamplerState {
/// The sampler processed the samples and is ready for more.
Sampled,
/// The sampler is done processing samples.
Stopped,
}
impl<'d, T: Instance> Pdm<'d, T> {
/// Create PDM driver
pub fn new(
@ -116,24 +79,18 @@ impl<'d, T: Instance> Pdm<'d, T> {
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
// configure
r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
r.ratio.write(|w| w.ratio().variant(config.ratio));
// use default for
// - gain right
// - gain left
// - clk
// - ratio
r.mode.write(|w| {
w.operation().variant(config.operation_mode.into());
w.edge().variant(config.edge.into());
w.edge().bit(config.edge == Edge::LeftRising);
w.operation().bit(config.operation_mode == OperationMode::Mono);
w
});
Self::_set_gain(r, config.gain_left, config.gain_right);
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
r.gainl.write(|w| w.gainl().default_gain());
r.gainr.write(|w| w.gainr().default_gain());
// IRQ
T::Interrupt::unpend();
@ -144,25 +101,6 @@ impl<'d, T: Instance> Pdm<'d, T> {
Self { _peri: pdm }
}
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
let gain_left = gain_left
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.clamp(0, 0x50);
let gain_right = gain_right
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.clamp(0, 0x50);
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
}
/// Adjust the gain of the PDM microphone on the fly
pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
Self::_set_gain(T::regs(), gain_left, gain_right)
}
/// Start sampling microphon data into a dummy buffer
/// Usefull to start the microphon and keep it active between recording samples
pub async fn start(&mut self) {
@ -260,108 +198,6 @@ impl<'d, T: Instance> Pdm<'d, T> {
compiler_fence(Ordering::SeqCst);
}
/// Continuous sampling with double buffers.
///
/// A sampler closure is provided that receives the buffer of samples, noting
/// that the size of this buffer can be less than the original buffer's size.
/// A command is return from the closure that indicates whether the sampling
/// should continue or stop.
///
/// NOTE: The time spent within the callback supplied should not exceed the time
/// taken to acquire the samples into a single buffer. You should measure the
/// time taken by the callback and set the sample buffer size accordingly.
/// Exceeding this time can lead to samples becoming dropped.
pub async fn run_task_sampler<S, const N: usize>(
&mut self,
bufs: &mut [[i16; N]; 2],
mut sampler: S,
) -> Result<(), Error>
where
S: FnMut(&[i16; N]) -> SamplerState,
{
let r = T::regs();
if r.events_started.read().bits() != 0 {
return Err(Error::AlreadyRunning);
}
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
// Reset and enable the events
r.events_end.reset();
r.events_started.reset();
r.events_stopped.reset();
r.intenset.write(|w| {
w.end().set();
w.started().set();
w.stopped().set();
w
});
// Don't reorder the start event before the previous writes. Hopefully self
// wouldn't happen anyway.
compiler_fence(Ordering::SeqCst);
r.tasks_start.write(|w| unsafe { w.bits(1) });
let mut current_buffer = 0;
let mut done = false;
let drop = OnDrop::new(|| {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
// N.B. It would be better if this were async, but Drop only support sync code.
while r.events_stopped.read().bits() != 0 {}
});
// Wait for events and complete when the sampler indicates it has had enough.
poll_fn(|cx| {
let r = T::regs();
T::state().waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
compiler_fence(Ordering::SeqCst);
r.events_end.reset();
r.intenset.write(|w| w.end().set());
if !done {
// Discard the last buffer after the user requested a stop.
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
let next_buffer = 1 - current_buffer;
current_buffer = next_buffer;
} else {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
done = true;
};
};
}
if r.events_started.read().bits() != 0 {
r.events_started.reset();
r.intenset.write(|w| w.started().set());
let next_buffer = 1 - current_buffer;
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) });
}
if r.events_stopped.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
drop.defuse();
Ok(())
}
}
/// PDM microphone driver Config
@ -370,20 +206,6 @@ pub struct Config {
pub operation_mode: OperationMode,
/// On which edge the left channel should be samples
pub edge: Edge,
/// Clock frequency
pub frequency: Frequency,
/// Clock ratio
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
pub ratio: Ratio,
/// Gain left in dB
pub gain_left: I7F1,
/// Gain right in dB
pub gain_right: I7F1,
}
impl Default for Config {
@ -391,16 +213,6 @@ impl Default for Config {
Self {
operation_mode: OperationMode::Mono,
edge: Edge::LeftFalling,
frequency: Frequency::DEFAULT,
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
ratio: Ratio::RATIO80,
gain_left: I7F1::ZERO,
gain_right: I7F1::ZERO,
}
}
}
@ -414,15 +226,6 @@ pub enum OperationMode {
Stereo,
}
impl From<OperationMode> for OPERATION_A {
fn from(mode: OperationMode) -> Self {
match mode {
OperationMode::Mono => OPERATION_A::MONO,
OperationMode::Stereo => OPERATION_A::STEREO,
}
}
}
/// PDM edge polarity
#[derive(PartialEq)]
pub enum Edge {
@ -432,15 +235,6 @@ pub enum Edge {
LeftFalling,
}
impl From<Edge> for EDGE_A {
fn from(edge: Edge) -> Self {
match edge {
Edge::LeftRising => EDGE_A::LEFT_RISING,
Edge::LeftFalling => EDGE_A::LEFT_FALLING,
}
}
}
impl<'d, T: Instance> Drop for Pdm<'d, T> {
fn drop(&mut self) {
let r = T::regs();

View File

@ -1,4 +1,4 @@
use embassy_hal_internal::into_ref;
use embassy_hal_common::into_ref;
use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
use crate::{pac, Peripheral};
@ -12,14 +12,14 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
Ppi::new_many_to_many(ch, [event], [task])
}
}
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
Ppi::new_many_to_many(ch, [event], [task1, task2])
}
}
@ -30,8 +30,8 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
pub fn new_many_to_many(
ch: impl Peripheral<P = C> + 'd,
events: [Event<'d>; EVENT_COUNT],
tasks: [Task<'d>; TASK_COUNT],
events: [Event; EVENT_COUNT],
tasks: [Task; TASK_COUNT],
) -> Self {
into_ref!(ch);

View File

@ -15,10 +15,9 @@
//! many tasks and events, but any single task or event can only be coupled with one channel.
//!
use core::marker::PhantomData;
use core::ptr::NonNull;
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use crate::{peripherals, Peripheral};
@ -31,9 +30,9 @@ pub(crate) use _version::*;
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
ch: PeripheralRef<'d, C>,
#[cfg(feature = "_dppi")]
events: [Event<'d>; EVENT_COUNT],
events: [Event; EVENT_COUNT],
#[cfg(feature = "_dppi")]
tasks: [Task<'d>; TASK_COUNT],
tasks: [Task; TASK_COUNT],
}
/// PPI channel group driver.
@ -96,7 +95,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
/// Get a reference to the "enable all" task.
///
/// When triggered, it will enable all the channels in this group.
pub fn task_enable_all(&self) -> Task<'d> {
pub fn task_enable_all(&self) -> Task {
let n = self.g.number();
Task::from_reg(&regs().tasks_chg[n].en)
}
@ -104,7 +103,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
/// Get a reference to the "disable all" task.
///
/// When triggered, it will disable all the channels in this group.
pub fn task_disable_all(&self) -> Task<'d> {
pub fn task_disable_all(&self) -> Task {
let n = self.g.number();
Task::from_reg(&regs().tasks_chg[n].dis)
}
@ -126,28 +125,20 @@ const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
/// a published event.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
pub struct Task(NonNull<u32>);
impl<'d> Task<'d> {
impl Task {
/// Create a new `Task` from a task register pointer
///
/// # Safety
///
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
Self(ptr, PhantomData)
}
/// Triggers this task.
pub fn trigger(&mut self) {
unsafe { self.0.as_ptr().write_volatile(1) };
Self(ptr)
}
pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
PhantomData,
)
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
}
/// Address of subscription register for this task.
@ -160,39 +151,26 @@ impl<'d> Task<'d> {
/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Task<'_> {}
unsafe impl Send for Task {}
/// Represents an event that a peripheral can publish.
///
/// An event can be set to publish on a PPI channel when the event happens.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
pub struct Event(NonNull<u32>);
impl<'d> Event<'d> {
impl Event {
/// Create a new `Event` from an event register pointer
///
/// # Safety
///
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
Self(ptr, PhantomData)
Self(ptr)
}
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
Self(
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
PhantomData,
)
}
/// Describes whether this Event is currently in a triggered state.
pub fn is_triggered(&self) -> bool {
unsafe { self.0.as_ptr().read_volatile() == 1 }
}
/// Clear the current register's triggered state, reverting it to 0.
pub fn clear(&mut self) {
unsafe { self.0.as_ptr().write_volatile(0) };
pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
}
/// Address of publish register for this event.
@ -205,7 +183,7 @@ impl<'d> Event<'d> {
/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Event<'_> {}
unsafe impl Send for Event {}
// ======================
// traits

View File

@ -1,14 +1,14 @@
use embassy_hal_internal::into_ref;
use embassy_hal_common::into_ref;
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
use crate::{pac, Peripheral};
impl<'d> Task<'d> {
impl Task {
fn reg_val(&self) -> u32 {
self.0.as_ptr() as _
}
}
impl<'d> Event<'d> {
impl Event {
fn reg_val(&self) -> u32 {
self.0.as_ptr() as _
}
@ -34,7 +34,7 @@ impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
into_ref!(ch);
let r = regs();
@ -49,7 +49,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
into_ref!(ch);
let r = regs();

View File

@ -4,7 +4,7 @@
use core::sync::atomic::{compiler_fence, Ordering};
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
@ -181,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Stopped` event endpoint for PPI.
#[inline(always)]
pub fn event_stopped(&self) -> Event<'d> {
pub fn event_stopped(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_stopped)
@ -189,7 +189,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `LoopsDone` event endpoint for PPI.
#[inline(always)]
pub fn event_loops_done(&self) -> Event<'d> {
pub fn event_loops_done(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_loopsdone)
@ -197,7 +197,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
#[inline(always)]
pub fn event_pwm_period_end(&self) -> Event<'d> {
pub fn event_pwm_period_end(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_pwmperiodend)
@ -205,7 +205,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq0 End` event endpoint for PPI.
#[inline(always)]
pub fn event_seq_end(&self) -> Event<'d> {
pub fn event_seq_end(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_seqend[0])
@ -213,7 +213,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq1 End` event endpoint for PPI.
#[inline(always)]
pub fn event_seq1_end(&self) -> Event<'d> {
pub fn event_seq1_end(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_seqend[1])
@ -221,7 +221,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq0 Started` event endpoint for PPI.
#[inline(always)]
pub fn event_seq0_started(&self) -> Event<'d> {
pub fn event_seq0_started(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_seqstarted[0])
@ -229,7 +229,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq1 Started` event endpoint for PPI.
#[inline(always)]
pub fn event_seq1_started(&self) -> Event<'d> {
pub fn event_seq1_started(&self) -> Event {
let r = T::regs();
Event::from_reg(&r.events_seqstarted[1])
@ -240,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_start_seq0(&self) -> Task<'d> {
pub unsafe fn task_start_seq0(&self) -> Task {
let r = T::regs();
Task::from_reg(&r.tasks_seqstart[0])
@ -251,7 +251,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_start_seq1(&self) -> Task<'d> {
pub unsafe fn task_start_seq1(&self) -> Task {
let r = T::regs();
Task::from_reg(&r.tasks_seqstart[1])
@ -262,7 +262,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_next_step(&self) -> Task<'d> {
pub unsafe fn task_next_step(&self) -> Task {
let r = T::regs();
Task::from_reg(&r.tasks_nextstep)
@ -273,7 +273,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_stop(&self) -> Task<'d> {
pub unsafe fn task_stop(&self) -> Task {
let r = T::regs();
Task::from_reg(&r.tasks_stop)

View File

@ -6,7 +6,7 @@ use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};

View File

@ -7,8 +7,8 @@ use core::marker::PhantomData;
use core::ptr;
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
use crate::gpio::{self, Pin as GpioPin};

View File

@ -8,8 +8,8 @@ use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::typelevel::Interrupt;

View File

@ -6,8 +6,8 @@ use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use pac::{saadc, SAADC};
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
@ -320,9 +320,7 @@ impl<'d, const N: usize> Saadc<'d, N> {
timer.cc(0).write(sample_counter);
timer.cc(0).short_compare_clear();
let timer_cc = timer.cc(0);
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample));
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer.cc(0).event_compare(), Task::from_reg(&r.tasks_sample));
timer.start();

View File

@ -8,7 +8,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
@ -468,19 +468,25 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead<u8> for Spim<'d, T> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_transfer(words, &[])
}
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite<u8> for Spim<'d, T> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -496,19 +502,25 @@ mod eha {
use super::*;
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
async fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.read(words).await
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
self.write(data).await
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
self.transfer(rx, tx).await
}

View File

@ -7,7 +7,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
use crate::chip::FORCE_COPY_BUFFER_SIZE;

View File

@ -3,8 +3,8 @@
use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::I30F2;

View File

@ -6,7 +6,7 @@
#![macro_use]
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
use crate::ppi::{Event, Task};
use crate::{pac, Peripheral};
@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> {
/// Returns the START task, for use with PPI.
///
/// When triggered, this task starts the timer.
pub fn task_start(&self) -> Task<'d> {
pub fn task_start(&self) -> Task {
Task::from_reg(&T::regs().tasks_start)
}
/// Returns the STOP task, for use with PPI.
///
/// When triggered, this task stops the timer.
pub fn task_stop(&self) -> Task<'d> {
pub fn task_stop(&self) -> Task {
Task::from_reg(&T::regs().tasks_stop)
}
/// Returns the CLEAR task, for use with PPI.
///
/// When triggered, this task resets the timer's counter to 0.
pub fn task_clear(&self) -> Task<'d> {
pub fn task_clear(&self) -> Task {
Task::from_reg(&T::regs().tasks_clear)
}
@ -190,7 +190,7 @@ impl<'d, T: Instance> Timer<'d, T> {
///
/// When triggered, this task increments the timer's counter by 1.
/// Only works in counter mode.
pub fn task_count(&self) -> Task<'d> {
pub fn task_count(&self) -> Task {
Task::from_reg(&T::regs().tasks_count)
}
@ -258,14 +258,14 @@ impl<'d, T: Instance> Cc<'d, T> {
/// Returns this CC register's CAPTURE task, for use with PPI.
///
/// When triggered, this task will capture the current value of the timer's counter in this register.
pub fn task_capture(&self) -> Task<'d> {
pub fn task_capture(&self) -> Task {
Task::from_reg(&T::regs().tasks_capture)
}
/// Returns this CC register's COMPARE event, for use with PPI.
///
/// This event will fire when the timer's counter reaches the value in this CC register.
pub fn event_compare(&self) -> Event<'d> {
pub fn event_compare(&self) -> Event {
Event::from_reg(&T::regs().events_compare[self.n])
}

View File

@ -9,7 +9,7 @@ use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
use embassy_embedded_hal::SetConfig;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};

View File

@ -8,7 +8,7 @@ use core::sync::atomic::compiler_fence;
use core::sync::atomic::Ordering::SeqCst;
use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
#[cfg(feature = "time")]
use embassy_time::{Duration, Instant};

View File

@ -18,8 +18,8 @@ use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use pac::uarte0::RegisterBlock;
// Re-export SVD variants to allow user to directly set values.
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};

Some files were not shown because too many files have changed in this diff Show More