Merge branch 'main' of github.com:embassy-rs/embassy

This commit is contained in:
klownfish 2024-09-11 11:55:50 +02:00
commit f0a86d1a34
418 changed files with 18674 additions and 1491 deletions

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

@ -8,6 +8,10 @@ export RUSTUP_HOME=/ci/cache/rustup
export CARGO_HOME=/ci/cache/cargo
export CARGO_TARGET_DIR=/ci/cache/target
# needed for "dumb HTTP" transport support
# used when pointing stm32-metapac to a CI-built one.
export CARGO_NET_GIT_FETCH_WITH_CLI=true
cargo test --manifest-path ./embassy-futures/Cargo.toml
cargo test --manifest-path ./embassy-sync/Cargo.toml
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
@ -21,7 +25,8 @@ cargo test --manifest-path ./embassy-boot/Cargo.toml --features ed25519-salty
cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nrf52840,time-driver-rtc1,gpiote
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp2040,_test
cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --features time-driver,rp235xa,_test
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f429vg,exti,time-driver-any,exti
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features stm32f732ze,exti,time-driver-any,exti

58
ci.sh
View File

@ -2,11 +2,13 @@
set -eo pipefail
# check-cfg is stable on rustc 1.79 but not cargo 1.79.
# however, our cargo-batch is currently based on cargo 1.80, which does support check-cfg.
# so, force build.rs scripts to emit check-cfg commands.
# when 1.80 hits stable we can make build.rs unconditionally emit check-cfg and remove all this.
export EMBASSY_FORCE_CHECK_CFG=1
if ! command -v cargo-batch &> /dev/null; then
echo "cargo-batch could not be found. Install it with the following command:"
echo ""
echo " cargo install --git https://github.com/embassy-rs/cargo-batch cargo --bin cargo-batch --locked"
echo ""
exit 1
fi
export RUSTFLAGS=-Dwarnings
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
@ -80,10 +82,13 @@ cargo batch \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,defmt,time \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv6m-none-eabi --features nrf51,time \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,defmt,rp2040 \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,log,rp2040 \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,intrinsics,rp2040 \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features time-driver,qspi-as-gpio,rp2040 \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,defmt,rp235xa \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,log,rp235xa \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv8m.main-none-eabihf --features time-driver,rp235xa,binary-info \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
@ -171,12 +176,17 @@ cargo batch \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs' \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs,bluetooth' \
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs,bluetooth' \
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040' \
--- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'embassy-rp/rp2040,overclock' \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
--- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi \
--- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \
--- build --release --manifest-path embassy-boot-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \
--- build --release --manifest-path embassy-boot-rp/Cargo.toml --target thumbv6m-none-eabi --features embassy-rp/rp2040 \
--- build --release --manifest-path embassy-boot-stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \
--- build --release --manifest-path docs/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path docs/examples/layer-by-layer/blinky-pac/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path docs/examples/layer-by-layer/blinky-hal/Cargo.toml --target thumbv7em-none-eabi \
@ -186,8 +196,11 @@ cargo batch \
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
--- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9160 \
--- build --release --manifest-path examples/nrf9151/s/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/s \
--- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/ns \
--- build --release --manifest-path examples/nrf51/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/nrf51 \
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
--- build --release --manifest-path examples/rp23/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/rp23 \
--- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
--- 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 \
@ -202,6 +215,8 @@ cargo batch \
--- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32h5 \
--- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \
--- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h735 \
--- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h755cm4 \
--- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h755cm7 \
--- build --release --manifest-path examples/stm32h7rs/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7rs \
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
@ -214,6 +229,9 @@ cargo batch \
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wl \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns,skip-include --out-dir out/examples/boot/nrf9120 \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns,skip-include --out-dir out/examples/boot/nrf9151 \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns,skip-include --out-dir out/examples/boot/nrf9161 \
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
--- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \
@ -225,10 +243,13 @@ cargo batch \
--- build --release --manifest-path examples/boot/application/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32wb-dfu \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9151-ns \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9161-ns \
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
--- build --release --manifest-path examples/boot/bootloader/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32l496zg \
--- build --release --manifest-path examples/boot/bootloader/stm32wb-dfu/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wb55rg \
--- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h747xi-cm7 \
--- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h743zi \
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
@ -269,6 +290,9 @@ cargo batch \
$BUILD_EXTRA
# temporarily disabled, bluepill board got bricked
rm -rf out/tests/stm32f103c8
rm out/tests/stm32wb55rg/wpan_mac
rm out/tests/stm32wb55rg/wpan_ble
@ -278,8 +302,10 @@ rm out/tests/stm32f207zg/eth
# doesn't work, gives "noise error", no idea why. usart_dma does pass.
rm out/tests/stm32u5a5zj/usart
# flaky, perhaps bad wire
# flaky, probably due to bad ringbuffered dma code.
rm out/tests/stm32l152re/usart_rx_ringbuffered
rm out/tests/stm32f207zg/usart_rx_ringbuffered
rm out/tests/stm32wl55jc/usart_rx_ringbuffered
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
echo No teleprobe token found, skipping running HIL tests

BIN
cyw43-firmware/43439A0.bin Executable file → Normal file

Binary file not shown.

Binary file not shown.

BIN
cyw43-firmware/43439A0_clm.bin Executable file → Normal file

Binary file not shown.

View File

@ -1,9 +1,14 @@
# WiFi firmware
# WiFi + Bluetooth firmware blobs
Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439
Firmware obtained from https://github.com/georgerobotics/cyw43-driver/tree/main/firmware
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
* 2023-08-21: synced with `a1dc885` - Update 43439 fw + clm to come from `wb43439A0_7_95_49_00_combined.h` + add Bluetooth firmware
* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 to 7.95.62
## Notes
If you update these files, please update the lengths in the `tests/rp/src/bin/cyw43_perf.rs` test (which relies on these files running from RAM).

17
cyw43-pio/CHANGELOG.md Normal file
View File

@ -0,0 +1,17 @@
# Changelog
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).
## Unreleased
## 0.2.0 - 2024-08-05
- Update to cyw43 0.2.0
- Update to embassy-rp 0.2.0
## 0.1.0 - 2024-01-11
- First release

View File

@ -1,6 +1,6 @@
[package]
name = "cyw43-pio"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "RP2040 PIO SPI implementation for cyw43"
keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
@ -15,8 +15,8 @@ documentation = "https://docs.embassy.dev/cyw43-pio"
overclock = []
[dependencies]
cyw43 = { version = "0.1.0", path = "../cyw43" }
embassy-rp = { version = "0.1.0", path = "../embassy-rp" }
cyw43 = { version = "0.2.0", path = "../cyw43" }
embassy-rp = { version = "0.2.0", path = "../embassy-rp" }
pio-proc = "0.2"
pio = "0.2.1"
fixed = "1.23.1"
@ -26,3 +26,4 @@ defmt = { version = "0.3", optional = true }
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-pio-v$VERSION/cyw43-pio/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43-pio/src/"
target = "thumbv6m-none-eabi"
features = ["embassy-rp/rp2040"]

View File

@ -181,7 +181,10 @@ where
let read_bits = read.len() * 32 + 32 - 1;
#[cfg(feature = "defmt")]
defmt::trace!("write={} read={}", write_bits, read_bits);
defmt::trace!("cmd_read write={} read={}", write_bits, read_bits);
#[cfg(feature = "defmt")]
defmt::trace!("cmd_read cmd = {:02x} len = {}", cmd, read.len());
unsafe {
instr::set_y(&mut self.sm, read_bits as u32);
@ -201,6 +204,10 @@ where
.rx()
.dma_pull(self.dma.reborrow(), slice::from_mut(&mut status))
.await;
#[cfg(feature = "defmt")]
defmt::trace!("cmd_read cmd = {:02x} len = {} read = {:08x}", cmd, read.len(), read);
status
}
}

23
cyw43/CHANGELOG.md Normal file
View File

@ -0,0 +1,23 @@
# Changelog
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).
## Unreleased
## 0.2.0 - 2024-08-05
- Update to new versions of embassy-{time,sync}
- Add more fields to the BssInfo packet struct #2461
- Extend the Scan API #2282
- Reuse buf to reduce stack usage #2580
- Add MAC address getter to cyw43 controller #2818
- Add function to join WPA2 network with precomputed PSK. #2885
- Add function to close soft AP. #3042
- Fixing missing re-export #3211
## 0.1.0 - 2024-01-11
- First release

View File

@ -1,6 +1,6 @@
[package]
name = "cyw43"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W."
keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"]
@ -10,17 +10,18 @@ repository = "https://github.com/embassy-rs/embassy"
documentation = "https://docs.embassy.dev/cyw43"
[features]
defmt = ["dep:defmt", "heapless/defmt-03", "embassy-time/defmt"]
defmt = ["dep:defmt", "heapless/defmt-03", "embassy-time/defmt", "bt-hci?/defmt", "embedded-io-async?/defmt-03"]
log = ["dep:log"]
bluetooth = ["dep:bt-hci", "dep:embedded-io-async"]
# Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`.
firmware-logs = []
[dependencies]
embassy-time = { version = "0.3.1", path = "../embassy-time"}
embassy-time = { version = "0.3.2", path = "../embassy-time"}
embassy-sync = { version = "0.6.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"}
defmt = { version = "0.3", optional = true }
log = { version = "0.4.17", optional = true }
@ -31,9 +32,12 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
num_enum = { version = "0.5.7", default-features = false }
heapless = "0.8.0"
# Bluetooth deps
embedded-io-async = { version = "0.6.0", optional = true }
bt-hci = { version = "0.1.0", optional = true }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"

508
cyw43/src/bluetooth.rs Normal file
View File

@ -0,0 +1,508 @@
use core::cell::RefCell;
use core::future::Future;
use core::mem::MaybeUninit;
use bt_hci::transport::WithIndicator;
use bt_hci::{ControllerToHostPacket, FromHciBytes, HostToControllerPacket, PacketKind, WriteHci};
use embassy_futures::yield_now;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::zerocopy_channel;
use embassy_time::{Duration, Timer};
use embedded_hal_1::digital::OutputPin;
use crate::bus::Bus;
pub use crate::bus::SpiBusCyw43;
use crate::consts::*;
use crate::util::round_up;
use crate::{util, CHIP};
pub(crate) struct BtState {
rx: [BtPacketBuf; 4],
tx: [BtPacketBuf; 4],
inner: MaybeUninit<BtStateInnre<'static>>,
}
impl BtState {
pub const fn new() -> Self {
Self {
rx: [const { BtPacketBuf::new() }; 4],
tx: [const { BtPacketBuf::new() }; 4],
inner: MaybeUninit::uninit(),
}
}
}
struct BtStateInnre<'d> {
rx: zerocopy_channel::Channel<'d, NoopRawMutex, BtPacketBuf>,
tx: zerocopy_channel::Channel<'d, NoopRawMutex, BtPacketBuf>,
}
/// Bluetooth driver.
pub struct BtDriver<'d> {
rx: RefCell<zerocopy_channel::Receiver<'d, NoopRawMutex, BtPacketBuf>>,
tx: RefCell<zerocopy_channel::Sender<'d, NoopRawMutex, BtPacketBuf>>,
}
pub(crate) struct BtRunner<'d> {
pub(crate) tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, BtPacketBuf>,
rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, BtPacketBuf>,
// Bluetooth circular buffers
addr: u32,
h2b_write_pointer: u32,
b2h_read_pointer: u32,
}
const BT_HCI_MTU: usize = 1024;
/// Represents a packet of size MTU.
pub(crate) struct BtPacketBuf {
pub(crate) len: usize,
pub(crate) buf: [u8; BT_HCI_MTU],
}
impl BtPacketBuf {
/// Create a new packet buffer.
pub const fn new() -> Self {
Self {
len: 0,
buf: [0; BT_HCI_MTU],
}
}
}
pub(crate) fn new<'d>(state: &'d mut BtState) -> (BtRunner<'d>, BtDriver<'d>) {
// safety: this is a self-referential struct, however:
// - it can't move while the `'d` borrow is active.
// - when the borrow ends, the dangling references inside the MaybeUninit will never be used again.
let state_uninit: *mut MaybeUninit<BtStateInnre<'d>> =
(&mut state.inner as *mut MaybeUninit<BtStateInnre<'static>>).cast();
let state = unsafe { &mut *state_uninit }.write(BtStateInnre {
rx: zerocopy_channel::Channel::new(&mut state.rx[..]),
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
});
let (rx_sender, rx_receiver) = state.rx.split();
let (tx_sender, tx_receiver) = state.tx.split();
(
BtRunner {
tx_chan: tx_receiver,
rx_chan: rx_sender,
addr: 0,
h2b_write_pointer: 0,
b2h_read_pointer: 0,
},
BtDriver {
rx: RefCell::new(rx_receiver),
tx: RefCell::new(tx_sender),
},
)
}
pub(crate) struct CybtFwCb<'a> {
pub p_next_line_start: &'a [u8],
}
pub(crate) struct HexFileData<'a> {
pub addr_mode: i32,
pub hi_addr: u16,
pub dest_addr: u32,
pub p_ds: &'a mut [u8],
}
pub(crate) fn read_firmware_patch_line(p_btfw_cb: &mut CybtFwCb, hfd: &mut HexFileData) -> u32 {
let mut abs_base_addr32 = 0;
loop {
let num_bytes = p_btfw_cb.p_next_line_start[0];
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[1..];
let addr = (p_btfw_cb.p_next_line_start[0] as u16) << 8 | p_btfw_cb.p_next_line_start[1] as u16;
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[2..];
let line_type = p_btfw_cb.p_next_line_start[0];
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[1..];
if num_bytes == 0 {
break;
}
hfd.p_ds[..num_bytes as usize].copy_from_slice(&p_btfw_cb.p_next_line_start[..num_bytes as usize]);
p_btfw_cb.p_next_line_start = &p_btfw_cb.p_next_line_start[num_bytes as usize..];
match line_type {
BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS => {
hfd.hi_addr = (hfd.p_ds[0] as u16) << 8 | hfd.p_ds[1] as u16;
hfd.addr_mode = BTFW_ADDR_MODE_EXTENDED;
}
BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS => {
hfd.hi_addr = (hfd.p_ds[0] as u16) << 8 | hfd.p_ds[1] as u16;
hfd.addr_mode = BTFW_ADDR_MODE_SEGMENT;
}
BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS => {
abs_base_addr32 = (hfd.p_ds[0] as u32) << 24
| (hfd.p_ds[1] as u32) << 16
| (hfd.p_ds[2] as u32) << 8
| hfd.p_ds[3] as u32;
hfd.addr_mode = BTFW_ADDR_MODE_LINEAR32;
}
BTFW_HEX_LINE_TYPE_DATA => {
hfd.dest_addr = addr as u32;
match hfd.addr_mode {
BTFW_ADDR_MODE_EXTENDED => hfd.dest_addr += (hfd.hi_addr as u32) << 16,
BTFW_ADDR_MODE_SEGMENT => hfd.dest_addr += (hfd.hi_addr as u32) << 4,
BTFW_ADDR_MODE_LINEAR32 => hfd.dest_addr += abs_base_addr32,
_ => {}
}
return num_bytes as u32;
}
_ => {}
}
}
0
}
impl<'a> BtRunner<'a> {
pub(crate) async fn init_bluetooth(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>, firmware: &[u8]) {
trace!("init_bluetooth");
bus.bp_write32(CHIP.bluetooth_base_address + BT2WLAN_PWRUP_ADDR, BT2WLAN_PWRUP_WAKE)
.await;
Timer::after(Duration::from_millis(2)).await;
self.upload_bluetooth_firmware(bus, firmware).await;
self.wait_bt_ready(bus).await;
self.init_bt_buffers(bus).await;
self.wait_bt_awake(bus).await;
self.bt_set_host_ready(bus).await;
self.bt_toggle_intr(bus).await;
}
pub(crate) async fn upload_bluetooth_firmware(
&mut self,
bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>,
firmware: &[u8],
) {
// read version
let version_length = firmware[0];
let _version = &firmware[1..=version_length as usize];
// skip version + 1 extra byte as per cybt_shared_bus_driver.c
let firmware = &firmware[version_length as usize + 2..];
// buffers
let mut data_buffer: [u8; 0x100] = [0; 0x100];
let mut aligned_data_buffer: [u8; 0x100] = [0; 0x100];
// structs
let mut btfw_cb = CybtFwCb {
p_next_line_start: firmware,
};
let mut hfd = HexFileData {
addr_mode: BTFW_ADDR_MODE_EXTENDED,
hi_addr: 0,
dest_addr: 0,
p_ds: &mut data_buffer,
};
loop {
let num_fw_bytes = read_firmware_patch_line(&mut btfw_cb, &mut hfd);
if num_fw_bytes == 0 {
break;
}
let fw_bytes = &hfd.p_ds[0..num_fw_bytes as usize];
let mut dest_start_addr = hfd.dest_addr + CHIP.bluetooth_base_address;
let mut aligned_data_buffer_index: usize = 0;
// pad start
if !util::is_aligned(dest_start_addr, 4) {
let num_pad_bytes = dest_start_addr % 4;
let padded_dest_start_addr = util::round_down(dest_start_addr, 4);
let memory_value = bus.bp_read32(padded_dest_start_addr).await;
let memory_value_bytes = memory_value.to_le_bytes();
// Copy the previous memory value's bytes to the start
for i in 0..num_pad_bytes as usize {
aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i];
aligned_data_buffer_index += 1;
}
// Copy the firmware bytes after the padding bytes
for i in 0..num_fw_bytes as usize {
aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i];
aligned_data_buffer_index += 1;
}
dest_start_addr = padded_dest_start_addr;
} else {
// Directly copy fw_bytes into aligned_data_buffer if no start padding is required
for i in 0..num_fw_bytes as usize {
aligned_data_buffer[aligned_data_buffer_index] = fw_bytes[i];
aligned_data_buffer_index += 1;
}
}
// pad end
let mut dest_end_addr = dest_start_addr + aligned_data_buffer_index as u32;
if !util::is_aligned(dest_end_addr, 4) {
let offset = dest_end_addr % 4;
let num_pad_bytes_end = 4 - offset;
let padded_dest_end_addr = util::round_down(dest_end_addr, 4);
let memory_value = bus.bp_read32(padded_dest_end_addr).await;
let memory_value_bytes = memory_value.to_le_bytes();
// Append the necessary memory bytes to pad the end of aligned_data_buffer
for i in offset..4 {
aligned_data_buffer[aligned_data_buffer_index] = memory_value_bytes[i as usize];
aligned_data_buffer_index += 1;
}
dest_end_addr += num_pad_bytes_end;
} else {
// pad end alignment not needed
}
let buffer_to_write = &aligned_data_buffer[0..aligned_data_buffer_index as usize];
assert!(dest_start_addr % 4 == 0);
assert!(dest_end_addr % 4 == 0);
assert!(aligned_data_buffer_index % 4 == 0);
bus.bp_write(dest_start_addr, buffer_to_write).await;
}
}
pub(crate) async fn wait_bt_ready(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("wait_bt_ready");
let mut success = false;
for _ in 0..300 {
let val = bus.bp_read32(BT_CTRL_REG_ADDR).await;
trace!("BT_CTRL_REG_ADDR = {:08x}", val);
if val & BTSDIO_REG_FW_RDY_BITMASK != 0 {
success = true;
break;
}
Timer::after(Duration::from_millis(1)).await;
}
assert!(success == true);
}
pub(crate) async fn wait_bt_awake(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("wait_bt_awake");
let mut success = false;
for _ in 0..300 {
let val = bus.bp_read32(BT_CTRL_REG_ADDR).await;
trace!("BT_CTRL_REG_ADDR = {:08x}", val);
if val & BTSDIO_REG_BT_AWAKE_BITMASK != 0 {
success = true;
break;
}
Timer::after(Duration::from_millis(1)).await;
}
assert!(success == true);
}
pub(crate) async fn bt_set_host_ready(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("bt_set_host_ready");
let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;
// TODO: do we need to swap endianness on this read?
let new_val = old_val | BTSDIO_REG_SW_RDY_BITMASK;
bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;
}
// TODO: use this
#[allow(dead_code)]
pub(crate) async fn bt_set_awake(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>, awake: bool) {
trace!("bt_set_awake");
let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;
// TODO: do we need to swap endianness on this read?
let new_val = if awake {
old_val | BTSDIO_REG_WAKE_BT_BITMASK
} else {
old_val & !BTSDIO_REG_WAKE_BT_BITMASK
};
bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;
}
pub(crate) async fn bt_toggle_intr(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("bt_toggle_intr");
let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;
// TODO: do we need to swap endianness on this read?
let new_val = old_val ^ BTSDIO_REG_DATA_VALID_BITMASK;
bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;
}
// TODO: use this
#[allow(dead_code)]
pub(crate) async fn bt_set_intr(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("bt_set_intr");
let old_val = bus.bp_read32(HOST_CTRL_REG_ADDR).await;
let new_val = old_val | BTSDIO_REG_DATA_VALID_BITMASK;
bus.bp_write32(HOST_CTRL_REG_ADDR, new_val).await;
}
pub(crate) async fn init_bt_buffers(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
trace!("init_bt_buffers");
self.addr = bus.bp_read32(WLAN_RAM_BASE_REG_ADDR).await;
assert!(self.addr != 0);
trace!("wlan_ram_base_addr = {:08x}", self.addr);
bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_IN, 0).await;
bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_OUT, 0).await;
bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_IN, 0).await;
bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_OUT, 0).await;
}
async fn bt_bus_request(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
// TODO: CYW43_THREAD_ENTER mutex?
self.bt_set_awake(bus, true).await;
self.wait_bt_awake(bus).await;
}
pub(crate) async fn hci_write(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
self.bt_bus_request(bus).await;
// NOTE(unwrap): we only call this when we do have a packet in the queue.
let buf = self.tx_chan.try_receive().unwrap();
debug!("HCI tx: {:02x}", crate::fmt::Bytes(&buf.buf[..buf.len]));
let len = buf.len as u32 - 1; // len doesn't include hci type byte
let rounded_len = round_up(len, 4);
let total_len = 4 + rounded_len;
let read_pointer = bus.bp_read32(self.addr + BTSDIO_OFFSET_HOST2BT_OUT).await;
let available = read_pointer.wrapping_sub(self.h2b_write_pointer + 4) % BTSDIO_FWBUF_SIZE;
if available < total_len {
warn!(
"bluetooth tx queue full, retrying. len {} available {}",
total_len, available
);
yield_now().await;
return;
}
// Build header
let mut header = [0u8; 4];
header[0] = len as u8;
header[1] = (len >> 8) as u8;
header[2] = (len >> 16) as u8;
header[3] = buf.buf[0]; // HCI type byte
// Write header
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;
bus.bp_write(addr, &header).await;
self.h2b_write_pointer = (self.h2b_write_pointer + 4) % BTSDIO_FWBUF_SIZE;
// Write payload.
let payload = &buf.buf[1..][..rounded_len as usize];
if self.h2b_write_pointer as usize + payload.len() > BTSDIO_FWBUF_SIZE as usize {
// wraparound
let n = BTSDIO_FWBUF_SIZE - self.h2b_write_pointer;
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;
bus.bp_write(addr, &payload[..n as usize]).await;
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF;
bus.bp_write(addr, &payload[n as usize..]).await;
} else {
// no wraparound
let addr = self.addr + BTSDIO_OFFSET_HOST_WRITE_BUF + self.h2b_write_pointer;
bus.bp_write(addr, payload).await;
}
self.h2b_write_pointer = (self.h2b_write_pointer + payload.len() as u32) % BTSDIO_FWBUF_SIZE;
// Update pointer.
bus.bp_write32(self.addr + BTSDIO_OFFSET_HOST2BT_IN, self.h2b_write_pointer)
.await;
self.bt_toggle_intr(bus).await;
self.tx_chan.receive_done();
}
async fn bt_has_work(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) -> bool {
let int_status = bus.bp_read32(CHIP.sdiod_core_base_address + SDIO_INT_STATUS).await;
if int_status & I_HMB_FC_CHANGE != 0 {
bus.bp_write32(
CHIP.sdiod_core_base_address + SDIO_INT_STATUS,
int_status & I_HMB_FC_CHANGE,
)
.await;
return true;
}
return false;
}
pub(crate) async fn handle_irq(&mut self, bus: &mut Bus<impl OutputPin, impl SpiBusCyw43>) {
if self.bt_has_work(bus).await {
loop {
// Check if we have data.
let write_pointer = bus.bp_read32(self.addr + BTSDIO_OFFSET_BT2HOST_IN).await;
let available = write_pointer.wrapping_sub(self.b2h_read_pointer) % BTSDIO_FWBUF_SIZE;
if available == 0 {
break;
}
// read header
let mut header = [0u8; 4];
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;
bus.bp_read(addr, &mut header).await;
// calc length
let len = header[0] as u32 | ((header[1]) as u32) << 8 | ((header[2]) as u32) << 16;
let rounded_len = round_up(len, 4);
if available < 4 + rounded_len {
warn!("ringbuf data not enough for a full packet?");
break;
}
self.b2h_read_pointer = (self.b2h_read_pointer + 4) % BTSDIO_FWBUF_SIZE;
// Obtain a buf from the channel.
let buf = self.rx_chan.send().await;
buf.buf[0] = header[3]; // hci packet type
let payload = &mut buf.buf[1..][..rounded_len as usize];
if self.b2h_read_pointer as usize + payload.len() > BTSDIO_FWBUF_SIZE as usize {
// wraparound
let n = BTSDIO_FWBUF_SIZE - self.b2h_read_pointer;
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;
bus.bp_read(addr, &mut payload[..n as usize]).await;
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF;
bus.bp_read(addr, &mut payload[n as usize..]).await;
} else {
// no wraparound
let addr = self.addr + BTSDIO_OFFSET_HOST_READ_BUF + self.b2h_read_pointer;
bus.bp_read(addr, payload).await;
}
self.b2h_read_pointer = (self.b2h_read_pointer + payload.len() as u32) % BTSDIO_FWBUF_SIZE;
bus.bp_write32(self.addr + BTSDIO_OFFSET_BT2HOST_OUT, self.b2h_read_pointer)
.await;
buf.len = 1 + len as usize;
debug!("HCI rx: {:02x}", crate::fmt::Bytes(&buf.buf[..buf.len]));
self.rx_chan.send_done();
self.bt_toggle_intr(bus).await;
}
}
}
}
impl<'d> embedded_io_async::ErrorType for BtDriver<'d> {
type Error = core::convert::Infallible;
}
impl<'d> bt_hci::transport::Transport for BtDriver<'d> {
fn read<'a>(&self, rx: &'a mut [u8]) -> impl Future<Output = Result<ControllerToHostPacket<'a>, Self::Error>> {
async {
let ch = &mut *self.rx.borrow_mut();
let buf = ch.receive().await;
let n = buf.len;
assert!(n < rx.len());
rx[..n].copy_from_slice(&buf.buf[..n]);
ch.receive_done();
let kind = PacketKind::from_hci_bytes_complete(&rx[..1]).unwrap();
let (res, _) = ControllerToHostPacket::from_hci_bytes_with_kind(kind, &rx[1..n]).unwrap();
Ok(res)
}
}
/// Write a complete HCI packet from the tx buffer
fn write<T: HostToControllerPacket>(&self, val: &T) -> impl Future<Output = Result<(), Self::Error>> {
async {
let ch = &mut *self.tx.borrow_mut();
let buf = ch.send().await;
let buf_len = buf.buf.len();
let mut slice = &mut buf.buf[..];
WithIndicator::new(val).write_hci(&mut slice).unwrap();
buf.len = buf_len - slice.len();
ch.send_done();
Ok(())
}
}
}

View File

@ -4,7 +4,7 @@ use embedded_hal_1::digital::OutputPin;
use futures::FutureExt;
use crate::consts::*;
use crate::slice8_mut;
use crate::util::slice8_mut;
/// Custom Spi Trait that _only_ supports the bus operation of the cyw43
/// Implementors are expected to hold the CS pin low during an operation.
@ -48,44 +48,91 @@ where
}
}
pub async fn init(&mut self) {
pub async fn init(&mut self, bluetooth_enabled: bool) {
// Reset
trace!("WL_REG off/on");
self.pwr.set_low().unwrap();
Timer::after_millis(20).await;
self.pwr.set_high().unwrap();
Timer::after_millis(250).await;
trace!("read REG_BUS_TEST_RO");
while self
.read32_swapped(REG_BUS_TEST_RO)
.read32_swapped(FUNC_BUS, REG_BUS_TEST_RO)
.inspect(|v| trace!("{:#x}", v))
.await
!= FEEDBEAD
{}
self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await;
let val = self.read32_swapped(REG_BUS_TEST_RW).await;
trace!("write REG_BUS_TEST_RW");
self.write32_swapped(FUNC_BUS, REG_BUS_TEST_RW, TEST_PATTERN).await;
let val = self.read32_swapped(FUNC_BUS, REG_BUS_TEST_RW).await;
trace!("{:#x}", val);
assert_eq!(val, TEST_PATTERN);
let val = self.read32_swapped(REG_BUS_CTRL).await;
trace!("read REG_BUS_CTRL");
let val = self.read32_swapped(FUNC_BUS, REG_BUS_CTRL).await;
trace!("{:#010b}", (val & 0xff));
// 32-bit word length, little endian (which is the default endianess).
// TODO: C library is uint32_t val = WORD_LENGTH_32 | HIGH_SPEED_MODE| ENDIAN_BIG | INTERRUPT_POLARITY_HIGH | WAKE_UP | 0x4 << (8 * SPI_RESPONSE_DELAY) | INTR_WITH_STATUS << (8 * SPI_STATUS_ENABLE);
trace!("write REG_BUS_CTRL");
self.write32_swapped(
FUNC_BUS,
REG_BUS_CTRL,
WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP | STATUS_ENABLE | INTERRUPT_WITH_STATUS,
WORD_LENGTH_32
| HIGH_SPEED
| INTERRUPT_POLARITY_HIGH
| WAKE_UP
| 0x4 << (8 * REG_BUS_RESPONSE_DELAY)
| STATUS_ENABLE << (8 * REG_BUS_STATUS_ENABLE)
| INTR_WITH_STATUS << (8 * REG_BUS_STATUS_ENABLE),
)
.await;
trace!("read REG_BUS_CTRL");
let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await;
trace!("{:#b}", val);
// TODO: C doesn't do this? i doubt it messes anything up
trace!("read REG_BUS_TEST_RO");
let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await;
trace!("{:#x}", val);
assert_eq!(val, FEEDBEAD);
// TODO: C doesn't do this? i doubt it messes anything up
trace!("read REG_BUS_TEST_RW");
let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await;
trace!("{:#x}", val);
assert_eq!(val, TEST_PATTERN);
trace!("write SPI_RESP_DELAY_F1 CYW43_BACKPLANE_READ_PAD_LEN_BYTES");
self.write8(FUNC_BUS, SPI_RESP_DELAY_F1, WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE)
.await;
// TODO: Make sure error interrupt bits are clear?
// cyw43_write_reg_u8(self, BUS_FUNCTION, SPI_INTERRUPT_REGISTER, DATA_UNAVAILABLE | COMMAND_ERROR | DATA_ERROR | F1_OVERFLOW) != 0)
trace!("Make sure error interrupt bits are clear");
self.write8(
FUNC_BUS,
REG_BUS_INTERRUPT,
(IRQ_DATA_UNAVAILABLE | IRQ_COMMAND_ERROR | IRQ_DATA_ERROR | IRQ_F1_OVERFLOW) as u8,
)
.await;
// Enable a selection of interrupts
// TODO: why not all of these F2_F3_FIFO_RD_UNDERFLOW | F2_F3_FIFO_WR_OVERFLOW | COMMAND_ERROR | DATA_ERROR | F2_PACKET_AVAILABLE | F1_OVERFLOW | F1_INTR
trace!("enable a selection of interrupts");
let mut val = IRQ_F2_F3_FIFO_RD_UNDERFLOW
| IRQ_F2_F3_FIFO_WR_OVERFLOW
| IRQ_COMMAND_ERROR
| IRQ_DATA_ERROR
| IRQ_F2_PACKET_AVAILABLE
| IRQ_F1_OVERFLOW;
if bluetooth_enabled {
val = val | IRQ_F1_INTR;
}
self.write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, val).await;
}
pub async fn wlan_read(&mut self, buf: &mut [u32], len_in_u8: u32) {
@ -107,6 +154,8 @@ where
#[allow(unused)]
pub async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) {
trace!("bp_read addr = {:08x}", addr);
// It seems the HW force-aligns the addr
// to 2 if data.len() >= 2
// to 4 if data.len() >= 4
@ -140,6 +189,8 @@ where
}
pub async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) {
trace!("bp_write addr = {:08x}", addr);
// It seems the HW force-aligns the addr
// to 2 if data.len() >= 2
// to 4 if data.len() >= 4
@ -196,23 +247,32 @@ where
}
async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 {
trace!("backplane_readn addr = {:08x} len = {}", addr, len);
self.backplane_set_window(addr).await;
let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;
if len == 4 {
bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG
bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG;
}
self.readn(FUNC_BACKPLANE, bus_addr, len).await
let val = self.readn(FUNC_BACKPLANE, bus_addr, len).await;
trace!("backplane_readn addr = {:08x} len = {} val = {:08x}", addr, len, val);
return val;
}
async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) {
trace!("backplane_writen addr = {:08x} len = {} val = {:08x}", addr, len, val);
self.backplane_set_window(addr).await;
let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK;
if len == 4 {
bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG
bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG;
}
self.writen(FUNC_BACKPLANE, bus_addr, val, len).await
self.writen(FUNC_BACKPLANE, bus_addr, val, len).await;
}
async fn backplane_set_window(&mut self, addr: u32) {
@ -293,8 +353,8 @@ where
self.status = self.spi.cmd_write(&[cmd, val]).await;
}
async fn read32_swapped(&mut self, addr: u32) -> u32 {
let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4);
async fn read32_swapped(&mut self, func: u32, addr: u32) -> u32 {
let cmd = cmd_word(READ, INC_ADDR, func, addr, 4);
let cmd = swap16(cmd);
let mut buf = [0; 1];
@ -303,8 +363,8 @@ where
swap16(buf[0])
}
async fn write32_swapped(&mut self, addr: u32, val: u32) {
let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4);
async fn write32_swapped(&mut self, func: u32, addr: u32, val: u32) {
let cmd = cmd_word(WRITE, INC_ADDR, func, addr, 4);
let buf = [swap16(cmd), swap16(val)];
self.status = self.spi.cmd_write(&buf).await;

View File

@ -5,19 +5,33 @@ pub(crate) const FUNC_BACKPLANE: u32 = 1;
pub(crate) const FUNC_WLAN: u32 = 2;
pub(crate) const FUNC_BT: u32 = 3;
// Register addresses
pub(crate) const REG_BUS_CTRL: u32 = 0x0;
pub(crate) const REG_BUS_RESPONSE_DELAY: u32 = 0x1;
pub(crate) const REG_BUS_STATUS_ENABLE: u32 = 0x2;
pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status
pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask
pub(crate) const REG_BUS_STATUS: u32 = 0x8;
pub(crate) const REG_BUS_TEST_RO: u32 = 0x14;
pub(crate) const REG_BUS_TEST_RW: u32 = 0x18;
pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c;
// SPI_BUS_CONTROL Bits
pub(crate) const WORD_LENGTH_32: u32 = 0x1;
pub(crate) const ENDIAN_BIG: u32 = 0x2;
pub(crate) const CLOCK_PHASE: u32 = 0x4;
pub(crate) const CLOCK_POLARITY: u32 = 0x8;
pub(crate) const HIGH_SPEED: u32 = 0x10;
pub(crate) const INTERRUPT_HIGH: u32 = 1 << 5;
pub(crate) const WAKE_UP: u32 = 1 << 7;
pub(crate) const STATUS_ENABLE: u32 = 1 << 16;
pub(crate) const INTERRUPT_WITH_STATUS: u32 = 1 << 17;
pub(crate) const INTERRUPT_POLARITY_HIGH: u32 = 0x20;
pub(crate) const WAKE_UP: u32 = 0x80;
// SPI_STATUS_ENABLE bits
pub(crate) const STATUS_ENABLE: u32 = 0x01;
pub(crate) const INTR_WITH_STATUS: u32 = 0x02;
pub(crate) const RESP_DELAY_ALL: u32 = 0x04;
pub(crate) const DWORD_PKT_LEN_EN: u32 = 0x08;
pub(crate) const CMD_ERR_CHK_EN: u32 = 0x20;
pub(crate) const DATA_ERR_CHK_EN: u32 = 0x40;
// SPI_STATUS_REGISTER bits
pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001;
@ -51,6 +65,13 @@ pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C;
pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E;
pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F;
pub(crate) const I_HMB_SW_MASK: u32 = 0x000000f0;
pub(crate) const I_HMB_FC_CHANGE: u32 = 1 << 5;
pub(crate) const SDIO_INT_STATUS: u32 = 0x20;
pub(crate) const SDIO_INT_HOST_MASK: u32 = 0x24;
pub(crate) const SPI_F2_WATERMARK: u8 = 0x20;
pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000;
pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF;
pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000;
@ -92,17 +113,6 @@ pub(crate) const IRQ_F1_INTR: u16 = 0x2000;
pub(crate) const IRQ_F2_INTR: u16 = 0x4000;
pub(crate) const IRQ_F3_INTR: u16 = 0x8000;
pub(crate) const IOCTL_CMD_UP: u32 = 2;
pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30;
pub(crate) const IOCTL_CMD_DISASSOC: u32 = 52;
pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
pub(crate) const IOCTL_CMD_SET_AP: u32 = 118;
pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262;
pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268;
pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0;
pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1;
pub(crate) const CHANNEL_TYPE_DATA: u8 = 2;
@ -119,6 +129,44 @@ pub(crate) const WPA2_SECURITY: u32 = 0x00400000;
pub(crate) const MIN_PSK_LEN: usize = 8;
pub(crate) const MAX_PSK_LEN: usize = 64;
// Bluetooth firmware extraction constants.
pub(crate) const BTFW_ADDR_MODE_UNKNOWN: i32 = 0;
pub(crate) const BTFW_ADDR_MODE_EXTENDED: i32 = 1;
pub(crate) const BTFW_ADDR_MODE_SEGMENT: i32 = 2;
pub(crate) const BTFW_ADDR_MODE_LINEAR32: i32 = 3;
pub(crate) const BTFW_HEX_LINE_TYPE_DATA: u8 = 0;
pub(crate) const BTFW_HEX_LINE_TYPE_END_OF_DATA: u8 = 1;
pub(crate) const BTFW_HEX_LINE_TYPE_EXTENDED_SEGMENT_ADDRESS: u8 = 2;
pub(crate) const BTFW_HEX_LINE_TYPE_EXTENDED_ADDRESS: u8 = 4;
pub(crate) const BTFW_HEX_LINE_TYPE_ABSOLUTE_32BIT_ADDRESS: u8 = 5;
// Bluetooth constants.
pub(crate) const SPI_RESP_DELAY_F1: u32 = 0x001d;
pub(crate) const WHD_BUS_SPI_BACKPLANE_READ_PADD_SIZE: u8 = 4;
pub(crate) const BT2WLAN_PWRUP_WAKE: u32 = 3;
pub(crate) const BT2WLAN_PWRUP_ADDR: u32 = 0x640894;
pub(crate) const BT_CTRL_REG_ADDR: u32 = 0x18000c7c;
pub(crate) const HOST_CTRL_REG_ADDR: u32 = 0x18000d6c;
pub(crate) const WLAN_RAM_BASE_REG_ADDR: u32 = 0x18000d68;
pub(crate) const BTSDIO_REG_DATA_VALID_BITMASK: u32 = 1 << 1;
pub(crate) const BTSDIO_REG_BT_AWAKE_BITMASK: u32 = 1 << 8;
pub(crate) const BTSDIO_REG_WAKE_BT_BITMASK: u32 = 1 << 17;
pub(crate) const BTSDIO_REG_SW_RDY_BITMASK: u32 = 1 << 24;
pub(crate) const BTSDIO_REG_FW_RDY_BITMASK: u32 = 1 << 24;
pub(crate) const BTSDIO_FWBUF_SIZE: u32 = 0x1000;
pub(crate) const BTSDIO_OFFSET_HOST_WRITE_BUF: u32 = 0;
pub(crate) const BTSDIO_OFFSET_HOST_READ_BUF: u32 = BTSDIO_FWBUF_SIZE;
pub(crate) const BTSDIO_OFFSET_HOST2BT_IN: u32 = 0x00002000;
pub(crate) const BTSDIO_OFFSET_HOST2BT_OUT: u32 = 0x00002004;
pub(crate) const BTSDIO_OFFSET_BT2HOST_IN: u32 = 0x00002008;
pub(crate) const BTSDIO_OFFSET_BT2HOST_OUT: u32 = 0x0000200C;
// Security type (authentication and encryption types are combined using bit mask)
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, PartialEq)]
@ -317,3 +365,306 @@ impl core::fmt::Display for FormatInterrupt {
core::fmt::Debug::fmt(self, f)
}
}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u32)]
pub(crate) enum Ioctl {
GetMagic = 0,
GetVersion = 1,
Up = 2,
Down = 3,
GetLoop = 4,
SetLoop = 5,
Dump = 6,
GetMsglevel = 7,
SetMsglevel = 8,
GetPromisc = 9,
SetPromisc = 10,
GetRate = 12,
GetInstance = 14,
GetInfra = 19,
SetInfra = 20,
GetAuth = 21,
SetAuth = 22,
GetBssid = 23,
SetBssid = 24,
GetSsid = 25,
SetSsid = 26,
Restart = 27,
GetChannel = 29,
SetChannel = 30,
GetSrl = 31,
SetSrl = 32,
GetLrl = 33,
SetLrl = 34,
GetPlcphdr = 35,
SetPlcphdr = 36,
GetRadio = 37,
SetRadio = 38,
GetPhytype = 39,
DumpRate = 40,
SetRateParams = 41,
GetKey = 44,
SetKey = 45,
GetRegulatory = 46,
SetRegulatory = 47,
GetPassiveScan = 48,
SetPassiveScan = 49,
Scan = 50,
ScanResults = 51,
Disassoc = 52,
Reassoc = 53,
GetRoamTrigger = 54,
SetRoamTrigger = 55,
GetRoamDelta = 56,
SetRoamDelta = 57,
GetRoamScanPeriod = 58,
SetRoamScanPeriod = 59,
Evm = 60,
GetTxant = 61,
SetTxant = 62,
GetAntdiv = 63,
SetAntdiv = 64,
GetClosed = 67,
SetClosed = 68,
GetMaclist = 69,
SetMaclist = 70,
GetRateset = 71,
SetRateset = 72,
Longtrain = 74,
GetBcnprd = 75,
SetBcnprd = 76,
GetDtimprd = 77,
SetDtimprd = 78,
GetSrom = 79,
SetSrom = 80,
GetWepRestrict = 81,
SetWepRestrict = 82,
GetCountry = 83,
SetCountry = 84,
GetPm = 85,
SetPm = 86,
GetWake = 87,
SetWake = 88,
GetForcelink = 90,
SetForcelink = 91,
FreqAccuracy = 92,
CarrierSuppress = 93,
GetPhyreg = 94,
SetPhyreg = 95,
GetRadioreg = 96,
SetRadioreg = 97,
GetRevinfo = 98,
GetUcantdiv = 99,
SetUcantdiv = 100,
RReg = 101,
WReg = 102,
GetMacmode = 105,
SetMacmode = 106,
GetMonitor = 107,
SetMonitor = 108,
GetGmode = 109,
SetGmode = 110,
GetLegacyErp = 111,
SetLegacyErp = 112,
GetRxAnt = 113,
GetCurrRateset = 114,
GetScansuppress = 115,
SetScansuppress = 116,
GetAp = 117,
SetAp = 118,
GetEapRestrict = 119,
SetEapRestrict = 120,
ScbAuthorize = 121,
ScbDeauthorize = 122,
GetWdslist = 123,
SetWdslist = 124,
GetAtim = 125,
SetAtim = 126,
GetRssi = 127,
GetPhyantdiv = 128,
SetPhyantdiv = 129,
ApRxOnly = 130,
GetTxPathPwr = 131,
SetTxPathPwr = 132,
GetWsec = 133,
SetWsec = 134,
GetPhyNoise = 135,
GetBssInfo = 136,
GetPktcnts = 137,
GetLazywds = 138,
SetLazywds = 139,
GetBandlist = 140,
GetBand = 141,
SetBand = 142,
ScbDeauthenticate = 143,
GetShortslot = 144,
GetShortslotOverride = 145,
SetShortslotOverride = 146,
GetShortslotRestrict = 147,
SetShortslotRestrict = 148,
GetGmodeProtection = 149,
GetGmodeProtectionOverride = 150,
SetGmodeProtectionOverride = 151,
Upgrade = 152,
GetIgnoreBcns = 155,
SetIgnoreBcns = 156,
GetScbTimeout = 157,
SetScbTimeout = 158,
GetAssoclist = 159,
GetClk = 160,
SetClk = 161,
GetUp = 162,
Out = 163,
GetWpaAuth = 164,
SetWpaAuth = 165,
GetUcflags = 166,
SetUcflags = 167,
GetPwridx = 168,
SetPwridx = 169,
GetTssi = 170,
GetSupRatesetOverride = 171,
SetSupRatesetOverride = 172,
GetProtectionControl = 178,
SetProtectionControl = 179,
GetPhylist = 180,
EncryptStrength = 181,
DecryptStatus = 182,
GetKeySeq = 183,
GetScanChannelTime = 184,
SetScanChannelTime = 185,
GetScanUnassocTime = 186,
SetScanUnassocTime = 187,
GetScanHomeTime = 188,
SetScanHomeTime = 189,
GetScanNprobes = 190,
SetScanNprobes = 191,
GetPrbRespTimeout = 192,
SetPrbRespTimeout = 193,
GetAtten = 194,
SetAtten = 195,
GetShmem = 196,
SetShmem = 197,
SetWsecTest = 200,
ScbDeauthenticateForReason = 201,
TkipCountermeasures = 202,
GetPiomode = 203,
SetPiomode = 204,
SetAssocPrefer = 205,
GetAssocPrefer = 206,
SetRoamPrefer = 207,
GetRoamPrefer = 208,
SetLed = 209,
GetLed = 210,
GetInterferenceMode = 211,
SetInterferenceMode = 212,
GetChannelQa = 213,
StartChannelQa = 214,
GetChannelSel = 215,
StartChannelSel = 216,
GetValidChannels = 217,
GetFakefrag = 218,
SetFakefrag = 219,
GetPwroutPercentage = 220,
SetPwroutPercentage = 221,
SetBadFramePreempt = 222,
GetBadFramePreempt = 223,
SetLeapList = 224,
GetLeapList = 225,
GetCwmin = 226,
SetCwmin = 227,
GetCwmax = 228,
SetCwmax = 229,
GetWet = 230,
SetWet = 231,
GetPub = 232,
GetKeyPrimary = 235,
SetKeyPrimary = 236,
GetAciArgs = 238,
SetAciArgs = 239,
UnsetCallback = 240,
SetCallback = 241,
GetRadar = 242,
SetRadar = 243,
SetSpectManagment = 244,
GetSpectManagment = 245,
WdsGetRemoteHwaddr = 246,
WdsGetWpaSup = 247,
SetCsScanTimer = 248,
GetCsScanTimer = 249,
MeasureRequest = 250,
Init = 251,
SendQuiet = 252,
Keepalive = 253,
SendPwrConstraint = 254,
UpgradeStatus = 255,
CurrentPwr = 256,
GetScanPassiveTime = 257,
SetScanPassiveTime = 258,
LegacyLinkBehavior = 259,
GetChannelsInCountry = 260,
GetCountryList = 261,
GetVar = 262,
SetVar = 263,
NvramGet = 264,
NvramSet = 265,
NvramDump = 266,
Reboot = 267,
SetWsecPmk = 268,
GetAuthMode = 269,
SetAuthMode = 270,
GetWakeentry = 271,
SetWakeentry = 272,
NdconfigItem = 273,
Nvotpw = 274,
Otpw = 275,
IovBlockGet = 276,
IovModulesGet = 277,
SoftReset = 278,
GetAllowMode = 279,
SetAllowMode = 280,
GetDesiredBssid = 281,
SetDesiredBssid = 282,
DisassocMyap = 283,
GetNbands = 284,
GetBandstates = 285,
GetWlcBssInfo = 286,
GetAssocInfo = 287,
GetOidPhy = 288,
SetOidPhy = 289,
SetAssocTime = 290,
GetDesiredSsid = 291,
GetChanspec = 292,
GetAssocState = 293,
SetPhyState = 294,
GetScanPending = 295,
GetScanreqPending = 296,
GetPrevRoamReason = 297,
SetPrevRoamReason = 298,
GetBandstatesPi = 299,
GetPhyState = 300,
GetBssWpaRsn = 301,
GetBssWpa2Rsn = 302,
GetBssBcnTs = 303,
GetIntDisassoc = 304,
SetNumPeers = 305,
GetNumBss = 306,
GetWsecPmk = 318,
GetRandomBytes = 319,
}
pub(crate) const WSEC_TKIP: u32 = 0x02;
pub(crate) const WSEC_AES: u32 = 0x04;
pub(crate) const AUTH_OPEN: u32 = 0x00;
pub(crate) const AUTH_SAE: u32 = 0x03;
pub(crate) const MFP_NONE: u32 = 0;
pub(crate) const MFP_CAPABLE: u32 = 1;
pub(crate) const MFP_REQUIRED: u32 = 2;
pub(crate) const WPA_AUTH_DISABLED: u32 = 0x0000;
pub(crate) const WPA_AUTH_WPA_PSK: u32 = 0x0004;
pub(crate) const WPA_AUTH_WPA2_PSK: u32 = 0x0080;
pub(crate) const WPA_AUTH_WPA3_SAE_PSK: u32 = 0x40000;

View File

@ -35,16 +35,19 @@ pub struct Control<'a> {
ioctl_state: &'a IoctlState,
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ScanType {
Active,
Passive,
}
#[derive(Clone)]
/// Scan options.
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct ScanOptions {
/// SSID to scan for.
pub ssid: Option<heapless::String<32>>,
/// If set to `None`, all APs will be returned. If set to `Some`, only APs
/// with the specified BSSID will be returned.
@ -72,6 +75,79 @@ impl Default for ScanOptions {
}
}
/// Authentication type, used in [`JoinOptions::auth`].
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum JoinAuth {
/// Open network
Open,
/// WPA only
Wpa,
/// WPA2 only
Wpa2,
/// WPA3 only
Wpa3,
/// WPA2 + WPA3
Wpa2Wpa3,
}
/// Options for [`Control::join`].
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct JoinOptions<'a> {
/// Authentication type. Default `Wpa2Wpa3`.
pub auth: JoinAuth,
/// Enable TKIP encryption. Default false.
pub cipher_tkip: bool,
/// Enable AES encryption. Default true.
pub cipher_aes: bool,
/// Passphrase. Default empty.
pub passphrase: &'a [u8],
/// If false, `passphrase` is the human-readable passphrase string.
/// If true, `passphrase` is the result of applying the PBKDF2 hash to the
/// passphrase string. This makes it possible to avoid storing unhashed passwords.
///
/// This is not compatible with WPA3.
/// Default false.
pub passphrase_is_prehashed: bool,
}
impl<'a> JoinOptions<'a> {
/// Create a new `JoinOptions` for joining open networks.
pub fn new_open() -> Self {
Self {
auth: JoinAuth::Open,
cipher_tkip: false,
cipher_aes: false,
passphrase: &[],
passphrase_is_prehashed: false,
}
}
/// Create a new `JoinOptions` for joining encrypted networks.
///
/// Defaults to supporting WPA2+WPA3 with AES only, you may edit
/// the returned options to change this.
pub fn new(passphrase: &'a [u8]) -> Self {
let mut this = Self::default();
this.passphrase = passphrase;
this
}
}
impl<'a> Default for JoinOptions<'a> {
fn default() -> Self {
Self {
auth: JoinAuth::Wpa2Wpa3,
cipher_tkip: false,
cipher_aes: true,
passphrase: &[],
passphrase_is_prehashed: false,
}
}
}
impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self {
Self {
@ -81,8 +157,7 @@ impl<'a> Control<'a> {
}
}
/// Initialize WiFi controller.
pub async fn init(&mut self, clm: &[u8]) {
async fn load_clm(&mut self, clm: &[u8]) {
const CHUNK_SIZE: usize = 1024;
debug!("Downloading CLM...");
@ -108,12 +183,17 @@ impl<'a> Control<'a> {
buf[0..8].copy_from_slice(b"clmload\x00");
buf[8..20].copy_from_slice(&header.to_bytes());
buf[20..][..chunk.len()].copy_from_slice(&chunk);
self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()])
self.ioctl(IoctlType::Set, Ioctl::SetVar, 0, &mut buf[..8 + 12 + chunk.len()])
.await;
}
// check clmload ok
assert_eq!(self.get_iovar_u32("clmload_status").await, 0);
}
/// Initialize WiFi controller.
pub async fn init(&mut self, clm: &[u8]) {
self.load_clm(&clm).await;
debug!("Configuring misc stuff...");
@ -139,7 +219,7 @@ impl<'a> Control<'a> {
Timer::after_millis(100).await;
// Set antenna to chip antenna
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
self.ioctl_set_u32(Ioctl::SetAntdiv, 0, 0).await;
self.set_iovar_u32("bus:txglom", 0).await;
Timer::after_millis(100).await;
@ -177,24 +257,24 @@ impl<'a> Control<'a> {
Timer::after_millis(100).await;
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
self.ioctl_set_u32(Ioctl::SetGmode, 0, 1).await; // SET_GMODE = auto
self.ioctl_set_u32(Ioctl::SetBand, 0, 0).await; // SET_BAND = any
Timer::after_millis(100).await;
self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));
debug!("INIT DONE");
debug!("cyw43 control init done");
}
/// Set the WiFi interface up.
async fn up(&mut self) {
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
self.ioctl(IoctlType::Set, Ioctl::Up, 0, &mut []).await;
}
/// Set the interface down.
async fn down(&mut self) {
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
self.ioctl(IoctlType::Set, Ioctl::Down, 0, &mut []).await;
}
/// Set power management mode.
@ -207,17 +287,74 @@ impl<'a> Control<'a> {
self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await;
self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await;
}
self.ioctl_set_u32(86, 0, mode_num).await;
self.ioctl_set_u32(Ioctl::SetPm, 0, mode_num).await;
}
/// Join an unprotected network with the provided ssid.
pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> {
pub async fn join(&mut self, ssid: &str, options: JoinOptions<'_>) -> Result<(), Error> {
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
self.ioctl_set_u32(134, 0, 0).await; // wsec = open
self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await;
self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1
self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0)
if options.auth == JoinAuth::Open {
self.ioctl_set_u32(Ioctl::SetWsec, 0, 0).await;
self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await;
self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;
self.ioctl_set_u32(Ioctl::SetAuth, 0, 0).await;
self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, WPA_AUTH_DISABLED).await;
} else {
let mut wsec = 0;
if options.cipher_aes {
wsec |= WSEC_AES;
}
if options.cipher_tkip {
wsec |= WSEC_TKIP;
}
self.ioctl_set_u32(Ioctl::SetWsec, 0, wsec).await;
self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await;
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
Timer::after_millis(100).await;
let (wpa12, wpa3, auth, mfp, wpa_auth) = match options.auth {
JoinAuth::Open => unreachable!(),
JoinAuth::Wpa => (true, false, AUTH_OPEN, MFP_NONE, WPA_AUTH_WPA_PSK),
JoinAuth::Wpa2 => (true, false, AUTH_OPEN, MFP_CAPABLE, WPA_AUTH_WPA2_PSK),
JoinAuth::Wpa3 => (false, true, AUTH_SAE, MFP_REQUIRED, WPA_AUTH_WPA3_SAE_PSK),
JoinAuth::Wpa2Wpa3 => (true, true, AUTH_SAE, MFP_CAPABLE, WPA_AUTH_WPA3_SAE_PSK),
};
if wpa12 {
let mut flags = 0;
if !options.passphrase_is_prehashed {
flags |= 1;
}
let mut pfi = PassphraseInfo {
len: options.passphrase.len() as _,
flags,
passphrase: [0; 64],
};
pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);
Timer::after_millis(3).await;
self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())
.await;
}
if wpa3 {
let mut pfi = SaePassphraseInfo {
len: options.passphrase.len() as _,
passphrase: [0; 128],
};
pfi.passphrase[..options.passphrase.len()].copy_from_slice(options.passphrase);
Timer::after_millis(3).await;
self.set_iovar("sae_password", &pfi.to_bytes()).await;
}
self.ioctl_set_u32(Ioctl::SetInfra, 0, 1).await;
self.ioctl_set_u32(Ioctl::SetAuth, 0, auth).await;
self.set_iovar_u32("mfp", mfp).await;
self.ioctl_set_u32(Ioctl::SetWpaAuth, 0, wpa_auth).await;
}
let mut i = SsidInfo {
len: ssid.len() as _,
@ -228,69 +365,13 @@ impl<'a> Control<'a> {
self.wait_for_join(i).await
}
/// Join a protected network with the provided ssid and [`PassphraseInfo`].
async fn join_wpa2_passphrase_info(&mut self, ssid: &str, passphrase_info: &PassphraseInfo) -> Result<(), Error> {
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2
self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await;
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
Timer::after_millis(100).await;
self.ioctl(
IoctlType::Set,
IOCTL_CMD_SET_PASSPHRASE,
0,
&mut passphrase_info.to_bytes(),
)
.await; // WLC_SET_WSEC_PMK
self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1
self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open)
self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth
let mut i = SsidInfo {
len: ssid.len() as _,
ssid: [0; 32],
};
i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes());
self.wait_for_join(i).await
}
/// Join a protected network with the provided ssid and passphrase.
pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
let mut pfi = PassphraseInfo {
len: passphrase.len() as _,
flags: 1,
passphrase: [0; 64],
};
pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes());
self.join_wpa2_passphrase_info(ssid, &pfi).await
}
/// Join a protected network with the provided ssid and precomputed PSK.
pub async fn join_wpa2_psk(&mut self, ssid: &str, psk: &[u8; 32]) -> Result<(), Error> {
let mut pfi = PassphraseInfo {
len: psk.len() as _,
flags: 0,
passphrase: [0; 64],
};
pfi.passphrase[..psk.len()].copy_from_slice(psk);
self.join_wpa2_passphrase_info(ssid, &pfi).await
}
async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> {
self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]);
let mut subscriber = self.events.queue.subscriber().unwrap();
// the actual join operation starts here
// we make sure to enable events before so we don't miss any
// set_ssid
self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes())
.await;
self.ioctl(IoctlType::Set, Ioctl::SetSsid, 0, &mut i.to_bytes()).await;
// to complete the join, we wait for a SET_SSID event
// we also save the AUTH status for the user, it may be interesting
@ -351,7 +432,7 @@ impl<'a> Control<'a> {
self.up().await;
// Turn on AP mode
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
self.ioctl_set_u32(Ioctl::SetAp, 0, 1).await;
// Set SSID
let mut i = SsidInfoWithIndex {
@ -365,7 +446,7 @@ impl<'a> Control<'a> {
self.set_iovar("bsscfg:ssid", &i.to_bytes()).await;
// Set channel number
self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await;
self.ioctl_set_u32(Ioctl::SetChannel, 0, channel as u32).await;
// Set security
self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await;
@ -382,7 +463,7 @@ impl<'a> Control<'a> {
passphrase: [0; 64],
};
pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes());
self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes())
self.ioctl(IoctlType::Set, Ioctl::SetWsecPmk, 0, &mut pfi.to_bytes())
.await;
}
@ -399,7 +480,7 @@ impl<'a> Control<'a> {
self.set_iovar_u32x2("bss", 0, 0).await; // bss = BSS_DOWN
// Turn off AP mode
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 0).await;
self.ioctl_set_u32(Ioctl::SetAp, 0, 0).await;
// Temporarily set wifi down
self.down().await;
@ -478,11 +559,11 @@ impl<'a> Control<'a> {
}
async fn set_iovar(&mut self, name: &str, val: &[u8]) {
self.set_iovar_v::<64>(name, val).await
self.set_iovar_v::<196>(name, val).await
}
async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) {
debug!("set {} = {:02x}", name, Bytes(val));
debug!("iovar set {} = {:02x}", name, Bytes(val));
let mut buf = [0; BUFSIZE];
buf[..name.len()].copy_from_slice(name.as_bytes());
@ -490,13 +571,13 @@ impl<'a> Control<'a> {
buf[name.len() + 1..][..val.len()].copy_from_slice(val);
let total_len = name.len() + 1 + val.len();
self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len])
self.ioctl_inner(IoctlType::Set, Ioctl::SetVar, 0, &mut buf[..total_len])
.await;
}
// TODO this is not really working, it always returns all zeros.
async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize {
debug!("get {}", name);
debug!("iovar get {}", name);
let mut buf = [0; 64];
buf[..name.len()].copy_from_slice(name.as_bytes());
@ -504,7 +585,7 @@ impl<'a> Control<'a> {
let total_len = max(name.len() + 1, res.len());
let res_len = self
.ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len])
.ioctl_inner(IoctlType::Get, Ioctl::GetVar, 0, &mut buf[..total_len])
.await;
let out_len = min(res.len(), res_len);
@ -512,12 +593,20 @@ impl<'a> Control<'a> {
out_len
}
async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) {
async fn ioctl_set_u32(&mut self, cmd: Ioctl, iface: u32, val: u32) {
let mut buf = val.to_le_bytes();
self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await;
}
async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize {
async fn ioctl(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {
if kind == IoctlType::Set {
debug!("ioctl set {:?} iface {} = {:02x}", cmd, iface, Bytes(buf));
}
let n = self.ioctl_inner(kind, cmd, iface, buf).await;
n
}
async fn ioctl_inner(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {
struct CancelOnDrop<'a>(&'a IoctlState);
impl CancelOnDrop<'_> {
@ -609,7 +698,7 @@ impl<'a> Control<'a> {
}
/// Leave the wifi, with which we are currently associated.
pub async fn leave(&mut self) {
self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await;
self.ioctl(IoctlType::Set, Ioctl::Disassoc, 0, &mut []).await;
info!("Disassociated")
}

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -4,9 +4,10 @@ use core::task::{Poll, Waker};
use embassy_sync::waitqueue::WakerRegistration;
use crate::consts::Ioctl;
use crate::fmt::Bytes;
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum IoctlType {
Get = 0,
Set = 2,
@ -16,7 +17,7 @@ pub enum IoctlType {
pub struct PendingIoctl {
pub buf: *mut [u8],
pub kind: IoctlType,
pub cmd: u32,
pub cmd: Ioctl,
pub iface: u32,
}
@ -101,7 +102,7 @@ impl IoctlState {
self.state.set(IoctlStateInner::Done { resp_len: 0 });
}
pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize {
pub async fn do_ioctl(&self, kind: IoctlType, cmd: Ioctl, iface: u32, buf: &mut [u8]) -> usize {
self.state
.set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface }));
self.wake_runner();

View File

@ -8,18 +8,18 @@
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
#[cfg(feature = "bluetooth")]
mod bluetooth;
mod bus;
mod consts;
mod control;
mod countries;
mod events;
mod ioctl;
mod structs;
mod control;
mod nvram;
mod runner;
use core::slice;
mod structs;
mod util;
use embassy_net_driver_channel as ch;
use embedded_hal_1::digital::OutputPin;
@ -28,7 +28,9 @@ use ioctl::IoctlState;
use crate::bus::Bus;
pub use crate::bus::SpiBusCyw43;
pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, Scanner};
pub use crate::control::{
AddMulticastAddressError, Control, Error as ControlError, JoinAuth, JoinOptions, ScanOptions, Scanner,
};
pub use crate::runner::Runner;
pub use crate::structs::BssInfo;
@ -56,6 +58,7 @@ impl Core {
struct Chip {
arm_core_base_address: u32,
socsram_base_address: u32,
bluetooth_base_address: u32,
socsram_wrapper_base_address: u32,
sdiod_core_base_address: u32,
pmu_base_address: u32,
@ -83,6 +86,7 @@ const WRAPPER_REGISTER_OFFSET: u32 = 0x100000;
const CHIP: Chip = Chip {
arm_core_base_address: 0x18003000 + WRAPPER_REGISTER_OFFSET,
socsram_base_address: 0x18004000,
bluetooth_base_address: 0x19000000,
socsram_wrapper_base_address: 0x18004000 + WRAPPER_REGISTER_OFFSET,
sdiod_core_base_address: 0x18002000,
pmu_base_address: 0x18000000,
@ -107,6 +111,12 @@ const CHIP: Chip = Chip {
/// Driver state.
pub struct State {
ioctl_state: IoctlState,
net: NetState,
#[cfg(feature = "bluetooth")]
bt: bluetooth::BtState,
}
struct NetState {
ch: ch::State<MTU, 4, 4>,
events: Events,
}
@ -116,8 +126,12 @@ impl State {
pub fn new() -> Self {
Self {
ioctl_state: IoctlState::new(),
ch: ch::State::new(),
events: Events::new(),
net: NetState {
ch: ch::State::new(),
events: Events::new(),
},
#[cfg(feature = "bluetooth")]
bt: bluetooth::BtState::new(),
}
}
}
@ -225,21 +239,60 @@ 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.net.ch, ch::driver::HardwareAddress::Ethernet([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);
let mut runner = Runner::new(
ch_runner,
Bus::new(pwr, spi),
&state.ioctl_state,
&state.net.events,
#[cfg(feature = "bluetooth")]
None,
);
runner.init(firmware).await;
runner.init(firmware, None).await;
let control = Control::new(state_ch, &state.net.events, &state.ioctl_state);
(
device,
Control::new(state_ch, &state.events, &state.ioctl_state),
runner,
)
(device, control, runner)
}
fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
let len = x.len() * 4;
unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
/// Create a new instance of the CYW43 driver.
///
/// Returns a handle to the network device, control handle and a runner for driving the low level
/// stack.
#[cfg(feature = "bluetooth")]
pub async fn new_with_bluetooth<'a, PWR, SPI>(
state: &'a mut State,
pwr: PWR,
spi: SPI,
wifi_firmware: &[u8],
bluetooth_firmware: &[u8],
) -> (
NetDriver<'a>,
bluetooth::BtDriver<'a>,
Control<'a>,
Runner<'a, PWR, SPI>,
)
where
PWR: OutputPin,
SPI: SpiBusCyw43,
{
let (ch_runner, device) = ch::new(&mut state.net.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
let state_ch = ch_runner.state_runner();
let (bt_runner, bt_driver) = bluetooth::new(&mut state.bt);
let mut runner = Runner::new(
ch_runner,
Bus::new(pwr, spi),
&state.ioctl_state,
&state.net.events,
#[cfg(feature = "bluetooth")]
Some(bt_runner),
);
runner.init(wifi_firmware, Some(bluetooth_firmware)).await;
let control = Control::new(state_ch, &state.net.events, &state.ioctl_state);
(device, bt_driver, control, runner)
}

View File

@ -1,4 +1,4 @@
use embassy_futures::select::{select3, Either3};
use embassy_futures::select::{select4, Either4};
use embassy_net_driver_channel as ch;
use embassy_time::{block_for, Duration, Timer};
use embedded_hal_1::digital::OutputPin;
@ -11,7 +11,8 @@ use crate::fmt::Bytes;
use crate::ioctl::{IoctlState, IoctlType, PendingIoctl};
use crate::nvram::NVRAM;
use crate::structs::*;
use crate::{events, slice8_mut, Core, CHIP, MTU};
use crate::util::slice8_mut;
use crate::{events, Core, CHIP, MTU};
#[cfg(feature = "firmware-logs")]
struct LogState {
@ -36,7 +37,7 @@ impl Default for LogState {
/// Driver communicating with the WiFi chip.
pub struct Runner<'a, PWR, SPI> {
ch: ch::Runner<'a, MTU>,
bus: Bus<PWR, SPI>,
pub(crate) bus: Bus<PWR, SPI>,
ioctl_state: &'a IoctlState,
ioctl_id: u16,
@ -47,6 +48,9 @@ pub struct Runner<'a, PWR, SPI> {
#[cfg(feature = "firmware-logs")]
log: LogState,
#[cfg(feature = "bluetooth")]
pub(crate) bt: Option<crate::bluetooth::BtRunner<'a>>,
}
impl<'a, PWR, SPI> Runner<'a, PWR, SPI>
@ -59,6 +63,7 @@ where
bus: Bus<PWR, SPI>,
ioctl_state: &'a IoctlState,
events: &'a Events,
#[cfg(feature = "bluetooth")] bt: Option<crate::bluetooth::BtRunner<'a>>,
) -> Self {
Self {
ch,
@ -70,33 +75,52 @@ where
events,
#[cfg(feature = "firmware-logs")]
log: LogState::default(),
#[cfg(feature = "bluetooth")]
bt,
}
}
pub(crate) async fn init(&mut self, firmware: &[u8]) {
self.bus.init().await;
pub(crate) async fn init(&mut self, wifi_fw: &[u8], bt_fw: Option<&[u8]>) {
self.bus.init(bt_fw.is_some()).await;
// Init ALP (Active Low Power) clock
debug!("init alp");
self.bus
.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ)
.await;
debug!("set f2 watermark");
self.bus
.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 0x10)
.await;
let watermark = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK).await;
debug!("watermark = {:02x}", watermark);
assert!(watermark == 0x10);
debug!("waiting for clock...");
while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {}
debug!("clock ok");
// clear request for ALP
debug!("clear request for ALP");
self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0).await;
let chip_id = self.bus.bp_read16(0x1800_0000).await;
debug!("chip ID: {}", chip_id);
// Upload firmware.
self.core_disable(Core::WLAN).await;
self.core_disable(Core::SOCSRAM).await; // TODO: is this needed if we reset right after?
self.core_reset(Core::SOCSRAM).await;
// this is 4343x specific stuff: Disable remap for SRAM_3
self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await;
self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await;
let ram_addr = CHIP.atcm_ram_base_address;
debug!("loading fw");
self.bus.bp_write(ram_addr, firmware).await;
self.bus.bp_write(ram_addr, wifi_fw).await;
debug!("loading nvram");
// Round up to 4 bytes.
@ -116,10 +140,23 @@ where
self.core_reset(Core::WLAN).await;
assert!(self.core_is_up(Core::WLAN).await);
// wait until HT clock is available; takes about 29ms
debug!("wait for HT clock");
while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
// "Set up the interrupt mask and enable interrupts"
// self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await;
debug!("setup interrupt mask");
self.bus
.bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_HOST_MASK, I_HMB_SW_MASK)
.await;
// Set up the interrupt mask and enable interrupts
if bt_fw.is_some() {
debug!("bluetooth setup interrupt mask");
self.bus
.bp_write32(CHIP.sdiod_core_base_address + SDIO_INT_HOST_MASK, I_HMB_FC_CHANGE)
.await;
}
self.bus
.write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, IRQ_F2_PACKET_AVAILABLE)
@ -128,11 +165,11 @@ where
// "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped."
// Sounds scary...
self.bus
.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32)
.write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, SPI_F2_WATERMARK)
.await;
// wait for wifi startup
debug!("waiting for wifi init...");
// wait for F2 to be ready
debug!("waiting for F2 to be ready...");
while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {}
// Some random configs related to sleep.
@ -153,19 +190,27 @@ where
*/
// clear pulls
debug!("clear pad pulls");
self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await;
let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await;
// start HT clock
//self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await;
//debug!("waiting for HT clock...");
//while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
//debug!("clock ok");
self.bus
.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10)
.await; // SBSDIO_HT_AVAIL_REQ
debug!("waiting for HT clock...");
while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {}
debug!("clock ok");
#[cfg(feature = "firmware-logs")]
self.log_init().await;
debug!("wifi init done");
#[cfg(feature = "bluetooth")]
if let Some(bt_fw) = bt_fw {
self.bt.as_mut().unwrap().init_bluetooth(&mut self.bus, bt_fw).await;
}
debug!("cyw43 runner init done");
}
#[cfg(feature = "firmware-logs")]
@ -222,7 +267,7 @@ where
}
}
/// Run the
/// Run the CYW43 event handling loop.
pub async fn run(mut self) -> ! {
let mut buf = [0; 512];
loop {
@ -231,11 +276,27 @@ where
if self.has_credit() {
let ioctl = self.ioctl_state.wait_pending();
let tx = self.ch.tx_buf();
let wifi_tx = self.ch.tx_buf();
#[cfg(feature = "bluetooth")]
let bt_tx = async {
match &mut self.bt {
Some(bt) => bt.tx_chan.receive().await,
None => core::future::pending().await,
}
};
#[cfg(not(feature = "bluetooth"))]
let bt_tx = core::future::pending::<()>();
// interrupts aren't working yet for bluetooth. Do busy-polling instead.
// Note for this to work `ev` has to go last in the `select()`. It prefers
// first futures if they're ready, so other select branches don't get starved.`
#[cfg(feature = "bluetooth")]
let ev = core::future::ready(());
#[cfg(not(feature = "bluetooth"))]
let ev = self.bus.wait_for_event();
match select3(ioctl, tx, ev).await {
Either3::First(PendingIoctl {
match select4(ioctl, wifi_tx, bt_tx, ev).await {
Either4::First(PendingIoctl {
buf: iobuf,
kind,
cmd,
@ -244,7 +305,7 @@ where
self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }, &mut buf).await;
self.check_status(&mut buf).await;
}
Either3::Second(packet) => {
Either4::Second(packet) => {
trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
let buf8 = slice8_mut(&mut buf);
@ -298,8 +359,19 @@ where
self.ch.tx_done();
self.check_status(&mut buf).await;
}
Either3::Third(()) => {
Either4::Third(_) => {
#[cfg(feature = "bluetooth")]
self.bt.as_mut().unwrap().hci_write(&mut self.bus).await;
}
Either4::Fourth(()) => {
self.handle_irq(&mut buf).await;
// If we do busy-polling, make sure to yield.
// `handle_irq` will only do a 32bit read if there's no work to do, which is really fast.
// Depending on optimization level, it is possible that the 32-bit read finishes on
// first poll, so it never yields and we starve all other tasks.
#[cfg(feature = "bluetooth")]
embassy_futures::yield_now().await;
}
}
} else {
@ -314,17 +386,24 @@ where
async fn handle_irq(&mut self, buf: &mut [u32; 512]) {
// Receive stuff
let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await;
trace!("irq{}", FormatInterrupt(irq));
if irq != 0 {
trace!("irq{}", FormatInterrupt(irq));
}
if irq & IRQ_F2_PACKET_AVAILABLE != 0 {
self.check_status(buf).await;
}
if irq & IRQ_DATA_UNAVAILABLE != 0 {
// TODO what should we do here?
warn!("IRQ DATA_UNAVAILABLE, clearing...");
// this seems to be ignorable with no ill effects.
trace!("IRQ DATA_UNAVAILABLE, clearing...");
self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await;
}
#[cfg(feature = "bluetooth")]
if let Some(bt) = &mut self.bt {
bt.handle_irq(&mut self.bus).await;
}
}
/// Handle F2 events while status register is set
@ -481,7 +560,7 @@ where
self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0
}
async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
async fn send_ioctl(&mut self, kind: IoctlType, cmd: Ioctl, iface: u32, data: &[u8], buf: &mut [u32; 512]) {
let buf8 = slice8_mut(buf);
let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len();
@ -503,7 +582,7 @@ where
};
let cdc_header = CdcHeader {
cmd: cmd,
cmd: cmd as u32,
len: data.len() as _,
flags: kind as u16 | (iface as u16) << 12,
id: self.ioctl_id,

View File

@ -394,6 +394,15 @@ pub struct PassphraseInfo {
}
impl_bytes!(PassphraseInfo);
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C)]
pub struct SaePassphraseInfo {
pub len: u16,
pub passphrase: [u8; 128],
}
impl_bytes!(SaePassphraseInfo);
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(C)]

20
cyw43/src/util.rs Normal file
View File

@ -0,0 +1,20 @@
#![allow(unused)]
use core::slice;
pub(crate) fn slice8_mut(x: &mut [u32]) -> &mut [u8] {
let len = x.len() * 4;
unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) }
}
pub(crate) fn is_aligned(a: u32, x: u32) -> bool {
(a & (x - 1)) == 0
}
pub(crate) fn round_down(x: u32, a: u32) -> u32 {
x & !(a - 1)
}
pub(crate) fn round_up(x: u32, a: u32) -> u32 {
((x + a - 1) / a) * a
}

View File

@ -6,9 +6,9 @@ version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-executor = { version = "0.5.0", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.3.1", path = "../../../embassy-time", features = ["defmt"] }
embassy-nrf = { version = "0.1.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt"] }
embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
defmt = "0.3"
defmt-rtt = "0.3"

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
cortex-m = "0.7"
cortex-m-rt = "0.7"
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
embassy-executor = { version = "0.5.0", features = ["arch-cortex-m", "executor-thread"] }
embassy-executor = { version = "0.6.0", features = ["arch-cortex-m", "executor-thread"] }
defmt = "0.3.0"
defmt-rtt = "0.3.0"

View File

@ -4,7 +4,7 @@ So you've got one of the examples running, but what now? Let's go through a simp
== Main
The full example can be found link:https://github.com/embassy-rs/embassy/tree/master/docs/examples/basic[here].
The full example can be found link:https://github.com/embassy-rs/embassy/tree/main/docs/examples/basic[here].
NOTE: If youre using VS Code and rust-analyzer to view and edit the examples, you may need to make some changes to `.vscode/settings.json` to tell it which project were working on. Follow the instructions commented in that file to get rust-analyzer working correctly.

View File

@ -2,6 +2,8 @@
Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]!
* link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock]
** A hobbyist project building an alarm clock around a Pi Pico W complete with code, components list and enclosure design files.
* link:https://github.com/haobogu/rmk/[RMK: A feature-rich Rust keyboard firmware]
** RMK has built-in layer support, wireless(BLE) support, real-time key editing support using vial, and more!
** Targets STM32, RP2040, nRF52 and ESP32 MCUs

View File

@ -352,8 +352,23 @@ There are two main ways to handle concurrency in Embassy:
In general, either of these approaches will work. The main differences of these approaches are:
When using **separate tasks**, each task needs its own RAM allocation, so there's a little overhead for each task, so one task that does three things will likely be a little bit smaller than three tasks that do one thing (not a lot, probably a couple dozen bytes). In contrast, with **multiple futures in one task**, you don't need multiple task allocations, and it will generally be easier to share data, or use borrowed resources, inside of a single task.
When using **separate tasks**, each task needs its own RAM allocation, so there's a little overhead for each task, so one task that does three things will likely be a little bit smaller than three tasks that do one thing (not a lot, probably a couple dozen bytes). In contrast, with **multiple futures in one task**, you don't need multiple task allocations, and it will generally be easier to share data, or use borrowed resources, inside of a single task.
An example showcasing some methods for sharing things between tasks link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/sharing.rs[can be found here].
But when it comes to "waking" tasks, for example when a data transfer is complete or a button is pressed, it's faster to wake a dedicated task, because that task does not need to check which future is actually ready. `join` and `select` must check ALL of the futures they are managing to see which one (or which ones) are ready to do more work. This is because all Rust executors (like Embassy or Tokio) only have the ability to wake tasks, not specific futures. This means you will use slightly less CPU time juggling futures when using dedicated tasks.
Practically, there's not a LOT of difference either way - so go with what makes it easier for you and your code first, but there will be some details that are slightly different in each case.
== splitting peripherals resources between tasks
There are two ways to split resources between tasks, either manually assigned or by a convenient macro. See link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/assign_resources.rs[this example]
== My code/driver works in debug mode, but not release mode (or with LTO)
Issues like these while implementing drivers often fall into one of the following general causes, which are a good list of common errors to check for:
1. Some kind of race condition - the faster code means you miss an interrupt or something
2. Some kind of UB, if you have unsafe code, or something like DMA with fences missing
3. Some kind of hardware errata, or some hardware misconfiguration like wrong clock speeds
4. Some issue with an interrupt handler, either enabling, disabling, or re-enabling of interrupts when necessary
5. Some kind of async issue, like not registering wakers fully before checking flags, or not registering or pending wakers at the right time

View File

@ -1,6 +1,6 @@
= Starting a new project
Once youve successfully xref:getting_started.adoc[run some example projects], the next step is to make a standalone Embassy project.
Once youve successfully xref:#_getting_started[run some example projects], the next step is to make a standalone Embassy project.
== Tools for generating Embassy projects

View File

@ -1,6 +1,6 @@
= Embassy nRF HAL
The link:https://github.com/embassy-rs/embassy/tree/master/embassy-nrf[Embassy nRF HAL] is based on the PACs (Peripheral Access Crate) from link:https://github.com/nrf-rs/[nrf-rs].
The link:https://github.com/embassy-rs/embassy/tree/main/embassy-nrf[Embassy nRF HAL] is based on the PACs (Peripheral Access Crate) from link:https://github.com/nrf-rs/[nrf-rs].
== Timer driver

View File

@ -48,7 +48,7 @@ link:https://github.com/lora-rs/lora-rs[lora-rs] supports LoRa networking on a w
link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
=== Bootloader and DFU
link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
link:https://github.com/embassy-rs/embassy/tree/main/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
== What is DMA?
@ -77,3 +77,7 @@ For more reading material on async Rust and Embassy:
* link:https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown[Comparsion of FreeRTOS and Embassy]
* link:https://dev.to/apollolabsbin/series/20707[Tutorials]
* link:https://blog.drogue.io/firmware-updates-part-1/[Firmware Updates with Embassy]
Videos:
* link:https://www.youtube.com/watch?v=wni5h5vIPhU[From Zero to Async in Embedded Rust]

View File

@ -8,7 +8,7 @@ The following examples shows different ways to use the on-board LED on a Raspber
Using mutual exclusion is the simplest way to share a peripheral.
TIP: Dependencies needed to run this example link:/book/dev/basic_application.html#_the_cargo_toml[can be found here].
TIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].
[,rust]
----
use defmt::*;
@ -78,7 +78,7 @@ To indicate that the pin will be set to an Output. The `AnyPin` could have been
A channel is another way to ensure exclusive access to a resource. Using a channel is great in the cases where the access can happen at a later point in time, allowing you to enqueue operations and do other things.
TIP: Dependencies needed to run this example link:/book/dev/basic_application.html#_the_cargo_toml[can be found here].
TIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].
[,rust]
----
use defmt::*;
@ -126,3 +126,9 @@ async fn toggle_led(control: Sender<'static, ThreadModeRawMutex, LedState, 64>,
This example replaces the Mutex with a Channel, and uses another task (the main loop) to drive the LED. The advantage of this approach is that only a single task references the peripheral, separating concerns. However, using a Mutex has a lower overhead and might be necessary if you need to ensure
that the operation is completed before continuing to do other work in your task.
An example showcasing more methods for sharing link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/sharing.rs[can be found here].
== Sharing an I2C or SPI bus between multiple devices
An example of how to deal with multiple devices sharing a common I2C or SPI bus link:https://github.com/embassy-rs/embassy/blob/main/examples/rp/src/bin/shared_bus.rs[can be found here].

View File

@ -1,6 +1,6 @@
= Embassy STM32 HAL
The link:https://github.com/embassy-rs/embassy/tree/master/embassy-stm32[Embassy STM32 HAL] is based on the `stm32-metapac` project.
The link:https://github.com/embassy-rs/embassy/tree/main/embassy-stm32[Embassy STM32 HAL] is based on the `stm32-metapac` project.
== The infinite variant problem

View File

@ -16,7 +16,7 @@ The `embassy::time::Timer` type provides two timing methods.
An example of a delay is provided as follows:
TIP: Dependencies needed to run this example link:/book/dev/basic_application.html#_the_cargo_toml[can be found here].
TIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].
[,rust]
----
use embassy::executor::{task, Executor};
@ -41,7 +41,7 @@ that expect a generic delay implementation to be provided.
An example of how this can be used:
TIP: Dependencies needed to run this example link:/book/dev/basic_application.html#_the_cargo_toml[can be found here].
TIP: Dependencies needed to run this example link:#_the_cargo_toml[can be found here].
[,rust]
----
use embassy::executor::{task, Executor};

View File

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot-nrf"
version = "0.2.0"
version = "0.3.0"
description = "Bootloader lib for nRF chips"
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
@ -25,8 +25,8 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.17", optional = true }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", default-features = false }
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
embassy-nrf = { version = "0.2.0", path = "../embassy-nrf", default-features = false }
embassy-boot = { version = "0.3.0", path = "../embassy-boot" }
cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.1"

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot-rp"
version = "0.2.0"
version = "0.3.0"
description = "Bootloader lib for RP2040 chips"
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
@ -16,6 +16,7 @@ categories = [
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot-rp/src/"
target = "thumbv6m-none-eabi"
features = ["embassy-rp/rp2040"]
[lib]
@ -24,9 +25,9 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4", optional = true }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embassy-rp = { version = "0.1.0", path = "../embassy-rp", default-features = false }
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-rp = { version = "0.2.0", path = "../embassy-rp", default-features = false }
embassy-boot = { version = "0.3.0", path = "../embassy-boot" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7" }

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -26,7 +26,7 @@ log = { version = "0.4", optional = true }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false }
embassy-boot = { version = "0.2.0", path = "../embassy-boot" }
embassy-boot = { version = "0.3.0", path = "../embassy-boot" }
cortex-m = { version = "0.7.6" }
cortex-m-rt = { version = "0.7" }
embedded-storage = "0.3.1"

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -1,7 +1,7 @@
[package]
edition = "2021"
name = "embassy-boot"
version = "0.2.0"
version = "0.3.0"
description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks."
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
@ -27,8 +27,8 @@ features = ["defmt"]
defmt = { version = "0.3", optional = true }
digest = "0.10"
log = { version = "0.4", optional = true }
ed25519-dalek = { version = "2", default_features = false, features = ["digest"], optional = true }
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true }
embassy-embedded-hal = { version = "0.2.0", path = "../embassy-embedded-hal" }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embedded-storage = "0.3.1"
embedded-storage-async = { version = "0.4.1" }
@ -42,7 +42,7 @@ rand = "0.8"
futures = { version = "0.3", features = ["executor"] }
sha1 = "0.10.5"
critical-section = { version = "1.1.1", features = ["std"] }
ed25519-dalek = { version = "2", default_features = false, features = ["std", "rand_core", "digest"] }
ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] }
[features]
ed25519-dalek = ["dep:ed25519-dalek", "_verify"]

View File

@ -236,10 +236,10 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> BootLoader<ACTIVE, DFU, S
///
pub fn prepare_boot(&mut self, aligned_buf: &mut [u8]) -> Result<State, BootError> {
const {
assert!(Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32 == 0);
assert!(Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32 == 0);
assert!(Self::PAGE_SIZE % DFU::WRITE_SIZE as u32 == 0);
assert!(Self::PAGE_SIZE % DFU::ERASE_SIZE as u32 == 0);
core::assert!(Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32 == 0);
core::assert!(Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32 == 0);
core::assert!(Self::PAGE_SIZE % DFU::WRITE_SIZE as u32 == 0);
core::assert!(Self::PAGE_SIZE % DFU::ERASE_SIZE as u32 == 0);
}
// Ensure we have enough progress pages to store copy progress

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -0,0 +1,21 @@
# Changelog for embassy-embedded-hal
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).
## Unreleased
## 0.2.0 - 2024-08-05
- Add Clone derive to flash Partition in embassy-embedded-hal
- Add support for all word sizes to async shared spi
- Add Copy and 'static constraint to Word type in SPI structs
- Improve flexibility by introducing SPI word size as a generic parameter
- Allow changing Spi/I2cDeviceWithConfig's config at runtime
- Impl `MultiwriteNorFlash` for `BlockingAsync`
## 0.1.0 - 2024-01-10
- First release

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-embedded-hal"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy."
@ -29,7 +29,7 @@ default = ["time"]
[dependencies]
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embassy-time = { version = "0.3.1", path = "../embassy-time", optional = true }
embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
"unproven",
] }

View File

@ -18,6 +18,16 @@ pub struct Partition<'a, M: RawMutex, T: NorFlash> {
size: u32,
}
impl<'a, M: RawMutex, T: NorFlash> Clone for Partition<'a, M, T> {
fn clone(&self) -> Self {
Self {
flash: self.flash,
offset: self.offset,
size: self.size,
}
}
}
impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> {
/// Create a new partition
pub const fn new(flash: &'a Mutex<M, T>, offset: u32, size: u32) -> Self {

View File

@ -19,6 +19,16 @@ pub struct BlockingPartition<'a, M: RawMutex, T: NorFlash> {
size: u32,
}
impl<'a, M: RawMutex, T: NorFlash> Clone for BlockingPartition<'a, M, T> {
fn clone(&self) -> Self {
Self {
flash: self.flash,
offset: self.offset,
size: self.size,
}
}
}
impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> {
/// Create a new partition
pub const fn new(flash: &'a Mutex<M, RefCell<T>>, offset: u32, size: u32) -> Self {

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-executor-macros"
version = "0.4.1"
version = "0.5.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "macros for creating the entry point and tasks for embassy-executor"

View File

@ -70,7 +70,7 @@ pub fn wasm() -> TokenStream {
let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new()));
executor.start(|spawner| {
spawner.spawn(__embassy_main(spawner)).unwrap();
spawner.must_spawn(__embassy_main(spawner));
});
Ok(())

View File

@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
## 0.6.0 - 2024-08-05
- Add collapse_debuginfo to fmt.rs macros.
- initial support for avr
- use nightly waker_getters APIs
## 0.5.0 - 2024-01-11
- Updated to `embassy-time-driver 0.1`, `embassy-time-queue-driver 0.1`, compatible with `embassy-time v0.3` and higher.

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-executor"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage"
@ -33,7 +33,7 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true }
embassy-executor-macros = { version = "0.4.0", path = "../embassy-executor-macros" }
embassy-executor-macros = { version = "0.5.0", path = "../embassy-executor-macros" }
embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true }
embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true }
critical-section = "1.1"
@ -79,7 +79,7 @@ arch-cortex-m = ["_arch", "dep:cortex-m"]
## RISC-V 32
arch-riscv32 = ["_arch"]
## WASM
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "critical-section/std"]
## AVR
arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"]

View File

@ -8,8 +8,6 @@
use std::collections::HashSet;
use std::env;
use std::ffi::OsString;
use std::process::Command;
/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring
/// them (`cargo:rust-check-cfg=cfg(X)`).
@ -17,7 +15,6 @@ use std::process::Command;
pub struct CfgSet {
enabled: HashSet<String>,
declared: HashSet<String>,
emit_declared: bool,
}
impl CfgSet {
@ -25,7 +22,6 @@ impl CfgSet {
Self {
enabled: HashSet::new(),
declared: HashSet::new(),
emit_declared: is_rustc_nightly(),
}
}
@ -49,7 +45,7 @@ impl CfgSet {
///
/// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.
pub fn declare(&mut self, cfg: impl AsRef<str>) {
if self.declared.insert(cfg.as_ref().to_owned()) && self.emit_declared {
if self.declared.insert(cfg.as_ref().to_owned()) {
println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref());
}
}
@ -69,21 +65,6 @@ impl CfgSet {
}
}
fn is_rustc_nightly() -> bool {
if env::var_os("EMBASSY_FORCE_CHECK_CFG").is_some() {
return true;
}
let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
let output = Command::new(rustc)
.arg("--version")
.output()
.expect("failed to run `rustc --version`");
String::from_utf8_lossy(&output.stdout).contains("nightly")
}
/// Sets configs that describe the target platform.
pub fn set_target_cfgs(cfgs: &mut CfgSet) {
let target = env::var("TARGET").unwrap();

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -1,5 +1,4 @@
#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
#![cfg_attr(feature = "nightly", feature(waker_getters))]
#![allow(clippy::new_without_default)]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]

View File

@ -50,8 +50,7 @@ pub fn task_from_waker(waker: &Waker) -> TaskRef {
#[cfg(feature = "nightly")]
{
let raw_waker = waker.as_raw();
(raw_waker.vtable(), raw_waker.data())
(waker.vtable(), waker.data())
}
};

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -237,7 +237,7 @@ impl<Fut: Future, const N: usize> Future for SelectArray<Fut, N> {
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct SelectSlice<'a, Fut> {
inner: &'a mut [Fut],
inner: Pin<&'a mut [Fut]>,
}
/// Creates a new future which will select over a slice of futures.
@ -247,31 +247,26 @@ pub struct SelectSlice<'a, Fut> {
/// future that was ready.
///
/// If the slice is empty, the resulting future will be Pending forever.
pub fn select_slice<'a, Fut: Future>(slice: &'a mut [Fut]) -> SelectSlice<'a, Fut> {
pub fn select_slice<'a, Fut: Future>(slice: Pin<&'a mut [Fut]>) -> SelectSlice<'a, Fut> {
SelectSlice { inner: slice }
}
impl<'a, Fut: Future> Future for SelectSlice<'a, Fut> {
type Output = (Fut::Output, usize);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move,
// its elements also cannot move. Therefore it is safe to access `inner` and pin
// references to the contained futures.
let item = unsafe {
self.get_unchecked_mut()
.inner
.iter_mut()
.enumerate()
.find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) {
Poll::Pending => None,
Poll::Ready(e) => Some((i, e)),
})
};
match item {
Some((idx, res)) => Poll::Ready((res, idx)),
None => Poll::Pending,
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// Safety: refer to
// https://users.rust-lang.org/t/working-with-pinned-slices-are-there-any-structurally-pinning-vec-like-collection-types/50634/2
#[inline(always)]
fn pin_iter<T>(slice: Pin<&mut [T]>) -> impl Iterator<Item = Pin<&mut T>> {
unsafe { slice.get_unchecked_mut().iter_mut().map(|v| Pin::new_unchecked(v)) }
}
for (i, fut) in pin_iter(self.inner.as_mut()).enumerate() {
if let Poll::Ready(res) = fut.poll(cx) {
return Poll::Ready((res, i));
}
}
Poll::Pending
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-hal-internal"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY."

View File

@ -8,8 +8,6 @@
use std::collections::HashSet;
use std::env;
use std::ffi::OsString;
use std::process::Command;
/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring
/// them (`cargo:rust-check-cfg=cfg(X)`).
@ -17,7 +15,6 @@ use std::process::Command;
pub struct CfgSet {
enabled: HashSet<String>,
declared: HashSet<String>,
emit_declared: bool,
}
impl CfgSet {
@ -25,7 +22,6 @@ impl CfgSet {
Self {
enabled: HashSet::new(),
declared: HashSet::new(),
emit_declared: is_rustc_nightly(),
}
}
@ -49,7 +45,7 @@ impl CfgSet {
///
/// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid.
pub fn declare(&mut self, cfg: impl AsRef<str>) {
if self.declared.insert(cfg.as_ref().to_owned()) && self.emit_declared {
if self.declared.insert(cfg.as_ref().to_owned()) {
println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref());
}
}
@ -69,21 +65,6 @@ impl CfgSet {
}
}
fn is_rustc_nightly() -> bool {
if env::var_os("EMBASSY_FORCE_CHECK_CFG").is_some() {
return true;
}
let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
let output = Command::new(rustc)
.arg("--version")
.output()
.expect("failed to run `rustc --version`");
String::from_utf8_lossy(&output.stdout).contains("nightly")
}
/// Sets configs that describe the target platform.
pub fn set_target_cfgs(cfgs: &mut CfgSet) {
let target = env::var("TARGET").unwrap();

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -16,8 +16,8 @@ log = { version = "0.4", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
embedded-hal-async = { version = "1.0" }
embedded-hal-bus = { version = "0.1", features = ["async"] }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
bitfield = "0.14.0"

View File

@ -21,7 +21,7 @@ APL can be used in [`intrinsic safety applications/explosion hazardous areas`](h
## Supported SPI modes
`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf)
`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/wp-content/uploads/2023/12/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf)
Both modes support with and without additional CRC.
Currently only `Generic` SPI with or without CRC is supported.

View File

@ -16,7 +16,7 @@ const CRC8X_TABLE: [u8; 256] = [
0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,
];
/// Calculate the crc of a pease of data.
/// Calculate the crc of a piece of data.
pub fn crc8(data: &[u8]) -> u8 {
data.iter().fold(0, |crc, &byte| CRC8X_TABLE[usize::from(byte ^ crc)])
}

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -5,6 +5,13 @@ 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).
## Unreleased
## 0.3.0 - 2024-08-05
- Add collapse_debuginfo to fmt.rs macros.
- Update embassy-sync version
## 0.2.0 - 2023-10-18
- Update `embassy-net-driver` to v0.2

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-net-driver-channel"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -13,7 +13,7 @@ documentation = "https://docs.embassy.dev/embassy-net-enc28j60"
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -17,10 +17,10 @@ log = [ "dep:log" ]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
embassy-sync = { version = "0.6.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -1,3 +1,5 @@
#![allow(unused)]
use heapless::{String, Vec};
/// internal supporting structures for CtrlMsg

View File

@ -0,0 +1,38 @@
[package]
name = "embassy-net-nrf91"
version = "0.1.0"
edition = "2021"
description = "embassy-net driver for Nordic nRF91-series cellular modems"
keywords = ["embedded", "nrf91", "embassy-net", "cellular"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/embassy-rs/embassy"
documentation = "https://docs.embassy.dev/embassy-net-nrf91"
[features]
defmt = [ "dep:defmt", "heapless/defmt-03" ]
log = [ "dep:log" ]
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
nrf9160-pac = { version = "0.12.0" }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-sync = { version = "0.6.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"}
heapless = "0.8"
embedded-io = "0.6.1"
at-commands = "0.5.4"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/"
target = "thumbv7em-none-eabi"
features = ["defmt"]
[package.metadata.docs.rs]
features = ["defmt"]

View File

@ -0,0 +1,9 @@
# nRF91 `embassy-net` integration
[`embassy-net`](https://crates.io/crates/embassy-net) driver for Nordic nRF91-series cellular modems.
See the [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/nrf9160) directory for usage examples with the nRF9160.
## Interoperability
This crate can run on any executor.

View File

@ -0,0 +1,350 @@
//! Helper utility to configure a specific modem context.
use core::net::IpAddr;
use core::str::FromStr;
use at_commands::builder::CommandBuilder;
use at_commands::parser::CommandParser;
use embassy_time::{Duration, Timer};
use heapless::Vec;
/// Provides a higher level API for controlling a given context.
pub struct Control<'a> {
control: crate::Control<'a>,
cid: u8,
}
/// Configuration for a given context
pub struct Config<'a> {
/// Desired APN address.
pub apn: &'a [u8],
/// Desired authentication protocol.
pub auth_prot: AuthProt,
/// Credentials.
pub auth: Option<(&'a [u8], &'a [u8])>,
}
/// Authentication protocol.
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum AuthProt {
/// No authentication.
None = 0,
/// PAP authentication.
Pap = 1,
/// CHAP authentication.
Chap = 2,
}
/// Error returned by control.
#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// Not enough space for command.
BufferTooSmall,
/// Error parsing response from modem.
AtParseError,
/// Error parsing IP addresses.
AddrParseError,
}
impl From<at_commands::parser::ParseError> for Error {
fn from(_: at_commands::parser::ParseError) -> Self {
Self::AtParseError
}
}
/// Status of a given context.
#[derive(PartialEq, Debug)]
pub struct Status {
/// Attached to APN or not.
pub attached: bool,
/// IP if assigned.
pub ip: Option<IpAddr>,
/// Gateway if assigned.
pub gateway: Option<IpAddr>,
/// DNS servers if assigned.
pub dns: Vec<IpAddr, 2>,
}
#[cfg(feature = "defmt")]
impl defmt::Format for Status {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "attached: {}", self.attached);
if let Some(ip) = &self.ip {
defmt::write!(f, ", ip: {}", defmt::Debug2Format(&ip));
}
}
}
impl<'a> Control<'a> {
/// Create a new instance of a control handle for a given context.
///
/// Will wait for the modem to be initialized if not.
pub async fn new(control: crate::Control<'a>, cid: u8) -> Self {
control.wait_init().await;
Self { control, cid }
}
/// Perform a raw AT command
pub async fn at_command(&self, req: &[u8], resp: &mut [u8]) -> usize {
self.control.at_command(req, resp).await
}
/// Configures the modem with the provided config.
///
/// NOTE: This will disconnect the modem from any current APN and should not
/// be called if the configuration has not been changed.
///
/// After configuring, invoke [`enable()`] to activate the configuration.
pub async fn configure(&self, config: &Config<'_>) -> Result<(), Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CFUN")
.with_int_parameter(0)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CGDCONT")
.with_int_parameter(self.cid)
.with_string_parameter("IP")
.with_string_parameter(config.apn)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
// info!("RES1: {}", unsafe { core::str::from_utf8_unchecked(&buf[..n]) });
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
let mut op = CommandBuilder::create_set(&mut cmd, true)
.named("+CGAUTH")
.with_int_parameter(self.cid)
.with_int_parameter(config.auth_prot as u8);
if let Some((username, password)) = config.auth {
op = op.with_string_parameter(username).with_string_parameter(password);
}
let op = op.finish().map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
// info!("RES2: {}", unsafe { core::str::from_utf8_unchecked(&buf[..n]) });
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
Ok(())
}
/// Attach to the PDN
pub async fn attach(&self) -> Result<(), Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CGATT")
.with_int_parameter(1)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
Ok(())
}
/// Read current connectivity status for modem.
pub async fn detach(&self) -> Result<(), Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CGATT")
.with_int_parameter(0)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
Ok(())
}
async fn attached(&self) -> Result<bool, Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_query(&mut cmd, true)
.named("+CGATT")
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
let (res,) = CommandParser::parse(&buf[..n])
.expect_identifier(b"+CGATT: ")
.expect_int_parameter()
.expect_identifier(b"\r\nOK")
.finish()?;
Ok(res == 1)
}
/// Read current connectivity status for modem.
pub async fn status(&self) -> Result<Status, Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_query(&mut cmd, true)
.named("+CGATT")
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
let (res,) = CommandParser::parse(&buf[..n])
.expect_identifier(b"+CGATT: ")
.expect_int_parameter()
.expect_identifier(b"\r\nOK")
.finish()?;
let attached = res == 1;
if !attached {
return Ok(Status {
attached,
ip: None,
gateway: None,
dns: Vec::new(),
});
}
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CGPADDR")
.with_int_parameter(self.cid)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
let (_, ip1, _ip2) = CommandParser::parse(&buf[..n])
.expect_identifier(b"+CGPADDR: ")
.expect_int_parameter()
.expect_optional_string_parameter()
.expect_optional_string_parameter()
.expect_identifier(b"\r\nOK")
.finish()?;
let ip = if let Some(ip) = ip1 {
let ip = IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?;
Some(ip)
} else {
None
};
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CGCONTRDP")
.with_int_parameter(self.cid)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
let (_cid, _bid, _apn, _mask, gateway, dns1, dns2, _, _, _, _, _mtu) = CommandParser::parse(&buf[..n])
.expect_identifier(b"+CGCONTRDP: ")
.expect_int_parameter()
.expect_optional_int_parameter()
.expect_optional_string_parameter()
.expect_optional_string_parameter()
.expect_optional_string_parameter()
.expect_optional_string_parameter()
.expect_optional_string_parameter()
.expect_optional_int_parameter()
.expect_optional_int_parameter()
.expect_optional_int_parameter()
.expect_optional_int_parameter()
.expect_optional_int_parameter()
.expect_identifier(b"\r\nOK")
.finish()?;
let gateway = if let Some(ip) = gateway {
if ip.is_empty() {
None
} else {
Some(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)
}
} else {
None
};
let mut dns = Vec::new();
if let Some(ip) = dns1 {
dns.push(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)
.unwrap();
}
if let Some(ip) = dns2 {
dns.push(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)
.unwrap();
}
Ok(Status {
attached,
ip,
gateway,
dns,
})
}
async fn wait_attached(&self) -> Result<Status, Error> {
while !self.attached().await? {
Timer::after(Duration::from_secs(1)).await;
}
let status = self.status().await?;
Ok(status)
}
/// Disable modem
pub async fn disable(&self) -> Result<(), Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CFUN")
.with_int_parameter(0)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
Ok(())
}
/// Enable modem
pub async fn enable(&self) -> Result<(), Error> {
let mut cmd: [u8; 256] = [0; 256];
let mut buf: [u8; 256] = [0; 256];
let op = CommandBuilder::create_set(&mut cmd, true)
.named("+CFUN")
.with_int_parameter(1)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
// Make modem survive PDN detaches
let op = CommandBuilder::create_set(&mut cmd, true)
.named("%XPDNCFG")
.with_int_parameter(1)
.finish()
.map_err(|_| Error::BufferTooSmall)?;
let n = self.control.at_command(op, &mut buf).await;
CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
Ok(())
}
/// Run a control loop for this context, ensuring that reaattach is handled.
pub async fn run<F: Fn(&Status)>(&self, reattach: F) -> Result<(), Error> {
self.enable().await?;
let status = self.wait_attached().await?;
let mut fd = self.control.open_raw_socket().await;
reattach(&status);
loop {
if !self.attached().await? {
trace!("detached");
self.control.close_raw_socket(fd).await;
let status = self.wait_attached().await?;
trace!("attached");
fd = self.control.open_raw_socket().await;
reattach(&status);
}
Timer::after(Duration::from_secs(10)).await;
}
}
}

View File

@ -0,0 +1,274 @@
#![macro_use]
#![allow(unused)]
use core::fmt::{Debug, Display, LowerHex};
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
#[collapse_debuginfo(yes)]
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
};
}
#[collapse_debuginfo(yes)]
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[collapse_debuginfo(yes)]
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}
pub(crate) struct Bytes<'a>(pub &'a [u8]);
impl<'a> Debug for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> Display for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> LowerHex for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
#[cfg(feature = "defmt")]
impl<'a> defmt::Format for Bytes<'a> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{:02x}", self.0)
}
}

1046
embassy-net-nrf91/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embedded-io-async = { version = "0.6.1" }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
ppproto = { version = "0.1.2"}
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -12,8 +12,8 @@ documentation = "https://docs.embassy.dev/embassy-net-wiznet"
[dependencies]
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }

View File

@ -44,6 +44,8 @@ raw = ["smoltcp/socket-raw"]
tcp = ["smoltcp/socket-tcp"]
## Enable DNS support
dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"]
## Enable mDNS support
mdns = ["dns", "smoltcp/socket-mdns"]
## Enable DHCPv4 support
dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"]
## Enable DHCPv4 support with hostname
@ -72,7 +74,7 @@ smoltcp = { version = "0.11.0", default-features = false, features = [
] }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embedded-io-async = { version = "0.6.1" }

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -34,6 +34,8 @@ use embassy_sync::waitqueue::WakerRegistration;
use embassy_time::{Instant, Timer};
#[allow(unused_imports)]
use heapless::Vec;
#[cfg(feature = "dns")]
pub use smoltcp::config::DNS_MAX_SERVER_COUNT;
#[cfg(feature = "igmp")]
pub use smoltcp::iface::MulticastError;
#[allow(unused_imports)]
@ -413,8 +415,11 @@ impl<D: Driver> Stack<D> {
/// ## Example
/// ```ignore
/// let config = embassy_net::Config::dhcpv4(Default::default());
///// Init network stack
/// static RESOURCES: StaticCell<embassy_net::StackResources<2> = StaticCell::new();
/// // Init network stack
/// // NOTE: DHCP and DNS need one socket slot if enabled. This is why we're
/// // provisioning space for 3 sockets here: one for DHCP, one for DNS, and one for your code (e.g. TCP).
/// // If you use more sockets you must increase this. If you don't enable DHCP or DNS you can decrease it.
/// static RESOURCES: StaticCell<embassy_net::StackResources<3>> = StaticCell::new();
/// static STACK: StaticCell<embassy_net::Stack> = StaticCell::new();
/// let stack = &*STACK.init(embassy_net::Stack::new(
/// device,
@ -823,9 +828,17 @@ impl<D: Driver> Inner<D> {
// Apply DNS servers
#[cfg(feature = "dns")]
s.sockets
.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
.update_servers(&dns_servers[..]);
if !dns_servers.is_empty() {
let count = if dns_servers.len() > DNS_MAX_SERVER_COUNT {
warn!("Number of DNS servers exceeds DNS_MAX_SERVER_COUNT, truncating list.");
DNS_MAX_SERVER_COUNT
} else {
dns_servers.len()
};
s.sockets
.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
.update_servers(&dns_servers[..count]);
}
self.config_waker.wake();
}

View File

@ -79,6 +79,15 @@ impl<'a> TcpReader<'a> {
///
/// Returns how many bytes were read, or an error. If no data is available, it waits
/// until there is at least one byte available.
///
/// # Note
/// A return value of Ok(0) means that we have read all data and the remote
/// side has closed our receive half of the socket. The remote can no longer
/// send bytes.
///
/// The send half of the socket is still open. If you want to reconnect using
/// the socket you split this reader off the send half needs to be closed using
/// [`abort()`](TcpSocket::abort).
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.io.read(buf).await
}
@ -273,6 +282,9 @@ impl<'a> TcpSocket<'a> {
///
/// Returns how many bytes were read, or an error. If no data is available, it waits
/// until there is at least one byte available.
///
/// A return value of Ok(0) means that the socket was closed and is longer
/// able to receive any data.
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
self.io.read(buf).await
}
@ -297,6 +309,10 @@ impl<'a> TcpSocket<'a> {
///
/// If the timeout is set, the socket will be closed if no data is received for the
/// specified duration.
///
/// # Note:
/// Set a keep alive interval ([`set_keep_alive`] to prevent timeouts when
/// the remote could still respond.
pub fn set_timeout(&mut self, duration: Option<Duration>) {
self.io
.with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp)))
@ -308,6 +324,9 @@ impl<'a> TcpSocket<'a> {
/// the specified duration of inactivity.
///
/// If not set, the socket will not send keep-alive packets.
///
/// By setting a [`timeout`](Self::timeout) larger then the keep alive you
/// can detect a remote endpoint that no longer answers.
pub fn set_keep_alive(&mut self, interval: Option<Duration>) {
self.io
.with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp)))
@ -515,7 +534,7 @@ impl<'d> TcpIo<'d> {
async fn flush(&mut self) -> Result<(), Error> {
poll_fn(move |cx| {
self.with_mut(|s, _| {
let data_pending = s.send_queue() > 0;
let data_pending = (s.send_queue() > 0) && s.state() != tcp::State::Closed;
let fin_pending = matches!(
s.state(),
tcp::State::FinWait1 | tcp::State::Closing | tcp::State::LastAck
@ -660,12 +679,25 @@ pub mod client {
pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> {
stack: &'d Stack<D>,
state: &'d TcpClientState<N, TX_SZ, RX_SZ>,
socket_timeout: Option<Duration>,
}
impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> {
/// Create a new `TcpClient`.
pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self {
Self { stack, state }
Self {
stack,
state,
socket_timeout: None,
}
}
/// Set the timeout for each socket created by this `TcpClient`.
///
/// If the timeout is set, the socket will be closed if no data is received for the
/// specified duration.
pub fn set_timeout(&mut self, timeout: Option<Duration>) {
self.socket_timeout = timeout;
}
}
@ -691,6 +723,7 @@ pub mod client {
};
let remote_endpoint = (addr, remote.port());
let mut socket = TcpConnection::new(&self.stack, self.state)?;
socket.socket.set_timeout(self.socket_timeout.clone());
socket
.socket
.connect(remote_endpoint)

View File

@ -138,6 +138,35 @@ impl<'a> UdpSocket<'a> {
})
}
/// Receive a datagram with a zero-copy function.
///
/// 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 call the provided function
/// with the number of bytes received and the remote endpoint and return
/// `Poll::Ready` with the function's returned value.
pub async fn recv_from_with<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&[u8], UdpMetadata) -> R,
{
let mut f = Some(f);
poll_fn(move |cx| {
self.with_mut(|s, _| {
match s.recv() {
Ok((buffer, endpoint)) => Poll::Ready(unwrap!(f.take())(buffer, endpoint)),
Err(udp::RecvError::Truncated) => unreachable!(),
Err(udp::RecvError::Exhausted) => {
// socket buffer is empty wait until at least one byte has arrived
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.
@ -181,6 +210,40 @@ impl<'a> UdpSocket<'a> {
})
}
/// Send a datagram to the specified remote endpoint with a zero-copy function.
///
/// This method will wait until the buffer can fit the requested size before
/// calling the function to fill its contents.
///
/// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`
pub async fn send_to_with<T, F, R>(&mut self, size: usize, remote_endpoint: T, f: F) -> Result<R, SendError>
where
T: Into<UdpMetadata> + Copy,
F: FnOnce(&mut [u8]) -> R,
{
let mut f = Some(f);
poll_fn(move |cx| {
self.with_mut(|s, _| {
match s.send(size, remote_endpoint) {
Ok(buffer) => Poll::Ready(Ok(unwrap!(f.take())(buffer))),
Err(udp::SendError::BufferFull) => {
s.register_send_waker(cx.waker());
Poll::Pending
}
Err(udp::SendError::Unaddressable) => {
// If no sender/outgoing port is specified, there is not really "no route"
if s.endpoint().port == 0 {
Poll::Ready(Err(SendError::SocketNotBound))
} else {
Poll::Ready(Err(SendError::NoRoute))
}
}
}
})
})
.await
}
/// Returns the local endpoint of the socket.
pub fn endpoint(&self) -> IpListenEndpoint {
self.with(|s, _| s.endpoint())

View File

@ -7,24 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
- Drop `sealed` mod
- nrf52840: Add dcdc voltage parameter to configure REG0 regulator
- radio: Add support for IEEE 802.15.4 and BLE via radio peripheral
- spim: Reduce trace-level messages ("Copying SPIM tx buffer..")
- uart: Add support for rx- or tx-only BufferedUart
- uart: Implement splitting Rx/Tx
- spi: Allow specifying OutputDrive for SPI spins
- pdm: Fix gain register value derivation
- spim: Implement chunked DMA transfers
- spi: Add bounds checks for EasyDMA buffer size
- uarte: Add support for handling RX errors
- nrf51: Implement support for nrf51 chip
- pwm: Expose `duty` method
- pwm: Fix infinite loop
- spi: Add support for configuring bit order for bus
- pwm: Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method
- gpio: Drop GPIO Pin generics (API break)
- pwm: Allow specifying OutputDrive for PWM channels
## 0.2.0 - 2024-08-05
- Support for NRF chips:
- nrf51
- nrf9151
- Support for new peripherals:
- EGU
- radio - low-level support for IEEE 802.15.4 and BLE via radio peripheral
- Peripheral changes:
- gpio: Drop GPIO Pin generics (API break)
- pdm: Fix gain register value derivation
- pwm:
- Expose `duty` method
- Expose `pwm::PWM_CLK_HZ` and add `is_enabled` method
- Allow specifying OutputDrive for PWM channels
- Fix infinite loop
- spim:
- Reduce trace-level messages ("Copying SPIM tx buffer..")
- Support configuring bit order for bus
- Allow specifying OutputDrive for SPI pins
- Add bounds checks for EasyDMA buffer size
- Implement chunked DMA transfers
- uart:
- Add support for rx- or tx-only BufferedUart
- Implement splitting Rx/Tx
- Add support for handling RX errors
- Miscellaneous changes:
- Add `collapse_debuginfo` to fmt.rs macros.
- Drop `sealed` mod
- nrf52840: Add dcdc voltage parameter to configure REG0 regulator
## 0.1.0 - 2024-01-12

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-nrf"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers"
@ -40,6 +40,7 @@ rt = [
"nrf5340-app-pac?/rt",
"nrf5340-net-pac?/rt",
"nrf9160-pac?/rt",
"nrf9120-pac?/rt",
]
## Enable features requiring `embassy-time`
@ -96,9 +97,18 @@ nrf5340-app-ns = ["_nrf5340-app", "_ns"]
## nRF5340 network core
nrf5340-net = ["_nrf5340-net"]
## nRF9160 in Secure mode
nrf9160-s = ["_nrf9160", "_s"]
nrf9160-s = ["_nrf9160", "_s", "_nrf91"]
## nRF9160 in Non-Secure mode
nrf9160-ns = ["_nrf9160", "_ns"]
nrf9160-ns = ["_nrf9160", "_ns", "_nrf91"]
## The nRF9120 is the internal part number for the nRF9161 and nRF9151.
## nRF9120 in Secure mode
nrf9120-s = ["_nrf9120", "_s", "_nrf91"]
nrf9151-s = ["nrf9120-s"]
nrf9161-s = ["nrf9120-s"]
## nRF9120 in Non-Secure mode
nrf9120-ns = ["_nrf9120", "_ns", "_nrf91"]
nrf9151-ns = ["nrf9120-ns"]
nrf9161-ns = ["nrf9120-ns"]
# Features starting with `_` are for internal use only. They're not intended
# to be enabled by other crates, and are not covered by semver guarantees.
@ -107,8 +117,10 @@ _nrf5340-app = ["_nrf5340", "nrf5340-app-pac"]
_nrf5340-net = ["_nrf5340", "nrf5340-net-pac"]
_nrf5340 = ["_gpio-p1", "_dppi"]
_nrf9160 = ["nrf9160-pac", "_dppi"]
_nrf9120 = ["nrf9120-pac", "_dppi"]
_nrf52 = ["_ppi"]
_nrf51 = ["_ppi"]
_nrf91 = []
_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"]
@ -125,10 +137,10 @@ _nrf52832_anomaly_109 = []
[dependencies]
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
embassy-time = { version = "0.3.1", path = "../embassy-time", optional = true }
embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
@ -161,3 +173,4 @@ 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 }
nrf9120-pac = { version = "0.12.0", optional = true }

View File

@ -219,6 +219,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
/// # Panics
///
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
@ -254,6 +255,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
/// # Panics
///
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new_with_rtscts(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
@ -286,6 +288,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
)
}
#[allow(clippy::too_many_arguments)]
fn new_inner(
peri: PeripheralRef<'d, U>,
timer: PeripheralRef<'d, T>,
@ -355,6 +358,11 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
self.tx.write(buf).await
}
/// Try writing a buffer without waiting, returning how many bytes were written.
pub fn try_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.tx.try_write(buf)
}
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
pub async fn flush(&mut self) -> Result<(), Error> {
self.tx.flush().await
@ -479,6 +487,29 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> {
.await
}
/// Try writing a buffer without waiting, returning how many bytes were written.
pub fn try_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
//trace!("poll_write: {:?}", buf.len());
let s = U::buffered_state();
let mut tx = unsafe { s.tx_buf.writer() };
let tx_buf = tx.push_slice();
if tx_buf.is_empty() {
return Ok(0);
}
let n = min(tx_buf.len(), buf.len());
tx_buf[..n].copy_from_slice(&buf[..n]);
tx.push_done(n);
//trace!("poll_write: queued {:?}", n);
compiler_fence(Ordering::SeqCst);
U::Interrupt::pend();
Ok(n)
}
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
pub async fn flush(&mut self) -> Result<(), Error> {
poll_fn(move |cx| {
@ -534,6 +565,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
/// # Panics
///
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
@ -564,6 +596,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
/// # Panics
///
/// Panics if `rx_buffer.len()` is odd.
#[allow(clippy::too_many_arguments)]
pub fn new_with_rts(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
@ -590,6 +623,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
)
}
#[allow(clippy::too_many_arguments)]
fn new_inner(
peri: PeripheralRef<'d, U>,
timer: PeripheralRef<'d, T>,
@ -614,6 +648,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
this
}
#[allow(clippy::too_many_arguments)]
fn new_innerer(
peri: PeripheralRef<'d, U>,
timer: PeripheralRef<'d, T>,
@ -766,6 +801,12 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> {
rx.pop_done(amt);
U::regs().intenset.write(|w| w.rxstarted().set());
}
/// we are ready to read if there is data in the buffer
fn read_ready() -> Result<bool, Error> {
let state = U::buffered_state();
Ok(!state.rx_buf.is_empty())
}
}
impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarteRx<'a, U, T> {
@ -827,6 +868,18 @@ mod _embedded_io {
}
}
impl<'d, U: UarteInstance, T: TimerInstance + 'd> embedded_io_async::ReadReady for BufferedUarte<'d, U, T> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
BufferedUarteRx::<'d, U, T>::read_ready()
}
}
impl<'d, U: UarteInstance, T: TimerInstance + 'd> embedded_io_async::ReadReady for BufferedUarteRx<'d, U, T> {
fn read_ready(&mut self) -> Result<bool, Self::Error> {
Self::read_ready()
}
}
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> {
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
self.fill_buf().await

View File

@ -0,0 +1,430 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {
// The nRF9120 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf9120_pac::NVIC_PRIO_BITS;
#[cfg(feature="rt")]
#[doc(no_inline)]
pub use nrf9120_pac::interrupt;
#[doc(no_inline)]
pub use nrf9120_pac::{
Interrupt,
cc_host_rgf_s as cc_host_rgf,
clock_ns as clock,
cryptocell_s as cryptocell,
ctrl_ap_peri_s as ctrl_ap_peri,
dppic_ns as dppic,
egu0_ns as egu0,
ficr_s as ficr,
fpu_ns as fpu,
gpiote0_s as gpiote,
i2s_ns as i2s,
ipc_ns as ipc,
kmu_ns as kmu,
nvmc_ns as nvmc,
p0_ns as p0,
pdm_ns as pdm,
power_ns as power,
pwm0_ns as pwm0,
regulators_ns as regulators,
rtc0_ns as rtc0,
saadc_ns as saadc,
spim0_ns as spim0,
spis0_ns as spis0,
spu_s as spu,
tad_s as tad,
timer0_ns as timer0,
twim0_ns as twim0,
twis0_ns as twis0,
uarte0_ns as uarte0,
uicr_s as uicr,
vmc_ns as vmc,
wdt_ns as wdt,
};
/// Non-Secure mode (NS) peripherals
pub mod ns {
#[doc(no_inline)]
pub use nrf9120_pac::{
CLOCK_NS as CLOCK,
DPPIC_NS as DPPIC,
EGU0_NS as EGU0,
EGU1_NS as EGU1,
EGU2_NS as EGU2,
EGU3_NS as EGU3,
EGU4_NS as EGU4,
EGU5_NS as EGU5,
FPU_NS as FPU,
GPIOTE1_NS as GPIOTE1,
I2S_NS as I2S,
IPC_NS as IPC,
KMU_NS as KMU,
NVMC_NS as NVMC,
P0_NS as P0,
PDM_NS as PDM,
POWER_NS as POWER,
PWM0_NS as PWM0,
PWM1_NS as PWM1,
PWM2_NS as PWM2,
PWM3_NS as PWM3,
REGULATORS_NS as REGULATORS,
RTC0_NS as RTC0,
RTC1_NS as RTC1,
SAADC_NS as SAADC,
SPIM0_NS as SPIM0,
SPIM1_NS as SPIM1,
SPIM2_NS as SPIM2,
SPIM3_NS as SPIM3,
SPIS0_NS as SPIS0,
SPIS1_NS as SPIS1,
SPIS2_NS as SPIS2,
SPIS3_NS as SPIS3,
TIMER0_NS as TIMER0,
TIMER1_NS as TIMER1,
TIMER2_NS as TIMER2,
TWIM0_NS as TWIM0,
TWIM1_NS as TWIM1,
TWIM2_NS as TWIM2,
TWIM3_NS as TWIM3,
TWIS0_NS as TWIS0,
TWIS1_NS as TWIS1,
TWIS2_NS as TWIS2,
TWIS3_NS as TWIS3,
UARTE0_NS as UARTE0,
UARTE1_NS as UARTE1,
UARTE2_NS as UARTE2,
UARTE3_NS as UARTE3,
VMC_NS as VMC,
WDT_NS as WDT,
};
}
/// Secure mode (S) peripherals
pub mod s {
#[doc(no_inline)]
pub use nrf9120_pac::{
CC_HOST_RGF_S as CC_HOST_RGF,
CLOCK_S as CLOCK,
CRYPTOCELL_S as CRYPTOCELL,
CTRL_AP_PERI_S as CTRL_AP_PERI,
DPPIC_S as DPPIC,
EGU0_S as EGU0,
EGU1_S as EGU1,
EGU2_S as EGU2,
EGU3_S as EGU3,
EGU4_S as EGU4,
EGU5_S as EGU5,
FICR_S as FICR,
FPU as FPU,
GPIOTE0_S as GPIOTE0,
I2S_S as I2S,
IPC_S as IPC,
KMU_S as KMU,
NVMC_S as NVMC,
P0_S as P0,
PDM_S as PDM,
POWER_S as POWER,
PWM0_S as PWM0,
PWM1_S as PWM1,
PWM2_S as PWM2,
PWM3_S as PWM3,
REGULATORS_S as REGULATORS,
RTC0_S as RTC0,
RTC1_S as RTC1,
SAADC_S as SAADC,
SPIM0_S as SPIM0,
SPIM1_S as SPIM1,
SPIM2_S as SPIM2,
SPIM3_S as SPIM3,
SPIS0_S as SPIS0,
SPIS1_S as SPIS1,
SPIS2_S as SPIS2,
SPIS3_S as SPIS3,
SPU_S as SPU,
TAD_S as TAD,
TIMER0_S as TIMER0,
TIMER1_S as TIMER1,
TIMER2_S as TIMER2,
TWIM0_S as TWIM0,
TWIM1_S as TWIM1,
TWIM2_S as TWIM2,
TWIM3_S as TWIM3,
TWIS0_S as TWIS0,
TWIS1_S as TWIS1,
TWIS2_S as TWIS2,
TWIS3_S as TWIS3,
UARTE0_S as UARTE0,
UARTE1_S as UARTE1,
UARTE2_S as UARTE2,
UARTE3_S as UARTE3,
UICR_S as UICR,
VMC_S as VMC,
WDT_S as WDT,
};
}
#[cfg(feature = "_ns")]
pub use ns::*;
#[cfg(feature = "_s")]
pub use s::*;
}
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
pub const EASY_DMA_SIZE: usize = (1 << 13) - 1;
pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_internal::peripherals! {
// RTC
RTC0,
RTC1,
// WDT
WDT,
// NVMC
NVMC,
// UARTE, TWI & SPI
SERIAL0,
SERIAL1,
SERIAL2,
SERIAL3,
// SAADC
SAADC,
// PWM
PWM0,
PWM1,
PWM2,
PWM3,
// TIMER
TIMER0,
TIMER1,
TIMER2,
// GPIOTE
GPIOTE_CH0,
GPIOTE_CH1,
GPIOTE_CH2,
GPIOTE_CH3,
GPIOTE_CH4,
GPIOTE_CH5,
GPIOTE_CH6,
GPIOTE_CH7,
// PPI
PPI_CH0,
PPI_CH1,
PPI_CH2,
PPI_CH3,
PPI_CH4,
PPI_CH5,
PPI_CH6,
PPI_CH7,
PPI_CH8,
PPI_CH9,
PPI_CH10,
PPI_CH11,
PPI_CH12,
PPI_CH13,
PPI_CH14,
PPI_CH15,
PPI_GROUP0,
PPI_GROUP1,
PPI_GROUP2,
PPI_GROUP3,
PPI_GROUP4,
PPI_GROUP5,
// GPIO port 0
P0_00,
P0_01,
P0_02,
P0_03,
P0_04,
P0_05,
P0_06,
P0_07,
P0_08,
P0_09,
P0_10,
P0_11,
P0_12,
P0_13,
P0_14,
P0_15,
P0_16,
P0_17,
P0_18,
P0_19,
P0_20,
P0_21,
P0_22,
P0_23,
P0_24,
P0_25,
P0_26,
P0_27,
P0_28,
P0_29,
P0_30,
P0_31,
// PDM
PDM,
// EGU
EGU0,
EGU1,
EGU2,
EGU3,
EGU4,
EGU5,
}
impl_uarte!(SERIAL0, UARTE0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0);
impl_uarte!(SERIAL1, UARTE1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1);
impl_uarte!(SERIAL2, UARTE2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2);
impl_uarte!(SERIAL3, UARTE3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3);
impl_spim!(SERIAL0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0);
impl_spim!(SERIAL1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1);
impl_spim!(SERIAL2, SPIM2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2);
impl_spim!(SERIAL3, SPIM3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3);
impl_spis!(SERIAL0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0);
impl_spis!(SERIAL1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1);
impl_spis!(SERIAL2, SPIS2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2);
impl_spis!(SERIAL3, SPIS3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3);
impl_twim!(SERIAL0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0);
impl_twim!(SERIAL1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1);
impl_twim!(SERIAL2, TWIM2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2);
impl_twim!(SERIAL3, TWIM3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3);
impl_twis!(SERIAL0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0);
impl_twis!(SERIAL1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1);
impl_twis!(SERIAL2, TWIS2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2);
impl_twis!(SERIAL3, TWIS3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3);
impl_pwm!(PWM0, PWM0, PWM0);
impl_pwm!(PWM1, PWM1, PWM1);
impl_pwm!(PWM2, PWM2, PWM2);
impl_pwm!(PWM3, PWM3, PWM3);
impl_pdm!(PDM, PDM, PDM);
impl_timer!(TIMER0, TIMER0, TIMER0);
impl_timer!(TIMER1, TIMER1, TIMER1);
impl_timer!(TIMER2, TIMER2, TIMER2);
impl_pin!(P0_00, 0, 0);
impl_pin!(P0_01, 0, 1);
impl_pin!(P0_02, 0, 2);
impl_pin!(P0_03, 0, 3);
impl_pin!(P0_04, 0, 4);
impl_pin!(P0_05, 0, 5);
impl_pin!(P0_06, 0, 6);
impl_pin!(P0_07, 0, 7);
impl_pin!(P0_08, 0, 8);
impl_pin!(P0_09, 0, 9);
impl_pin!(P0_10, 0, 10);
impl_pin!(P0_11, 0, 11);
impl_pin!(P0_12, 0, 12);
impl_pin!(P0_13, 0, 13);
impl_pin!(P0_14, 0, 14);
impl_pin!(P0_15, 0, 15);
impl_pin!(P0_16, 0, 16);
impl_pin!(P0_17, 0, 17);
impl_pin!(P0_18, 0, 18);
impl_pin!(P0_19, 0, 19);
impl_pin!(P0_20, 0, 20);
impl_pin!(P0_21, 0, 21);
impl_pin!(P0_22, 0, 22);
impl_pin!(P0_23, 0, 23);
impl_pin!(P0_24, 0, 24);
impl_pin!(P0_25, 0, 25);
impl_pin!(P0_26, 0, 26);
impl_pin!(P0_27, 0, 27);
impl_pin!(P0_28, 0, 28);
impl_pin!(P0_29, 0, 29);
impl_pin!(P0_30, 0, 30);
impl_pin!(P0_31, 0, 31);
impl_ppi_channel!(PPI_CH0, 0 => configurable);
impl_ppi_channel!(PPI_CH1, 1 => configurable);
impl_ppi_channel!(PPI_CH2, 2 => configurable);
impl_ppi_channel!(PPI_CH3, 3 => configurable);
impl_ppi_channel!(PPI_CH4, 4 => configurable);
impl_ppi_channel!(PPI_CH5, 5 => configurable);
impl_ppi_channel!(PPI_CH6, 6 => configurable);
impl_ppi_channel!(PPI_CH7, 7 => configurable);
impl_ppi_channel!(PPI_CH8, 8 => configurable);
impl_ppi_channel!(PPI_CH9, 9 => configurable);
impl_ppi_channel!(PPI_CH10, 10 => configurable);
impl_ppi_channel!(PPI_CH11, 11 => configurable);
impl_ppi_channel!(PPI_CH12, 12 => configurable);
impl_ppi_channel!(PPI_CH13, 13 => configurable);
impl_ppi_channel!(PPI_CH14, 14 => configurable);
impl_ppi_channel!(PPI_CH15, 15 => configurable);
impl_saadc_input!(P0_13, ANALOG_INPUT0);
impl_saadc_input!(P0_14, ANALOG_INPUT1);
impl_saadc_input!(P0_15, ANALOG_INPUT2);
impl_saadc_input!(P0_16, ANALOG_INPUT3);
impl_saadc_input!(P0_17, ANALOG_INPUT4);
impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7);
impl_egu!(EGU0, EGU0, EGU0);
impl_egu!(EGU1, EGU1, EGU1);
impl_egu!(EGU2, EGU2, EGU2);
impl_egu!(EGU3, EGU3, EGU3);
impl_egu!(EGU4, EGU4, EGU4);
impl_egu!(EGU5, EGU5, EGU5);
embassy_hal_internal::interrupt_mod!(
SPU,
CLOCK_POWER,
SPIM0_SPIS0_TWIM0_TWIS0_UARTE0,
SPIM1_SPIS1_TWIM1_TWIS1_UARTE1,
SPIM2_SPIS2_TWIM2_TWIS2_UARTE2,
SPIM3_SPIS3_TWIM3_TWIS3_UARTE3,
GPIOTE0,
SAADC,
TIMER0,
TIMER1,
TIMER2,
RTC0,
RTC1,
WDT,
EGU0,
EGU1,
EGU2,
EGU3,
EGU4,
EGU5,
PWM0,
PWM1,
PWM2,
PDM,
PWM3,
I2S,
IPC,
FPU,
GPIOTE1,
KMU,
CRYPTOCELL,
);

View File

@ -90,19 +90,15 @@ macro_rules! todo {
};
}
#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*)
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}

View File

@ -534,11 +534,13 @@ mod eh02 {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
self.set_high();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(self.set_low())
self.set_low();
Ok(())
}
}
@ -580,11 +582,13 @@ mod eh02 {
type Error = Infallible;
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
self.set_high();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(self.set_low())
self.set_low();
Ok(())
}
}
@ -628,11 +632,13 @@ impl<'d> embedded_hal_1::digital::ErrorType for Output<'d> {
impl<'d> embedded_hal_1::digital::OutputPin for Output<'d> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
self.set_high();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(self.set_low())
self.set_low();
Ok(())
}
}
@ -665,11 +671,13 @@ impl<'d> embedded_hal_1::digital::InputPin for Flex<'d> {
impl<'d> embedded_hal_1::digital::OutputPin for Flex<'d> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
self.set_high();
Ok(())
}
fn set_low(&mut self) -> Result<(), Self::Error> {
Ok(self.set_low())
self.set_low();
Ok(())
}
}

View File

@ -53,9 +53,9 @@ pub enum OutputChannelPolarity {
fn regs() -> &'static pac::gpiote::RegisterBlock {
cfg_if::cfg_if! {
if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s"))] {
if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s", feature="nrf9120-s"))] {
unsafe { &*pac::GPIOTE0::ptr() }
} else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns"))] {
} else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns", feature="nrf9120-ns"))] {
unsafe { &*pac::GPIOTE1::ptr() }
} else {
unsafe { &*pac::GPIOTE::ptr() }
@ -81,9 +81,9 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
}
// Enable interrupts
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))]
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s", feature = "nrf9120-s"))]
let irq = interrupt::GPIOTE0;
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns", feature = "nrf9120-ns"))]
let irq = interrupt::GPIOTE1;
#[cfg(any(feature = "_nrf51", feature = "_nrf52", feature = "nrf5340-net"))]
let irq = interrupt::GPIOTE;
@ -96,14 +96,14 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
g.intenset.write(|w| w.port().set());
}
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))]
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s", feature = "nrf9120-s"))]
#[cfg(feature = "rt")]
#[interrupt]
fn GPIOTE0() {
unsafe { handle_gpiote_interrupt() };
}
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns", feature = "nrf9120-ns"))]
#[cfg(feature = "rt")]
#[interrupt]
fn GPIOTE1() {

View File

@ -24,8 +24,36 @@
feature = "nrf5340-net",
feature = "nrf9160-s",
feature = "nrf9160-ns",
feature = "nrf9120-s",
feature = "nrf9120-ns",
feature = "nrf9151-s",
feature = "nrf9151-ns",
feature = "nrf9161-s",
feature = "nrf9161-ns",
)))]
compile_error!("No chip feature activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840");
compile_error!(
"No chip feature activated. You must activate exactly one of the following features:
nrf51,
nrf52805,
nrf52810,
nrf52811,
nrf52820,
nrf52832,
nrf52833,
nrf52840,
nrf5340-app-s,
nrf5340-app-ns,
nrf5340-net,
nrf9160-s,
nrf9160-ns,
nrf9120-s,
nrf9120-ns,
nrf9151-s,
nrf9151-ns,
nrf9161-s,
nrf9161-ns,
"
);
#[cfg(all(feature = "reset-pin-as-gpio", not(feature = "_nrf52")))]
compile_error!("feature `reset-pin-as-gpio` is only valid for nRF52 series chips.");
@ -47,7 +75,7 @@ pub mod gpio;
pub mod gpiote;
// TODO: tested on other chips
#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340-app")))]
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))]
pub mod radio;
#[cfg(not(feature = "nrf51"))]
@ -62,7 +90,7 @@ pub mod nvmc;
feature = "nrf52833",
feature = "nrf52840",
feature = "_nrf5340-app",
feature = "_nrf9160"
feature = "_nrf91",
))]
pub mod pdm;
pub mod ppi;
@ -73,11 +101,11 @@ pub mod ppi;
feature = "_nrf5340-net"
)))]
pub mod pwm;
#[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))]
#[cfg(not(any(feature = "nrf51", feature = "_nrf91", feature = "_nrf5340-net")))]
pub mod qdec;
#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))]
pub mod qspi;
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
pub mod rng;
#[cfg(not(any(feature = "nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc;
@ -85,7 +113,7 @@ pub mod saadc;
pub mod spim;
#[cfg(not(feature = "nrf51"))]
pub mod spis;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
pub mod temp;
pub mod timer;
#[cfg(not(feature = "nrf51"))]
@ -116,6 +144,7 @@ pub mod wdt;
#[cfg_attr(feature = "_nrf5340-app", path = "chips/nrf5340_app.rs")]
#[cfg_attr(feature = "_nrf5340-net", path = "chips/nrf5340_net.rs")]
#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")]
#[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")]
mod chip;
/// Macro to bind interrupts to handlers.
@ -196,15 +225,15 @@ pub mod config {
/// Internal RC oscillator
InternalRC,
/// Synthesized from the high frequency clock source.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
Synthesized,
/// External source from xtal.
ExternalXtal,
/// External source from xtal with low swing applied.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
ExternalLowSwing,
/// External source from xtal with full swing applied.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
ExternalFullSwing,
}
@ -222,7 +251,7 @@ pub mod config {
}
/// Settings for enabling the built in DCDC converters.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
pub struct DcdcConfig {
/// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used.
#[cfg(feature = "nrf52840")]
@ -264,7 +293,7 @@ pub mod config {
}
/// Settings for enabling the built in DCDC converter.
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
pub struct DcdcConfig {
/// Config for the main rail, if disabled LDO will be used.
pub regmain: bool,
@ -298,7 +327,7 @@ pub mod config {
// xtals if they know they have them.
hfclk_source: HfclkSource::Internal,
lfclk_source: LfclkSource::InternalRC,
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
dcdc: DcdcConfig {
#[cfg(feature = "nrf52840")]
reg0: false,
@ -312,7 +341,7 @@ pub mod config {
regmain: false,
regradio: false,
},
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
dcdc: DcdcConfig { regmain: false },
#[cfg(feature = "gpiote")]
gpiote_interrupt_priority: crate::interrupt::Priority::P0,
@ -329,7 +358,7 @@ pub mod config {
}
}
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
#[allow(unused)]
mod consts {
pub const UICR_APPROTECT: *mut u32 = 0x00FF8000 as *mut u32;
@ -468,7 +497,7 @@ pub fn init(config: config::Config) -> Peripherals {
// UICR.APPROTECT = Enabled
let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_ENABLED);
needs_reset |= res == WriteResult::Written;
#[cfg(any(feature = "_nrf5340-app", feature = "_nrf9160"))]
#[cfg(any(feature = "_nrf5340-app", feature = "_nrf91"))]
{
let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_ENABLED);
needs_reset |= res == WriteResult::Written;
@ -552,7 +581,7 @@ pub fn init(config: config::Config) -> Peripherals {
}
// Configure LFCLK.
#[cfg(not(any(feature = "nrf51", feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "nrf51", feature = "_nrf5340", feature = "_nrf91")))]
match config.lfclk_source {
config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().rc()),
config::LfclkSource::Synthesized => r.lfclksrc.write(|w| w.src().synth()),
@ -572,7 +601,7 @@ pub fn init(config: config::Config) -> Peripherals {
w
}),
}
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
match config.lfclk_source {
config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().lfrc()),
config::LfclkSource::ExternalXtal => r.lfclksrc.write(|w| w.src().lfxo()),
@ -585,7 +614,7 @@ pub fn init(config: config::Config) -> Peripherals {
r.tasks_lfclkstart.write(|w| unsafe { w.bits(1) });
while r.events_lfclkstarted.read().bits() == 0 {}
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
{
// Setup DCDCs.
let pwr = unsafe { &*pac::POWER::ptr() };
@ -597,7 +626,7 @@ pub fn init(config: config::Config) -> Peripherals {
pwr.dcdcen.write(|w| w.dcdcen().set_bit());
}
}
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
{
// Setup DCDC.
let reg = unsafe { &*pac::REGULATORS::ptr() };
@ -629,7 +658,7 @@ pub fn init(config: config::Config) -> Peripherals {
time_driver::init(config.time_interrupt_priority);
// Disable UARTE (enabled by default for some reason)
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
unsafe {
(*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled());
(*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled());

View File

@ -60,23 +60,23 @@ impl<'d> Nvmc<'d> {
while p.ready.read().ready().is_busy() {}
}
#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))]
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340")))]
fn wait_ready_write(&mut self) {
self.wait_ready();
}
#[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))]
#[cfg(any(feature = "_nrf91", feature = "_nrf5340"))]
fn wait_ready_write(&mut self) {
let p = Self::regs();
while p.readynext.read().readynext().is_busy() {}
}
#[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))]
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340")))]
fn erase_page(&mut self, page_addr: u32) {
Self::regs().erasepage().write(|w| unsafe { w.bits(page_addr) });
}
#[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))]
#[cfg(any(feature = "_nrf91", feature = "_nrf5340"))]
fn erase_page(&mut self, page_addr: u32) {
let first_page_word = page_addr as *mut u32;
unsafe {

View File

@ -21,7 +21,7 @@ pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
feature = "_nrf91",
))]
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
use crate::{interrupt, Peripheral};
@ -121,7 +121,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
feature = "_nrf91",
))]
r.ratio.write(|w| w.ratio().variant(config.ratio));
r.mode.write(|w| {
@ -374,7 +374,7 @@ pub struct Config {
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
feature = "_nrf91",
))]
pub ratio: Ratio,
/// Gain left in dB
@ -393,7 +393,7 @@ impl Default for Config {
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
feature = "_nrf91",
))]
ratio: Ratio::RATIO80,
gain_left: I7F1::ZERO,

View File

@ -78,7 +78,8 @@ impl Default for Config {
/// Used to configure an individual SAADC peripheral channel.
///
/// See the `Default` impl for suitable default values.
/// Construct using the `single_ended` or `differential` methods. These provide sensible defaults
/// for the public fields, which can be overridden if required.
#[non_exhaustive]
pub struct ChannelConfig<'d> {
/// Reference voltage of the SAADC input.
@ -722,9 +723,9 @@ macro_rules! impl_saadc_input {
pub struct VddInput;
impl_peripheral!(VddInput);
#[cfg(not(feature = "_nrf9160"))]
#[cfg(not(feature = "_nrf91"))]
impl_saadc_input!(@local, VddInput, VDD);
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
impl_saadc_input!(@local, VddInput, VDDGPIO);
/// A dummy `Input` pin implementation for SAADC peripheral sampling from the

View File

@ -30,9 +30,9 @@ impl Config {
pub fn try_new(_wdt: &peripherals::WDT) -> Option<Self> {
let r = unsafe { &*WDT::ptr() };
#[cfg(not(feature = "_nrf9160"))]
#[cfg(not(feature = "_nrf91"))]
let runstatus = r.runstatus.read().runstatus().bit();
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
let runstatus = r.runstatus.read().runstatuswdt().bit();
if runstatus {
@ -83,9 +83,9 @@ impl Watchdog {
let crv = config.timeout_ticks.max(MIN_TICKS);
let rren = (1u32 << N) - 1;
#[cfg(not(feature = "_nrf9160"))]
#[cfg(not(feature = "_nrf91"))]
let runstatus = r.runstatus.read().runstatus().bit();
#[cfg(feature = "_nrf9160")]
#[cfg(feature = "_nrf91")]
let runstatus = r.runstatus.read().runstatuswdt().bit();
if runstatus {
@ -184,8 +184,9 @@ impl WatchdogHandle {
/// Steal a watchdog handle by index.
///
/// Safety: watchdog must be initialized, index must be between 0 and N-1 where
/// N is the handle count when initializing.
/// # Safety
/// Watchdog must be initialized and `index` must be between `0` and `N-1`
/// where `N` is the handle count when initializing.
pub unsafe fn steal(index: u8) -> Self {
Self { index }
}

32
embassy-rp/CHANGELOG.md Normal file
View File

@ -0,0 +1,32 @@
# Changelog for embassy-rp
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).
## Unreleased
## 0.2.0 - 2024-08-05
- Add read_to_break_with_count
- add option to provide your own boot2
- Add multichannel ADC
- Add collapse_debuginfo to fmt.rs macros.
- Use raw slices .len() method instead of unsafe hacks.
- Add missing word "pin" in rp pwm documentation
- Add Clone and Copy to Error types
- fix spinlocks staying locked after reset.
- wait until read matches for PSM accesses.
- Remove generics
- fix drop implementation of BufferedUartRx and BufferedUartTx
- implement `embedded_storage_async::nor_flash::MultiwriteNorFlash`
- rp usb: wake ep-wakers after stalling
- rp usb: add stall implementation
- Add parameter for enabling pull-up and pull-down in RP PWM input mode
- rp: remove mod sealed.
- rename pins data type and the macro
- rename pwm channels to pwm slices, including in documentation
- rename the Channel trait to Slice and the PwmPin to PwmChannel
- i2c: Fix race condition that appears on fast repeated transfers.
- Add a basic "read to break" function

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-rp"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 microcontroller"
@ -14,7 +14,9 @@ src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/emba
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/"
features = ["defmt", "unstable-pac", "time-driver"]
flavors = [
{ name = "rp2040", target = "thumbv6m-none-eabi" },
{ name = "rp2040", target = "thumbv6m-none-eabi", features = ["rp2040"] },
{ name = "rp235xa", target = "thumbv8m.main-none-eabihf", features = ["rp235xa"] },
{ name = "rp235xb", target = "thumbv8m.main-none-eabihf", features = ["rp235xb"] },
]
[package.metadata.docs.rs]
@ -22,13 +24,13 @@ features = ["defmt", "unstable-pac", "time-driver"]
[features]
default = [ "rt" ]
## Enable the rt feature of [`rp-pac`](https://docs.rs/crates/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
## Enable the rt feature of [`rp-pac`](https://docs.rs/rp-pac). This brings in the [`cortex-m-rt`](https://docs.rs/cortex-m-rt) crate, which adds startup code and minimal runtime initialization.
rt = [ "rp-pac/rt" ]
## Enable [defmt support](https://docs.rs/defmt) and enables `defmt` debug-log messages and formatting in embassy drivers.
defmt = ["dep:defmt", "embassy-usb-driver/defmt", "embassy-hal-internal/defmt"]
## Configure the critical section crate to use an implementation that is safe for multicore use on rp2040.
## Configure the [`critical-section`](https://docs.rs/critical-section) crate to use an implementation that is safe for multicore use on rp2040.
critical-section-impl = ["critical-section/restore-state-u8"]
## Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
@ -88,13 +90,30 @@ boot2-w25x10cl = []
## ```
boot2-none = []
## Configure the hal for use with the rp2040
rp2040 = ["rp-pac/rp2040"]
_rp235x = ["rp-pac/rp235x"]
## Configure the hal for use with the rp235xA
rp235xa = ["_rp235x"]
## Configure the hal for use with the rp235xB
rp235xb = ["_rp235x"]
# hack around cortex-m peripherals being wrong when running tests.
_test = []
## Add a binary-info header block containing picotool-compatible metadata.
##
## Takes up a little flash space, but picotool can then report the name of your
## program and other details.
binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"]
[dependencies]
embassy-sync = { version = "0.6.0", path = "../embassy-sync" }
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
embassy-time = { version = "0.3.1", path = "../embassy-time" }
embassy-time = { version = "0.3.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" }
atomic-polyfill = "1.0.1"
defmt = { version = "0.3", optional = true }
@ -112,7 +131,7 @@ embedded-storage-async = { version = "0.4.1" }
rand_core = "0.6.4"
fixed = "1.23.1"
rp-pac = { version = "6" }
rp-pac = { git = "https://github.com/embassy-rs/rp-pac.git", rev = "a7f42d25517f7124ad3b4ed492dec8b0f50a0e6c", feature = ["rt"] }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
@ -123,7 +142,9 @@ pio-proc = {version= "0.2" }
pio = {version= "0.2.1" }
rp2040-boot2 = "0.3"
document-features = "0.2.7"
sha2-const-stable = "0.1"
rp-binary-info = { version = "0.1.0", optional = true }
[dev-dependencies]
embassy-executor = { version = "0.5.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
static_cell = { version = "2" }

202
embassy-rp/LICENSE-APACHE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) Embassy project contributors
Portions copyright (c) rp-rs organization
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

26
embassy-rp/LICENSE-MIT Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) Embassy project contributors
Portions copyright (c) rp-rs organization
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -23,5 +23,5 @@ The `embassy-rp` HAL implements the traits from [embedded-hal](https://crates.io
This crate can run on any executor.
Optionally, some features requiring [`embassy-time`](https://crates.io/crates/embassy-time) can be activated with the `time` feature. If you enable it,
Optionally, some features requiring [`embassy-time`](https://crates.io/crates/embassy-time) can be activated with the `time-driver` feature. If you enable it,
you must link an `embassy-time` driver in your project.

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