aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json6
-rw-r--r--README.md4
-rwxr-xr-xci.sh2
-rw-r--r--cyw43/README.md12
-rw-r--r--embassy-boot/nrf/README.md2
-rw-r--r--embassy-macros/Cargo.toml4
-rw-r--r--embassy-macros/src/lib.rs37
-rw-r--r--embassy-macros/src/macros/main.rs9
-rw-r--r--embassy-macros/src/macros/task.rs23
-rw-r--r--embassy-net-driver-channel/Cargo.toml11
-rw-r--r--embassy-net-driver-channel/README.md96
-rw-r--r--embassy-net-driver-channel/src/lib.rs1
-rw-r--r--embassy-net-driver/Cargo.toml11
-rw-r--r--embassy-net-driver/README.md16
-rw-r--r--embassy-net-esp-hosted/Cargo.toml20
-rw-r--r--embassy-net-esp-hosted/src/control.rs139
-rw-r--r--embassy-net-esp-hosted/src/esp_hosted_config.proto432
-rw-r--r--embassy-net-esp-hosted/src/fmt.rs257
-rw-r--r--embassy-net-esp-hosted/src/ioctl.rs123
-rw-r--r--embassy-net-esp-hosted/src/lib.rs337
-rw-r--r--embassy-net-esp-hosted/src/proto.rs652
-rw-r--r--embassy-net-w5500/src/lib.rs5
-rw-r--r--embassy-net/Cargo.toml13
-rw-r--r--embassy-net/README.md64
-rw-r--r--embassy-net/src/device.rs3
-rw-r--r--embassy-net/src/lib.rs44
-rw-r--r--embassy-net/src/udp.rs2
-rw-r--r--embassy-nrf/src/lib.rs6
-rw-r--r--embassy-nrf/src/ppi/mod.rs15
-rw-r--r--embassy-rp/Cargo.toml2
-rw-r--r--embassy-rp/src/adc.rs10
-rw-r--r--embassy-rp/src/clocks.rs92
-rw-r--r--embassy-rp/src/gpio.rs4
-rw-r--r--embassy-rp/src/pio.rs4
-rw-r--r--embassy-rp/src/usb.rs1
-rw-r--r--embassy-stm32-wpan/Cargo.toml5
-rw-r--r--embassy-stm32-wpan/build.rs13
-rw-r--r--embassy-stm32-wpan/src/cmd.rs2
-rw-r--r--embassy-stm32-wpan/src/consts.rs4
-rw-r--r--embassy-stm32-wpan/src/evt.rs38
-rw-r--r--embassy-stm32-wpan/src/lhci.rs112
-rw-r--r--embassy-stm32-wpan/src/lib.rs28
-rw-r--r--embassy-stm32-wpan/src/sub/ble.rs (renamed from embassy-stm32-wpan/src/ble.rs)43
-rw-r--r--embassy-stm32-wpan/src/sub/mac.rs (renamed from embassy-stm32-wpan/src/mac.rs)48
-rw-r--r--embassy-stm32-wpan/src/sub/mm.rs (renamed from embassy-stm32-wpan/src/mm.rs)23
-rw-r--r--embassy-stm32-wpan/src/sub/mod.rs6
-rw-r--r--embassy-stm32-wpan/src/sub/sys.rs (renamed from embassy-stm32-wpan/src/sys.rs)3
-rw-r--r--embassy-stm32-wpan/src/tables.rs34
-rw-r--r--embassy-stm32-wpan/tl_mbox.x.in (renamed from embassy-stm32/tl_mbox.x.in)0
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs12
-rw-r--r--embassy-stm32/src/adc/v3.rs8
-rw-r--r--embassy-stm32/src/can/bxcan.rs8
-rw-r--r--embassy-stm32/src/dac.rs260
-rw-r--r--embassy-stm32/src/dac/mod.rs570
-rw-r--r--embassy-stm32/src/dma/bdma.rs32
-rw-r--r--embassy-stm32/src/eth/v1/rx_desc.rs2
-rw-r--r--embassy-stm32/src/pwm/complementary_pwm.rs2
-rw-r--r--embassy-stm32/src/rcc/c0.rs10
-rw-r--r--embassy-stm32/src/rcc/f0.rs23
-rw-r--r--embassy-stm32/src/rcc/f1.rs36
-rw-r--r--embassy-stm32/src/rcc/f2.rs2
-rw-r--r--embassy-stm32/src/rcc/f4.rs12
-rw-r--r--embassy-stm32/src/rcc/f7.rs12
-rw-r--r--embassy-stm32/src/rcc/g0.rs10
-rw-r--r--embassy-stm32/src/rcc/g4.rs77
-rw-r--r--embassy-stm32/src/rcc/h7.rs17
-rw-r--r--embassy-stm32/src/rcc/l0.rs10
-rw-r--r--embassy-stm32/src/rcc/l1.rs6
-rw-r--r--embassy-stm32/src/rcc/l4.rs6
-rw-r--r--embassy-stm32/src/rcc/l5.rs6
-rw-r--r--embassy-stm32/src/rcc/u5.rs2
-rw-r--r--embassy-stm32/src/rtc/v2.rs4
-rw-r--r--embassy-stm32/src/rtc/v3.rs2
-rw-r--r--embassy-stm32/src/sdmmc/mod.rs6
-rw-r--r--embassy-stm32/src/spi/mod.rs4
-rw-r--r--embassy-stm32/src/usart/mod.rs2
-rw-r--r--embassy-stm32/src/usb/usb.rs141
-rw-r--r--embassy-stm32/src/usb_otg/usb.rs519
-rw-r--r--embassy-stm32/src/wdg/mod.rs2
-rw-r--r--embassy-sync/src/pipe.rs4
-rw-r--r--embassy-usb/src/class/cdc_ncm/embassy_net.rs5
-rw-r--r--embassy-usb/src/class/cdc_ncm/mod.rs4
-rw-r--r--embassy-usb/src/lib.rs2
-rw-r--r--examples/boot/application/nrf/.cargo/config.toml4
-rw-r--r--examples/boot/application/rp/.cargo/config.toml2
-rw-r--r--examples/boot/application/stm32f3/.cargo/config.toml4
-rw-r--r--examples/boot/application/stm32f7/.cargo/config.toml4
-rw-r--r--examples/boot/application/stm32h7/.cargo/config.toml4
-rwxr-xr-xexamples/boot/application/stm32h7/flash-boot.sh2
-rw-r--r--examples/boot/application/stm32l0/.cargo/config.toml4
-rw-r--r--examples/boot/application/stm32l1/.cargo/config.toml4
-rw-r--r--examples/boot/application/stm32l4/.cargo/config.toml4
-rw-r--r--examples/boot/application/stm32wl/.cargo/config.toml4
-rw-r--r--examples/boot/bootloader/nrf/.cargo/config.toml2
-rw-r--r--examples/boot/bootloader/rp/.cargo/config.toml2
-rw-r--r--examples/nrf-rtos-trace/.cargo/config.toml4
-rw-r--r--examples/nrf52840-rtic/.cargo/config.toml4
-rw-r--r--examples/nrf52840/.cargo/config.toml4
-rw-r--r--examples/nrf52840/Cargo.toml22
-rw-r--r--examples/nrf52840/src/bin/nvmc.rs2
-rw-r--r--examples/nrf52840/src/bin/self_spawn.rs6
-rw-r--r--examples/nrf52840/src/bin/wdt.rs2
-rw-r--r--examples/nrf52840/src/bin/wifi_esp_hosted.rs139
-rw-r--r--examples/nrf5340/.cargo/config.toml4
-rw-r--r--examples/rp/.cargo/config.toml2
-rw-r--r--examples/rp/src/bin/wifi_ap_tcp_server.rs4
-rw-r--r--examples/rp/src/bin/wifi_blinky.rs4
-rw-r--r--examples/rp/src/bin/wifi_scan.rs4
-rw-r--r--examples/rp/src/bin/wifi_tcp_server.rs4
-rw-r--r--examples/std/README.md23
-rw-r--r--examples/stm32c0/.cargo/config.toml4
-rw-r--r--examples/stm32f0/.cargo/config.toml2
-rw-r--r--examples/stm32f1/.cargo/config.toml4
-rw-r--r--examples/stm32f2/.cargo/config.toml4
-rw-r--r--examples/stm32f3/.cargo/config.toml4
-rw-r--r--examples/stm32f4/.cargo/config.toml4
-rw-r--r--examples/stm32f4/src/bin/dac.rs9
-rw-r--r--examples/stm32f4/src/bin/usb_ethernet.rs4
-rw-r--r--examples/stm32f4/src/bin/usb_serial.rs4
-rw-r--r--examples/stm32f7/.cargo/config.toml4
-rw-r--r--examples/stm32f7/src/bin/usb_serial.rs4
-rw-r--r--examples/stm32g0/.cargo/config.toml4
-rw-r--r--examples/stm32g4/.cargo/config.toml4
-rw-r--r--examples/stm32g4/src/bin/usb_serial.rs26
-rw-r--r--examples/stm32h5/.cargo/config.toml2
-rw-r--r--examples/stm32h7/.cargo/config.toml2
-rw-r--r--examples/stm32h7/src/bin/dac.rs9
-rw-r--r--examples/stm32h7/src/bin/usb_serial.rs4
-rw-r--r--examples/stm32l0/.cargo/config.toml4
-rw-r--r--examples/stm32l1/.cargo/config.toml4
-rw-r--r--examples/stm32l4/.cargo/config.toml8
-rw-r--r--examples/stm32l4/src/bin/dac.rs17
-rw-r--r--examples/stm32l4/src/bin/dac_dma.rs137
-rw-r--r--examples/stm32l4/src/bin/usb_serial.rs4
-rw-r--r--examples/stm32l5/.cargo/config.toml4
-rw-r--r--examples/stm32u5/.cargo/config.toml4
-rw-r--r--examples/stm32u5/src/bin/usb_serial.rs4
-rw-r--r--examples/stm32wb/.cargo/config.toml4
-rw-r--r--examples/stm32wb/Cargo.toml9
-rw-r--r--examples/stm32wb/src/bin/eddystone_beacon.rs249
-rw-r--r--examples/stm32wb/src/bin/tl_mbox_ble.rs4
-rw-r--r--examples/stm32wb/src/bin/tl_mbox_mac.rs8
-rw-r--r--examples/stm32wl/.cargo/config.toml4
-rw-r--r--tests/nrf/Cargo.toml4
-rw-r--r--tests/nrf/src/bin/wifi_esp_hosted_perf.rs270
-rw-r--r--tests/rp/src/bin/cyw43-perf.rs13
-rw-r--r--tests/stm32/Cargo.toml3
-rw-r--r--tests/stm32/src/bin/tl_mbox.rs220
149 files changed, 4951 insertions, 1029 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9ef7fe1ce..725fb69d0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,16 +6,16 @@
6 "rust-analyzer.check.allTargets": false, 6 "rust-analyzer.check.allTargets": false,
7 "rust-analyzer.check.noDefaultFeatures": true, 7 "rust-analyzer.check.noDefaultFeatures": true,
8 "rust-analyzer.cargo.noDefaultFeatures": true, 8 "rust-analyzer.cargo.noDefaultFeatures": true,
9 "rust-analyzer.cargo.target": "thumbv7em-none-eabi", 9 "rust-analyzer.cargo.target": "thumbv7m-none-eabi",
10 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", 10 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
11 "rust-analyzer.cargo.features": [ 11 "rust-analyzer.cargo.features": [
12 "nightly", 12 ///"nightly",
13 ], 13 ],
14 "rust-analyzer.linkedProjects": [ 14 "rust-analyzer.linkedProjects": [
15 // Declare for the target you wish to develop 15 // Declare for the target you wish to develop
16 // "embassy-executor/Cargo.toml", 16 // "embassy-executor/Cargo.toml",
17 // "embassy-sync/Cargo.toml", 17 // "embassy-sync/Cargo.toml",
18 "examples/nrf52840/Cargo.toml", 18 "examples/stm32wl/Cargo.toml",
19 // "examples/nrf5340/Cargo.toml", 19 // "examples/nrf5340/Cargo.toml",
20 // "examples/nrf-rtos-trace/Cargo.toml", 20 // "examples/nrf-rtos-trace/Cargo.toml",
21 // "examples/rp/Cargo.toml", 21 // "examples/rp/Cargo.toml",
diff --git a/README.md b/README.md
index 315d247ee..b05e55aa5 100644
--- a/README.md
+++ b/README.md
@@ -99,10 +99,10 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer
99 99
100### Running examples 100### Running examples
101 101
102- Install `probe-rs-cli` with defmt support. 102- Install `probe-rs`.
103 103
104```bash 104```bash
105cargo install probe-rs-cli 105cargo install probe-rs --features cli
106``` 106```
107 107
108- Change directory to the sample's base directory. For example: 108- Change directory to the sample's base directory. For example:
diff --git a/ci.sh b/ci.sh
index 3fe1b1ce8..a03efb856 100755
--- a/ci.sh
+++ b/ci.sh
@@ -3,7 +3,7 @@
3set -euo pipefail 3set -euo pipefail
4 4
5export RUSTFLAGS=-Dwarnings 5export RUSTFLAGS=-Dwarnings
6export DEFMT_LOG=trace,cyw43=info,cyw43_pio=info,smoltcp=info 6export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
7 7
8# needed by wifi examples 8# needed by wifi examples
9export WIFI_NETWORK=x 9export WIFI_NETWORK=x
diff --git a/cyw43/README.md b/cyw43/README.md
index defea489f..e4a81410d 100644
--- a/cyw43/README.md
+++ b/cyw43/README.md
@@ -1,6 +1,6 @@
1# cyw43 1# cyw43
2 2
3WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver). 3Rust driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
4 4
5## Current status 5## Current status
6 6
@@ -19,18 +19,18 @@ Working:
19TODO: 19TODO:
20 20
21- Setting a custom MAC address. 21- Setting a custom MAC address.
22- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) 22- Bus sleep (for power consumption optimization)
23 23
24## Running the examples 24## Running the examples
25 25
26- `cargo install probe-rs-cli` 26- `cargo install probe-rs --features cli`
27- `cd examples/rpi-pico-w` 27- `cd examples/rp`
28### Example 1: Scan the wifi stations 28### Example 1: Scan the wifi stations
29- `cargo run --release --bin wifi_scan` 29- `cargo run --release --bin wifi_scan`
30### Example 2: Create an access point (IP and credentials in the code) 30### Example 2: Create an access point (IP and credentials in the code)
31- `cargo run --release --bin tcp_server_ap` 31- `cargo run --release --bin wifi_ap_tcp_server`
32### Example 3: Connect to an existing network and create a server 32### Example 3: Connect to an existing network and create a server
33- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` 33- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server`
34 34
35After a few seconds, you should see that DHCP picks up an IP address like this 35After a few seconds, you should see that DHCP picks up an IP address like this
36``` 36```
diff --git a/embassy-boot/nrf/README.md b/embassy-boot/nrf/README.md
index 7ce3c7021..fe581823d 100644
--- a/embassy-boot/nrf/README.md
+++ b/embassy-boot/nrf/README.md
@@ -6,7 +6,7 @@ An adaptation of `embassy-boot` for nRF.
6 6
7## Features 7## Features
8 8
9* Load applications with our without the softdevice. 9* Load applications with or without the softdevice.
10* Configure bootloader partitions based on linker script. 10* Configure bootloader partitions based on linker script.
11* Using watchdog timer to detect application failure. 11* Using watchdog timer to detect application failure.
12 12
diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml
index 781026b99..3b8fe8b44 100644
--- a/embassy-macros/Cargo.toml
+++ b/embassy-macros/Cargo.toml
@@ -12,9 +12,9 @@ categories = [
12] 12]
13 13
14[dependencies] 14[dependencies]
15syn = { version = "1.0.76", features = ["full", "extra-traits"] } 15syn = { version = "2.0.15", features = ["full", "extra-traits"] }
16quote = "1.0.9" 16quote = "1.0.9"
17darling = "0.13.0" 17darling = "0.20.1"
18proc-macro2 = "1.0.29" 18proc-macro2 = "1.0.29"
19 19
20[lib] 20[lib]
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs
index ba4f13b77..c9d58746a 100644
--- a/embassy-macros/src/lib.rs
+++ b/embassy-macros/src/lib.rs
@@ -1,11 +1,28 @@
1#![doc = include_str!("../README.md")] 1#![doc = include_str!("../README.md")]
2extern crate proc_macro; 2extern crate proc_macro;
3 3
4use darling::ast::NestedMeta;
4use proc_macro::TokenStream; 5use proc_macro::TokenStream;
5 6
6mod macros; 7mod macros;
7mod util; 8mod util;
8use macros::*; 9use macros::*;
10use syn::parse::{Parse, ParseBuffer};
11use syn::punctuated::Punctuated;
12use syn::Token;
13
14struct Args {
15 meta: Vec<NestedMeta>,
16}
17
18impl Parse for Args {
19 fn parse(input: &ParseBuffer) -> syn::Result<Self> {
20 let meta = Punctuated::<NestedMeta, Token![,]>::parse_terminated(input)?;
21 Ok(Args {
22 meta: meta.into_iter().collect(),
23 })
24 }
25}
9 26
10/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how 27/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how
11/// many concurrent tasks can be spawned (default is 1) for the function. 28/// many concurrent tasks can be spawned (default is 1) for the function.
@@ -39,10 +56,10 @@ use macros::*;
39/// ``` 56/// ```
40#[proc_macro_attribute] 57#[proc_macro_attribute]
41pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { 58pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
42 let args = syn::parse_macro_input!(args as syn::AttributeArgs); 59 let args = syn::parse_macro_input!(args as Args);
43 let f = syn::parse_macro_input!(item as syn::ItemFn); 60 let f = syn::parse_macro_input!(item as syn::ItemFn);
44 61
45 task::run(args, f).unwrap_or_else(|x| x).into() 62 task::run(&args.meta, f).unwrap_or_else(|x| x).into()
46} 63}
47 64
48/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task. 65/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.
@@ -65,9 +82,9 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
65/// ``` 82/// ```
66#[proc_macro_attribute] 83#[proc_macro_attribute]
67pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { 84pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
68 let args = syn::parse_macro_input!(args as syn::AttributeArgs); 85 let args = syn::parse_macro_input!(args as Args);
69 let f = syn::parse_macro_input!(item as syn::ItemFn); 86 let f = syn::parse_macro_input!(item as syn::ItemFn);
70 main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into() 87 main::run(&args.meta, f, main::cortex_m()).unwrap_or_else(|x| x).into()
71} 88}
72 89
73/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. 90/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task.
@@ -100,9 +117,9 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
100/// ``` 117/// ```
101#[proc_macro_attribute] 118#[proc_macro_attribute]
102pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { 119pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {
103 let args = syn::parse_macro_input!(args as syn::AttributeArgs); 120 let args = syn::parse_macro_input!(args as Args);
104 let f = syn::parse_macro_input!(item as syn::ItemFn); 121 let f = syn::parse_macro_input!(item as syn::ItemFn);
105 main::run(args.clone(), f, main::riscv(args)) 122 main::run(&args.meta, f, main::riscv(&args.meta))
106 .unwrap_or_else(|x| x) 123 .unwrap_or_else(|x| x)
107 .into() 124 .into()
108} 125}
@@ -127,9 +144,9 @@ pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {
127/// ``` 144/// ```
128#[proc_macro_attribute] 145#[proc_macro_attribute]
129pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { 146pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
130 let args = syn::parse_macro_input!(args as syn::AttributeArgs); 147 let args = syn::parse_macro_input!(args as Args);
131 let f = syn::parse_macro_input!(item as syn::ItemFn); 148 let f = syn::parse_macro_input!(item as syn::ItemFn);
132 main::run(args, f, main::std()).unwrap_or_else(|x| x).into() 149 main::run(&args.meta, f, main::std()).unwrap_or_else(|x| x).into()
133} 150}
134 151
135/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task. 152/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task.
@@ -152,7 +169,7 @@ pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
152/// ``` 169/// ```
153#[proc_macro_attribute] 170#[proc_macro_attribute]
154pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { 171pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream {
155 let args = syn::parse_macro_input!(args as syn::AttributeArgs); 172 let args = syn::parse_macro_input!(args as Args);
156 let f = syn::parse_macro_input!(item as syn::ItemFn); 173 let f = syn::parse_macro_input!(item as syn::ItemFn);
157 main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into() 174 main::run(&args.meta, f, main::wasm()).unwrap_or_else(|x| x).into()
158} 175}
diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs
index 5c099f68a..7c4d55163 100644
--- a/embassy-macros/src/macros/main.rs
+++ b/embassy-macros/src/macros/main.rs
@@ -1,3 +1,4 @@
1use darling::export::NestedMeta;
1use darling::FromMeta; 2use darling::FromMeta;
2use proc_macro2::TokenStream; 3use proc_macro2::TokenStream;
3use quote::quote; 4use quote::quote;
@@ -11,8 +12,8 @@ struct Args {
11 entry: Option<String>, 12 entry: Option<String>,
12} 13}
13 14
14pub fn riscv(args: syn::AttributeArgs) -> TokenStream { 15pub fn riscv(args: &[NestedMeta]) -> TokenStream {
15 let maybe_entry = match Args::from_list(&args) { 16 let maybe_entry = match Args::from_list(args) {
16 Ok(args) => args.entry, 17 Ok(args) => args.entry,
17 Err(e) => return e.write_errors(), 18 Err(e) => return e.write_errors(),
18 }; 19 };
@@ -77,9 +78,9 @@ pub fn std() -> TokenStream {
77 } 78 }
78} 79}
79 80
80pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> { 81pub fn run(args: &[NestedMeta], f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> {
81 #[allow(unused_variables)] 82 #[allow(unused_variables)]
82 let args = Args::from_list(&args).map_err(|e| e.write_errors())?; 83 let args = Args::from_list(args).map_err(|e| e.write_errors())?;
83 84
84 let fargs = f.sig.inputs.clone(); 85 let fargs = f.sig.inputs.clone();
85 86
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs
index 9f30cf43e..1d30434e9 100644
--- a/embassy-macros/src/macros/task.rs
+++ b/embassy-macros/src/macros/task.rs
@@ -1,20 +1,24 @@
1use darling::export::NestedMeta;
1use darling::FromMeta; 2use darling::FromMeta;
2use proc_macro2::TokenStream; 3use proc_macro2::{Span, TokenStream};
3use quote::{format_ident, quote}; 4use quote::{format_ident, quote};
4use syn::{parse_quote, ItemFn, ReturnType, Type}; 5use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type};
5 6
6use crate::util::ctxt::Ctxt; 7use crate::util::ctxt::Ctxt;
7 8
8#[derive(Debug, FromMeta)] 9#[derive(Debug, FromMeta)]
9struct Args { 10struct Args {
10 #[darling(default)] 11 #[darling(default)]
11 pool_size: Option<usize>, 12 pool_size: Option<syn::Expr>,
12} 13}
13 14
14pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> { 15pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
15 let args = Args::from_list(&args).map_err(|e| e.write_errors())?; 16 let args = Args::from_list(args).map_err(|e| e.write_errors())?;
16 17
17 let pool_size: usize = args.pool_size.unwrap_or(1); 18 let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit {
19 attrs: vec![],
20 lit: Lit::Int(LitInt::new("1", Span::call_site())),
21 }));
18 22
19 let ctxt = Ctxt::new(); 23 let ctxt = Ctxt::new();
20 24
@@ -45,10 +49,6 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke
45 }, 49 },
46 } 50 }
47 51
48 if pool_size < 1 {
49 ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater");
50 }
51
52 let mut arg_names = Vec::new(); 52 let mut arg_names = Vec::new();
53 let mut fargs = f.sig.inputs.clone(); 53 let mut fargs = f.sig.inputs.clone();
54 54
@@ -82,7 +82,8 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke
82 let mut task_outer: ItemFn = parse_quote! { 82 let mut task_outer: ItemFn = parse_quote! {
83 #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { 83 #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
84 type Fut = impl ::core::future::Future + 'static; 84 type Fut = impl ::core::future::Future + 'static;
85 static POOL: ::embassy_executor::raw::TaskPool<Fut, #pool_size> = ::embassy_executor::raw::TaskPool::new(); 85 const POOL_SIZE: usize = #pool_size;
86 static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
86 unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } 87 unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
87 } 88 }
88 }; 89 };
diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml
index e475551e1..bee2e3021 100644
--- a/embassy-net-driver-channel/Cargo.toml
+++ b/embassy-net-driver-channel/Cargo.toml
@@ -2,6 +2,14 @@
2name = "embassy-net-driver-channel" 2name = "embassy-net-driver-channel"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0"
6description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
7repository = "https://github.com/embassy-rs/embassy"
8categories = [
9 "embedded",
10 "no-std",
11 "asynchronous",
12]
5 13
6[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
@@ -9,6 +17,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
9features = ["defmt"] 17features = ["defmt"]
10target = "thumbv7em-none-eabi" 18target = "thumbv7em-none-eabi"
11 19
20[package.metadata.docs.rs]
21features = ["defmt"]
22
12[dependencies] 23[dependencies]
13defmt = { version = "0.3", optional = true } 24defmt = { version = "0.3", optional = true }
14log = { version = "0.4.14", optional = true } 25log = { version = "0.4.14", optional = true }
diff --git a/embassy-net-driver-channel/README.md b/embassy-net-driver-channel/README.md
new file mode 100644
index 000000000..dd90e7ad2
--- /dev/null
+++ b/embassy-net-driver-channel/README.md
@@ -0,0 +1,96 @@
1# embassy-net-driver-channel
2
3This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a
4higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.
5
6The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by
7hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
8knows when to poll your driver again to make more progress.
9
10With `embassy-net-driver-channel`
11
12## A note about deadlocks
13
14When implementing a driver using this crate, it might be tempting to write it in the most straightforward way:
15
16```rust,ignore
17loop {
18 // Wait for either..
19 match select(
20 // ... the chip signaling an interrupt, indicating a packet is available to receive, or
21 irq_pin.wait_for_low(),
22 // ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
23 tx_chan.tx_buf(),
24 ).await {
25 Either::First(_) => {
26 // a packet is ready to be received!
27 let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
28 let n = receive_packet_over_spi(buf).await;
29 rx_chan.rx_done(n);
30 }
31 Either::Second(buf) => {
32 // a packet is ready to be sent!
33 send_packet_over_spi(buf).await;
34 tx_chan.tx_done();
35 }
36 }
37}
38```
39
40However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.
41
42The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
43
44The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
45
46```rust,ignore
47loop {
48 // Wait for either..
49 match select(
50 async {
51 // ... the chip signaling an interrupt, indicating a packet is available to receive
52 irq_pin.wait_for_low().await;
53 // *AND* the buffer is ready...
54 rx_chan.rx_buf().await
55 },
56 // ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet
57 tx_chan.tx_buf(),
58 ).await {
59 Either::First(buf) => {
60 // a packet is ready to be received!
61 let n = receive_packet_over_spi(buf).await;
62 rx_chan.rx_done(n);
63 }
64 Either::Second(buf) => {
65 // a packet is ready to be sent!
66 send_packet_over_spi(buf).await;
67 tx_chan.tx_done();
68 }
69 }
70}
71```
72
73## Examples
74
75These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.
76
77- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
78- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
79- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
80- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
81
82
83## Interoperability
84
85This crate can run on any executor.
86
87
88## License
89
90This work is licensed under either of
91
92- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
93 http://www.apache.org/licenses/LICENSE-2.0)
94- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
95
96at your option.
diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs
index 0c8dcc22b..02a4c00d6 100644
--- a/embassy-net-driver-channel/src/lib.rs
+++ b/embassy-net-driver-channel/src/lib.rs
@@ -1,4 +1,5 @@
1#![no_std] 1#![no_std]
2#![doc = include_str!("../README.md")]
2 3
3// must go first! 4// must go first!
4mod fmt; 5mod fmt;
diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml
index ff6f29355..da6d9ad62 100644
--- a/embassy-net-driver/Cargo.toml
+++ b/embassy-net-driver/Cargo.toml
@@ -3,7 +3,13 @@ name = "embassy-net-driver"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6description = "Driver trait for the `embassy-net` async TCP/IP network stack."
7repository = "https://github.com/embassy-rs/embassy"
8categories = [
9 "embedded",
10 "no-std",
11 "asynchronous",
12]
7 13
8[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
@@ -11,5 +17,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
11features = ["defmt"] 17features = ["defmt"]
12target = "thumbv7em-none-eabi" 18target = "thumbv7em-none-eabi"
13 19
20[package.metadata.docs.rs]
21features = ["defmt"]
22
14[dependencies] 23[dependencies]
15defmt = { version = "0.3", optional = true } \ No newline at end of file 24defmt = { version = "0.3", optional = true } \ No newline at end of file
diff --git a/embassy-net-driver/README.md b/embassy-net-driver/README.md
index 84f25492d..6a757380d 100644
--- a/embassy-net-driver/README.md
+++ b/embassy-net-driver/README.md
@@ -1,5 +1,21 @@
1# embassy-net-driver 1# embassy-net-driver
2 2
3This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support
4for a new hardware platform.
5
6If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.
7
8If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate.
9This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,
10if the driver trait has not had breaking changes.
11
12See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API
13to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
14packet queues for RX and TX.
15
16## Interoperability
17
18This crate can run on any executor.
3 19
4## License 20## License
5 21
diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml
new file mode 100644
index 000000000..a7e18ee09
--- /dev/null
+++ b/embassy-net-esp-hosted/Cargo.toml
@@ -0,0 +1,20 @@
1[package]
2name = "embassy-net-esp-hosted"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7defmt = { version = "0.3", optional = true }
8log = { version = "0.4.14", optional = true }
9
10embassy-time = { version = "0.1.0", path = "../embassy-time" }
11embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
12embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
13embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
14
15embedded-hal = { version = "1.0.0-alpha.10" }
16embedded-hal-async = { version = "=0.2.0-alpha.1" }
17
18noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
19#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
20heapless = "0.7.16"
diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs
new file mode 100644
index 000000000..fce82ade7
--- /dev/null
+++ b/embassy-net-esp-hosted/src/control.rs
@@ -0,0 +1,139 @@
1use ch::driver::LinkState;
2use defmt::Debug2Format;
3use embassy_net_driver_channel as ch;
4use heapless::String;
5
6use crate::ioctl::Shared;
7use crate::proto::{self, CtrlMsg};
8
9#[derive(Debug)]
10pub struct Error {
11 pub status: u32,
12}
13
14pub struct Control<'a> {
15 state_ch: ch::StateRunner<'a>,
16 shared: &'a Shared,
17}
18
19#[allow(unused)]
20enum WifiMode {
21 None = 0,
22 Sta = 1,
23 Ap = 2,
24 ApSta = 3,
25}
26
27impl<'a> Control<'a> {
28 pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
29 Self { state_ch, shared }
30 }
31
32 pub async fn init(&mut self) {
33 debug!("wait for init event...");
34 self.shared.init_wait().await;
35
36 debug!("set wifi mode");
37 self.set_wifi_mode(WifiMode::Sta as _).await;
38
39 let mac_addr = self.get_mac_addr().await;
40 debug!("mac addr: {:02x}", mac_addr);
41 self.state_ch.set_ethernet_address(mac_addr);
42 }
43
44 pub async fn join(&mut self, ssid: &str, password: &str) {
45 let req = proto::CtrlMsg {
46 msg_id: proto::CtrlMsgId::ReqConnectAp as _,
47 msg_type: proto::CtrlMsgType::Req as _,
48 payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp {
49 ssid: String::from(ssid),
50 pwd: String::from(password),
51 bssid: String::new(),
52 listen_interval: 3,
53 is_wpa3_supported: false,
54 })),
55 };
56 let resp = self.ioctl(req).await;
57 let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
58 debug!("======= {:?}", Debug2Format(&resp));
59 assert_eq!(resp.resp, 0);
60 self.state_ch.set_link_state(LinkState::Up);
61 }
62
63 async fn get_mac_addr(&mut self) -> [u8; 6] {
64 let req = proto::CtrlMsg {
65 msg_id: proto::CtrlMsgId::ReqGetMacAddress as _,
66 msg_type: proto::CtrlMsgType::Req as _,
67 payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress(
68 proto::CtrlMsgReqGetMacAddress {
69 mode: WifiMode::Sta as _,
70 },
71 )),
72 };
73 let resp = self.ioctl(req).await;
74 let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
75 assert_eq!(resp.resp, 0);
76
77 // WHY IS THIS A STRING? WHYYYY
78 fn nibble_from_hex(b: u8) -> u8 {
79 match b {
80 b'0'..=b'9' => b - b'0',
81 b'a'..=b'f' => b + 0xa - b'a',
82 b'A'..=b'F' => b + 0xa - b'A',
83 _ => panic!("invalid hex digit {}", b),
84 }
85 }
86
87 let mac = resp.mac.as_bytes();
88 let mut res = [0; 6];
89 assert_eq!(mac.len(), 17);
90 for (i, b) in res.iter_mut().enumerate() {
91 *b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
92 }
93 res
94 }
95
96 async fn set_wifi_mode(&mut self, mode: u32) {
97 let req = proto::CtrlMsg {
98 msg_id: proto::CtrlMsgId::ReqSetWifiMode as _,
99 msg_type: proto::CtrlMsgType::Req as _,
100 payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
101 };
102 let resp = self.ioctl(req).await;
103 let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
104 assert_eq!(resp.resp, 0);
105 }
106
107 async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg {
108 debug!("ioctl req: {:?}", &req);
109
110 let mut buf = [0u8; 128];
111
112 let req_len = noproto::write(&req, &mut buf).unwrap();
113
114 struct CancelOnDrop<'a>(&'a Shared);
115
116 impl CancelOnDrop<'_> {
117 fn defuse(self) {
118 core::mem::forget(self);
119 }
120 }
121
122 impl Drop for CancelOnDrop<'_> {
123 fn drop(&mut self) {
124 self.0.ioctl_cancel();
125 }
126 }
127
128 let ioctl = CancelOnDrop(self.shared);
129
130 let resp_len = ioctl.0.ioctl(&mut buf, req_len).await;
131
132 ioctl.defuse();
133
134 let res = noproto::read(&buf[..resp_len]).unwrap();
135 debug!("ioctl resp: {:?}", &res);
136
137 res
138 }
139}
diff --git a/embassy-net-esp-hosted/src/esp_hosted_config.proto b/embassy-net-esp-hosted/src/esp_hosted_config.proto
new file mode 100644
index 000000000..aa1bfde64
--- /dev/null
+++ b/embassy-net-esp-hosted/src/esp_hosted_config.proto
@@ -0,0 +1,432 @@
1syntax = "proto3";
2
3/* Enums similar to ESP IDF */
4enum Ctrl_VendorIEType {
5 Beacon = 0;
6 Probe_req = 1;
7 Probe_resp = 2;
8 Assoc_req = 3;
9 Assoc_resp = 4;
10}
11
12enum Ctrl_VendorIEID {
13 ID_0 = 0;
14 ID_1 = 1;
15}
16
17enum Ctrl_WifiMode {
18 NONE = 0;
19 STA = 1;
20 AP = 2;
21 APSTA = 3;
22}
23
24enum Ctrl_WifiBw {
25 BW_Invalid = 0;
26 HT20 = 1;
27 HT40 = 2;
28}
29
30enum Ctrl_WifiPowerSave {
31 PS_Invalid = 0;
32 MIN_MODEM = 1;
33 MAX_MODEM = 2;
34}
35
36enum Ctrl_WifiSecProt {
37 Open = 0;
38 WEP = 1;
39 WPA_PSK = 2;
40 WPA2_PSK = 3;
41 WPA_WPA2_PSK = 4;
42 WPA2_ENTERPRISE = 5;
43 WPA3_PSK = 6;
44 WPA2_WPA3_PSK = 7;
45}
46
47/* enums for Control path */
48enum Ctrl_Status {
49 Connected = 0;
50 Not_Connected = 1;
51 No_AP_Found = 2;
52 Connection_Fail = 3;
53 Invalid_Argument = 4;
54 Out_Of_Range = 5;
55}
56
57
58enum CtrlMsgType {
59 MsgType_Invalid = 0;
60 Req = 1;
61 Resp = 2;
62 Event = 3;
63 MsgType_Max = 4;
64}
65
66enum CtrlMsgId {
67 MsgId_Invalid = 0;
68
69 /** Request Msgs **/
70 Req_Base = 100;
71
72 Req_GetMACAddress = 101;
73 Req_SetMacAddress = 102;
74 Req_GetWifiMode = 103;
75 Req_SetWifiMode = 104;
76
77 Req_GetAPScanList = 105;
78 Req_GetAPConfig = 106;
79 Req_ConnectAP = 107;
80 Req_DisconnectAP = 108;
81
82 Req_GetSoftAPConfig = 109;
83 Req_SetSoftAPVendorSpecificIE = 110;
84 Req_StartSoftAP = 111;
85 Req_GetSoftAPConnectedSTAList = 112;
86 Req_StopSoftAP = 113;
87
88 Req_SetPowerSaveMode = 114;
89 Req_GetPowerSaveMode = 115;
90
91 Req_OTABegin = 116;
92 Req_OTAWrite = 117;
93 Req_OTAEnd = 118;
94
95 Req_SetWifiMaxTxPower = 119;
96 Req_GetWifiCurrTxPower = 120;
97
98 Req_ConfigHeartbeat = 121;
99 /* Add new control path command response before Req_Max
100 * and update Req_Max */
101 Req_Max = 122;
102
103 /** Response Msgs **/
104 Resp_Base = 200;
105
106 Resp_GetMACAddress = 201;
107 Resp_SetMacAddress = 202;
108 Resp_GetWifiMode = 203;
109 Resp_SetWifiMode = 204;
110
111 Resp_GetAPScanList = 205;
112 Resp_GetAPConfig = 206;
113 Resp_ConnectAP = 207;
114 Resp_DisconnectAP = 208;
115
116 Resp_GetSoftAPConfig = 209;
117 Resp_SetSoftAPVendorSpecificIE = 210;
118 Resp_StartSoftAP = 211;
119 Resp_GetSoftAPConnectedSTAList = 212;
120 Resp_StopSoftAP = 213;
121
122 Resp_SetPowerSaveMode = 214;
123 Resp_GetPowerSaveMode = 215;
124
125 Resp_OTABegin = 216;
126 Resp_OTAWrite = 217;
127 Resp_OTAEnd = 218;
128
129 Resp_SetWifiMaxTxPower = 219;
130 Resp_GetWifiCurrTxPower = 220;
131
132 Resp_ConfigHeartbeat = 221;
133 /* Add new control path command response before Resp_Max
134 * and update Resp_Max */
135 Resp_Max = 222;
136
137 /** Event Msgs **/
138 Event_Base = 300;
139 Event_ESPInit = 301;
140 Event_Heartbeat = 302;
141 Event_StationDisconnectFromAP = 303;
142 Event_StationDisconnectFromESPSoftAP = 304;
143 /* Add new control path command notification before Event_Max
144 * and update Event_Max */
145 Event_Max = 305;
146}
147
148/* internal supporting structures for CtrlMsg */
149message ScanResult {
150 bytes ssid = 1;
151 uint32 chnl = 2;
152 int32 rssi = 3;
153 bytes bssid = 4;
154 Ctrl_WifiSecProt sec_prot = 5;
155}
156
157message ConnectedSTAList {
158 bytes mac = 1;
159 int32 rssi = 2;
160}
161
162
163/* Control path structures */
164/** Req/Resp structure **/
165message CtrlMsg_Req_GetMacAddress {
166 int32 mode = 1;
167}
168
169message CtrlMsg_Resp_GetMacAddress {
170 bytes mac = 1;
171 int32 resp = 2;
172}
173
174message CtrlMsg_Req_GetMode {
175}
176
177message CtrlMsg_Resp_GetMode {
178 int32 mode = 1;
179 int32 resp = 2;
180}
181
182message CtrlMsg_Req_SetMode {
183 int32 mode = 1;
184}
185
186message CtrlMsg_Resp_SetMode {
187 int32 resp = 1;
188}
189
190message CtrlMsg_Req_GetStatus {
191}
192
193message CtrlMsg_Resp_GetStatus {
194 int32 resp = 1;
195}
196
197message CtrlMsg_Req_SetMacAddress {
198 bytes mac = 1;
199 int32 mode = 2;
200}
201
202message CtrlMsg_Resp_SetMacAddress {
203 int32 resp = 1;
204}
205
206message CtrlMsg_Req_GetAPConfig {
207}
208
209message CtrlMsg_Resp_GetAPConfig {
210 bytes ssid = 1;
211 bytes bssid = 2;
212 int32 rssi = 3;
213 int32 chnl = 4;
214 Ctrl_WifiSecProt sec_prot = 5;
215 int32 resp = 6;
216}
217
218message CtrlMsg_Req_ConnectAP {
219 string ssid = 1;
220 string pwd = 2;
221 string bssid = 3;
222 bool is_wpa3_supported = 4;
223 int32 listen_interval = 5;
224}
225
226message CtrlMsg_Resp_ConnectAP {
227 int32 resp = 1;
228 bytes mac = 2;
229}
230
231message CtrlMsg_Req_GetSoftAPConfig {
232}
233
234message CtrlMsg_Resp_GetSoftAPConfig {
235 bytes ssid = 1;
236 bytes pwd = 2;
237 int32 chnl = 3;
238 Ctrl_WifiSecProt sec_prot = 4;
239 int32 max_conn = 5;
240 bool ssid_hidden = 6;
241 int32 bw = 7;
242 int32 resp = 8;
243}
244
245message CtrlMsg_Req_StartSoftAP {
246 string ssid = 1;
247 string pwd = 2;
248 int32 chnl = 3;
249 Ctrl_WifiSecProt sec_prot = 4;
250 int32 max_conn = 5;
251 bool ssid_hidden = 6;
252 int32 bw = 7;
253}
254
255message CtrlMsg_Resp_StartSoftAP {
256 int32 resp = 1;
257 bytes mac = 2;
258}
259
260message CtrlMsg_Req_ScanResult {
261}
262
263message CtrlMsg_Resp_ScanResult {
264 uint32 count = 1;
265 repeated ScanResult entries = 2;
266 int32 resp = 3;
267}
268
269message CtrlMsg_Req_SoftAPConnectedSTA {
270}
271
272message CtrlMsg_Resp_SoftAPConnectedSTA {
273 uint32 num = 1;
274 repeated ConnectedSTAList stations = 2;
275 int32 resp = 3;
276}
277
278message CtrlMsg_Req_OTABegin {
279}
280
281message CtrlMsg_Resp_OTABegin {
282 int32 resp = 1;
283}
284
285message CtrlMsg_Req_OTAWrite {
286 bytes ota_data = 1;
287}
288
289message CtrlMsg_Resp_OTAWrite {
290 int32 resp = 1;
291}
292
293message CtrlMsg_Req_OTAEnd {
294}
295
296message CtrlMsg_Resp_OTAEnd {
297 int32 resp = 1;
298}
299
300message CtrlMsg_Req_VendorIEData {
301 int32 element_id = 1;
302 int32 length = 2;
303 bytes vendor_oui = 3;
304 int32 vendor_oui_type = 4;
305 bytes payload = 5;
306}
307
308message CtrlMsg_Req_SetSoftAPVendorSpecificIE {
309 bool enable = 1;
310 Ctrl_VendorIEType type = 2;
311 Ctrl_VendorIEID idx = 3;
312 CtrlMsg_Req_VendorIEData vendor_ie_data = 4;
313}
314
315message CtrlMsg_Resp_SetSoftAPVendorSpecificIE {
316 int32 resp = 1;
317}
318
319message CtrlMsg_Req_SetWifiMaxTxPower {
320 int32 wifi_max_tx_power = 1;
321}
322
323message CtrlMsg_Resp_SetWifiMaxTxPower {
324 int32 resp = 1;
325}
326
327message CtrlMsg_Req_GetWifiCurrTxPower {
328}
329
330message CtrlMsg_Resp_GetWifiCurrTxPower {
331 int32 wifi_curr_tx_power = 1;
332 int32 resp = 2;
333}
334
335message CtrlMsg_Req_ConfigHeartbeat {
336 bool enable = 1;
337 int32 duration = 2;
338}
339
340message CtrlMsg_Resp_ConfigHeartbeat {
341 int32 resp = 1;
342}
343
344/** Event structure **/
345message CtrlMsg_Event_ESPInit {
346 bytes init_data = 1;
347}
348
349message CtrlMsg_Event_Heartbeat {
350 int32 hb_num = 1;
351}
352
353message CtrlMsg_Event_StationDisconnectFromAP {
354 int32 resp = 1;
355}
356
357message CtrlMsg_Event_StationDisconnectFromESPSoftAP {
358 int32 resp = 1;
359 bytes mac = 2;
360}
361
362message CtrlMsg {
363 /* msg_type could be req, resp or Event */
364 CtrlMsgType msg_type = 1;
365
366 /* msg id */
367 CtrlMsgId msg_id = 2;
368
369 /* union of all msg ids */
370 oneof payload {
371 /** Requests **/
372 CtrlMsg_Req_GetMacAddress req_get_mac_address = 101;
373 CtrlMsg_Req_SetMacAddress req_set_mac_address = 102;
374 CtrlMsg_Req_GetMode req_get_wifi_mode = 103;
375 CtrlMsg_Req_SetMode req_set_wifi_mode = 104;
376
377 CtrlMsg_Req_ScanResult req_scan_ap_list = 105;
378 CtrlMsg_Req_GetAPConfig req_get_ap_config = 106;
379 CtrlMsg_Req_ConnectAP req_connect_ap = 107;
380 CtrlMsg_Req_GetStatus req_disconnect_ap = 108;
381
382 CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109;
383 CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110;
384 CtrlMsg_Req_StartSoftAP req_start_softap = 111;
385 CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112;
386 CtrlMsg_Req_GetStatus req_stop_softap = 113;
387
388 CtrlMsg_Req_SetMode req_set_power_save_mode = 114;
389 CtrlMsg_Req_GetMode req_get_power_save_mode = 115;
390
391 CtrlMsg_Req_OTABegin req_ota_begin = 116;
392 CtrlMsg_Req_OTAWrite req_ota_write = 117;
393 CtrlMsg_Req_OTAEnd req_ota_end = 118;
394
395 CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119;
396 CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120;
397 CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121;
398
399 /** Responses **/
400 CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201;
401 CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202;
402 CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203;
403 CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204;
404
405 CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205;
406 CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206;
407 CtrlMsg_Resp_ConnectAP resp_connect_ap = 207;
408 CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208;
409
410 CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209;
411 CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210;
412 CtrlMsg_Resp_StartSoftAP resp_start_softap = 211;
413 CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212;
414 CtrlMsg_Resp_GetStatus resp_stop_softap = 213;
415
416 CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214;
417 CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215;
418
419 CtrlMsg_Resp_OTABegin resp_ota_begin = 216;
420 CtrlMsg_Resp_OTAWrite resp_ota_write = 217;
421 CtrlMsg_Resp_OTAEnd resp_ota_end = 218;
422 CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219;
423 CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220;
424 CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221;
425
426 /** Notifications **/
427 CtrlMsg_Event_ESPInit event_esp_init = 301;
428 CtrlMsg_Event_Heartbeat event_heartbeat = 302;
429 CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303;
430 CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304;
431 }
432}
diff --git a/embassy-net-esp-hosted/src/fmt.rs b/embassy-net-esp-hosted/src/fmt.rs
new file mode 100644
index 000000000..91984bde1
--- /dev/null
+++ b/embassy-net-esp-hosted/src/fmt.rs
@@ -0,0 +1,257 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9macro_rules! assert {
10 ($($x:tt)*) => {
11 {
12 #[cfg(not(feature = "defmt"))]
13 ::core::assert!($($x)*);
14 #[cfg(feature = "defmt")]
15 ::defmt::assert!($($x)*);
16 }
17 };
18}
19
20macro_rules! assert_eq {
21 ($($x:tt)*) => {
22 {
23 #[cfg(not(feature = "defmt"))]
24 ::core::assert_eq!($($x)*);
25 #[cfg(feature = "defmt")]
26 ::defmt::assert_eq!($($x)*);
27 }
28 };
29}
30
31macro_rules! assert_ne {
32 ($($x:tt)*) => {
33 {
34 #[cfg(not(feature = "defmt"))]
35 ::core::assert_ne!($($x)*);
36 #[cfg(feature = "defmt")]
37 ::defmt::assert_ne!($($x)*);
38 }
39 };
40}
41
42macro_rules! debug_assert {
43 ($($x:tt)*) => {
44 {
45 #[cfg(not(feature = "defmt"))]
46 ::core::debug_assert!($($x)*);
47 #[cfg(feature = "defmt")]
48 ::defmt::debug_assert!($($x)*);
49 }
50 };
51}
52
53macro_rules! debug_assert_eq {
54 ($($x:tt)*) => {
55 {
56 #[cfg(not(feature = "defmt"))]
57 ::core::debug_assert_eq!($($x)*);
58 #[cfg(feature = "defmt")]
59 ::defmt::debug_assert_eq!($($x)*);
60 }
61 };
62}
63
64macro_rules! debug_assert_ne {
65 ($($x:tt)*) => {
66 {
67 #[cfg(not(feature = "defmt"))]
68 ::core::debug_assert_ne!($($x)*);
69 #[cfg(feature = "defmt")]
70 ::defmt::debug_assert_ne!($($x)*);
71 }
72 };
73}
74
75macro_rules! todo {
76 ($($x:tt)*) => {
77 {
78 #[cfg(not(feature = "defmt"))]
79 ::core::todo!($($x)*);
80 #[cfg(feature = "defmt")]
81 ::defmt::todo!($($x)*);
82 }
83 };
84}
85
86#[cfg(not(feature = "defmt"))]
87macro_rules! unreachable {
88 ($($x:tt)*) => {
89 ::core::unreachable!($($x)*)
90 };
91}
92
93#[cfg(feature = "defmt")]
94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 ::defmt::unreachable!($($x)*);
97 };
98}
99
100macro_rules! panic {
101 ($($x:tt)*) => {
102 {
103 #[cfg(not(feature = "defmt"))]
104 ::core::panic!($($x)*);
105 #[cfg(feature = "defmt")]
106 ::defmt::panic!($($x)*);
107 }
108 };
109}
110
111macro_rules! trace {
112 ($s:literal $(, $x:expr)* $(,)?) => {
113 {
114 #[cfg(feature = "log")]
115 ::log::trace!($s $(, $x)*);
116 #[cfg(feature = "defmt")]
117 ::defmt::trace!($s $(, $x)*);
118 #[cfg(not(any(feature = "log", feature="defmt")))]
119 let _ = ($( & $x ),*);
120 }
121 };
122}
123
124macro_rules! debug {
125 ($s:literal $(, $x:expr)* $(,)?) => {
126 {
127 #[cfg(feature = "log")]
128 ::log::debug!($s $(, $x)*);
129 #[cfg(feature = "defmt")]
130 ::defmt::debug!($s $(, $x)*);
131 #[cfg(not(any(feature = "log", feature="defmt")))]
132 let _ = ($( & $x ),*);
133 }
134 };
135}
136
137macro_rules! info {
138 ($s:literal $(, $x:expr)* $(,)?) => {
139 {
140 #[cfg(feature = "log")]
141 ::log::info!($s $(, $x)*);
142 #[cfg(feature = "defmt")]
143 ::defmt::info!($s $(, $x)*);
144 #[cfg(not(any(feature = "log", feature="defmt")))]
145 let _ = ($( & $x ),*);
146 }
147 };
148}
149
150macro_rules! warn {
151 ($s:literal $(, $x:expr)* $(,)?) => {
152 {
153 #[cfg(feature = "log")]
154 ::log::warn!($s $(, $x)*);
155 #[cfg(feature = "defmt")]
156 ::defmt::warn!($s $(, $x)*);
157 #[cfg(not(any(feature = "log", feature="defmt")))]
158 let _ = ($( & $x ),*);
159 }
160 };
161}
162
163macro_rules! error {
164 ($s:literal $(, $x:expr)* $(,)?) => {
165 {
166 #[cfg(feature = "log")]
167 ::log::error!($s $(, $x)*);
168 #[cfg(feature = "defmt")]
169 ::defmt::error!($s $(, $x)*);
170 #[cfg(not(any(feature = "log", feature="defmt")))]
171 let _ = ($( & $x ),*);
172 }
173 };
174}
175
176#[cfg(feature = "defmt")]
177macro_rules! unwrap {
178 ($($x:tt)*) => {
179 ::defmt::unwrap!($($x)*)
180 };
181}
182
183#[cfg(not(feature = "defmt"))]
184macro_rules! unwrap {
185 ($arg:expr) => {
186 match $crate::fmt::Try::into_result($arg) {
187 ::core::result::Result::Ok(t) => t,
188 ::core::result::Result::Err(e) => {
189 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
190 }
191 }
192 };
193 ($arg:expr, $($msg:expr),+ $(,)? ) => {
194 match $crate::fmt::Try::into_result($arg) {
195 ::core::result::Result::Ok(t) => t,
196 ::core::result::Result::Err(e) => {
197 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
198 }
199 }
200 }
201}
202
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204pub struct NoneError;
205
206pub trait Try {
207 type Ok;
208 type Error;
209 fn into_result(self) -> Result<Self::Ok, Self::Error>;
210}
211
212impl<T> Try for Option<T> {
213 type Ok = T;
214 type Error = NoneError;
215
216 #[inline]
217 fn into_result(self) -> Result<T, NoneError> {
218 self.ok_or(NoneError)
219 }
220}
221
222impl<T, E> Try for Result<T, E> {
223 type Ok = T;
224 type Error = E;
225
226 #[inline]
227 fn into_result(self) -> Self {
228 self
229 }
230}
231
232pub struct Bytes<'a>(pub &'a [u8]);
233
234impl<'a> Debug for Bytes<'a> {
235 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
236 write!(f, "{:#02x?}", self.0)
237 }
238}
239
240impl<'a> Display for Bytes<'a> {
241 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
242 write!(f, "{:#02x?}", self.0)
243 }
244}
245
246impl<'a> LowerHex for Bytes<'a> {
247 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
248 write!(f, "{:#02x?}", self.0)
249 }
250}
251
252#[cfg(feature = "defmt")]
253impl<'a> defmt::Format for Bytes<'a> {
254 fn format(&self, fmt: defmt::Formatter) {
255 defmt::write!(fmt, "{:02x}", self.0)
256 }
257}
diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs
new file mode 100644
index 000000000..e2a6815aa
--- /dev/null
+++ b/embassy-net-esp-hosted/src/ioctl.rs
@@ -0,0 +1,123 @@
1use core::cell::RefCell;
2use core::future::poll_fn;
3use core::task::Poll;
4
5use embassy_sync::waitqueue::WakerRegistration;
6
7use crate::fmt::Bytes;
8
9#[derive(Clone, Copy)]
10pub struct PendingIoctl {
11 pub buf: *mut [u8],
12 pub req_len: usize,
13}
14
15#[derive(Clone, Copy)]
16enum IoctlState {
17 Pending(PendingIoctl),
18 Sent { buf: *mut [u8] },
19 Done { resp_len: usize },
20}
21
22pub struct Shared(RefCell<SharedInner>);
23
24struct SharedInner {
25 ioctl: IoctlState,
26 is_init: bool,
27 control_waker: WakerRegistration,
28 runner_waker: WakerRegistration,
29}
30
31impl Shared {
32 pub fn new() -> Self {
33 Self(RefCell::new(SharedInner {
34 ioctl: IoctlState::Done { resp_len: 0 },
35 is_init: false,
36 control_waker: WakerRegistration::new(),
37 runner_waker: WakerRegistration::new(),
38 }))
39 }
40
41 pub async fn ioctl_wait_complete(&self) -> usize {
42 poll_fn(|cx| {
43 let mut this = self.0.borrow_mut();
44 if let IoctlState::Done { resp_len } = this.ioctl {
45 Poll::Ready(resp_len)
46 } else {
47 this.control_waker.register(cx.waker());
48 Poll::Pending
49 }
50 })
51 .await
52 }
53
54 pub async fn ioctl_wait_pending(&self) -> PendingIoctl {
55 let pending = poll_fn(|cx| {
56 let mut this = self.0.borrow_mut();
57 if let IoctlState::Pending(pending) = this.ioctl {
58 Poll::Ready(pending)
59 } else {
60 this.runner_waker.register(cx.waker());
61 Poll::Pending
62 }
63 })
64 .await;
65
66 self.0.borrow_mut().ioctl = IoctlState::Sent { buf: pending.buf };
67 pending
68 }
69
70 pub fn ioctl_cancel(&self) {
71 self.0.borrow_mut().ioctl = IoctlState::Done { resp_len: 0 };
72 }
73
74 pub async fn ioctl(&self, buf: &mut [u8], req_len: usize) -> usize {
75 trace!("ioctl req bytes: {:02x}", Bytes(&buf[..req_len]));
76
77 {
78 let mut this = self.0.borrow_mut();
79 this.ioctl = IoctlState::Pending(PendingIoctl { buf, req_len });
80 this.runner_waker.wake();
81 }
82
83 self.ioctl_wait_complete().await
84 }
85
86 pub fn ioctl_done(&self, response: &[u8]) {
87 let mut this = self.0.borrow_mut();
88 if let IoctlState::Sent { buf } = this.ioctl {
89 trace!("ioctl resp bytes: {:02x}", Bytes(response));
90
91 // TODO fix this
92 (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response);
93
94 this.ioctl = IoctlState::Done {
95 resp_len: response.len(),
96 };
97 this.control_waker.wake();
98 } else {
99 warn!("IOCTL Response but no pending Ioctl");
100 }
101 }
102
103 // // // // // // // // // // // // // // // // // // // //
104
105 pub fn init_done(&self) {
106 let mut this = self.0.borrow_mut();
107 this.is_init = true;
108 this.control_waker.wake();
109 }
110
111 pub async fn init_wait(&self) {
112 poll_fn(|cx| {
113 let mut this = self.0.borrow_mut();
114 if this.is_init {
115 Poll::Ready(())
116 } else {
117 this.control_waker.register(cx.waker());
118 Poll::Pending
119 }
120 })
121 .await
122 }
123}
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs
new file mode 100644
index 000000000..44dfbe89c
--- /dev/null
+++ b/embassy-net-esp-hosted/src/lib.rs
@@ -0,0 +1,337 @@
1#![no_std]
2
3use control::Control;
4use embassy_futures::select::{select3, Either3};
5use embassy_net_driver_channel as ch;
6use embassy_time::{Duration, Instant, Timer};
7use embedded_hal::digital::{InputPin, OutputPin};
8use embedded_hal_async::digital::Wait;
9use embedded_hal_async::spi::SpiDevice;
10use ioctl::Shared;
11use proto::CtrlMsg;
12
13use crate::ioctl::PendingIoctl;
14use crate::proto::CtrlMsgPayload;
15
16mod proto;
17
18// must be first
19mod fmt;
20
21mod control;
22mod ioctl;
23
24const MTU: usize = 1514;
25
26macro_rules! impl_bytes {
27 ($t:ident) => {
28 impl $t {
29 pub const SIZE: usize = core::mem::size_of::<Self>();
30
31 #[allow(unused)]
32 pub fn to_bytes(&self) -> [u8; Self::SIZE] {
33 unsafe { core::mem::transmute(*self) }
34 }
35
36 #[allow(unused)]
37 pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {
38 let alignment = core::mem::align_of::<Self>();
39 assert_eq!(
40 bytes.as_ptr().align_offset(alignment),
41 0,
42 "{} is not aligned",
43 core::any::type_name::<Self>()
44 );
45 unsafe { core::mem::transmute(bytes) }
46 }
47
48 #[allow(unused)]
49 pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {
50 let alignment = core::mem::align_of::<Self>();
51 assert_eq!(
52 bytes.as_ptr().align_offset(alignment),
53 0,
54 "{} is not aligned",
55 core::any::type_name::<Self>()
56 );
57
58 unsafe { core::mem::transmute(bytes) }
59 }
60 }
61 };
62}
63
64#[repr(C, packed)]
65#[derive(Clone, Copy, Debug, Default)]
66struct PayloadHeader {
67 /// InterfaceType on lower 4 bits, number on higher 4 bits.
68 if_type_and_num: u8,
69
70 /// Flags.
71 ///
72 /// bit 0: more fragments.
73 flags: u8,
74
75 len: u16,
76 offset: u16,
77 checksum: u16,
78 seq_num: u16,
79 reserved2: u8,
80
81 /// Packet type for HCI or PRIV interface, reserved otherwise
82 hci_priv_packet_type: u8,
83}
84impl_bytes!(PayloadHeader);
85
86#[allow(unused)]
87#[repr(u8)]
88enum InterfaceType {
89 Sta = 0,
90 Ap = 1,
91 Serial = 2,
92 Hci = 3,
93 Priv = 4,
94 Test = 5,
95}
96
97const MAX_SPI_BUFFER_SIZE: usize = 1600;
98
99pub struct State {
100 shared: Shared,
101 ch: ch::State<MTU, 4, 4>,
102}
103
104impl State {
105 pub fn new() -> Self {
106 Self {
107 shared: Shared::new(),
108 ch: ch::State::new(),
109 }
110 }
111}
112
113pub type NetDriver<'a> = ch::Device<'a, MTU>;
114
115pub async fn new<'a, SPI, IN, OUT>(
116 state: &'a mut State,
117 spi: SPI,
118 handshake: IN,
119 ready: IN,
120 reset: OUT,
121) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>)
122where
123 SPI: SpiDevice,
124 IN: InputPin + Wait,
125 OUT: OutputPin,
126{
127 let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
128 let state_ch = ch_runner.state_runner();
129
130 let mut runner = Runner {
131 ch: ch_runner,
132 shared: &state.shared,
133 next_seq: 1,
134 handshake,
135 ready,
136 reset,
137 spi,
138 };
139 runner.init().await;
140
141 (device, Control::new(state_ch, &state.shared), runner)
142}
143
144pub struct Runner<'a, SPI, IN, OUT> {
145 ch: ch::Runner<'a, MTU>,
146 shared: &'a Shared,
147
148 next_seq: u16,
149
150 spi: SPI,
151 handshake: IN,
152 ready: IN,
153 reset: OUT,
154}
155
156impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT>
157where
158 SPI: SpiDevice,
159 IN: InputPin + Wait,
160 OUT: OutputPin,
161{
162 async fn init(&mut self) {}
163
164 pub async fn run(mut self) -> ! {
165 debug!("resetting...");
166 self.reset.set_low().unwrap();
167 Timer::after(Duration::from_millis(100)).await;
168 self.reset.set_high().unwrap();
169 Timer::after(Duration::from_millis(1000)).await;
170
171 let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
172 let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
173
174 loop {
175 self.handshake.wait_for_high().await.unwrap();
176
177 let ioctl = self.shared.ioctl_wait_pending();
178 let tx = self.ch.tx_buf();
179 let ev = async { self.ready.wait_for_high().await.unwrap() };
180
181 match select3(ioctl, tx, ev).await {
182 Either3::First(PendingIoctl { buf, req_len }) => {
183 tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02");
184 tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes());
185 tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);
186
187 let mut header = PayloadHeader {
188 if_type_and_num: InterfaceType::Serial as _,
189 len: (req_len + 14) as _,
190 offset: PayloadHeader::SIZE as _,
191 seq_num: self.next_seq,
192 ..Default::default()
193 };
194 self.next_seq = self.next_seq.wrapping_add(1);
195
196 // Calculate checksum
197 tx_buf[0..12].copy_from_slice(&header.to_bytes());
198 header.checksum = checksum(&tx_buf[..26 + req_len]);
199 tx_buf[0..12].copy_from_slice(&header.to_bytes());
200 }
201 Either3::Second(packet) => {
202 tx_buf[12..][..packet.len()].copy_from_slice(packet);
203
204 let mut header = PayloadHeader {
205 if_type_and_num: InterfaceType::Sta as _,
206 len: packet.len() as _,
207 offset: PayloadHeader::SIZE as _,
208 seq_num: self.next_seq,
209 ..Default::default()
210 };
211 self.next_seq = self.next_seq.wrapping_add(1);
212
213 // Calculate checksum
214 tx_buf[0..12].copy_from_slice(&header.to_bytes());
215 header.checksum = checksum(&tx_buf[..12 + packet.len()]);
216 tx_buf[0..12].copy_from_slice(&header.to_bytes());
217
218 self.ch.tx_done();
219 }
220 Either3::Third(()) => {
221 tx_buf[..PayloadHeader::SIZE].fill(0);
222 }
223 }
224
225 if tx_buf[0] != 0 {
226 trace!("tx: {:02x}", &tx_buf[..40]);
227 }
228
229 self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
230
231 // The esp-hosted firmware deasserts the HANSHAKE pin a few us AFTER ending the SPI transfer
232 // If we check it again too fast, we'll see it's high from the previous transfer, and if we send it
233 // data it will get lost.
234 // Make sure we check it after 100us at minimum.
235 let delay_until = Instant::now() + Duration::from_micros(100);
236 self.handle_rx(&mut rx_buf);
237 Timer::at(delay_until).await;
238 }
239 }
240
241 fn handle_rx(&mut self, buf: &mut [u8]) {
242 trace!("rx: {:02x}", &buf[..40]);
243
244 let buf_len = buf.len();
245 let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap());
246
247 if h.len == 0 || h.offset as usize != PayloadHeader::SIZE {
248 return;
249 }
250
251 let payload_len = h.len as usize;
252 if buf_len < PayloadHeader::SIZE + payload_len {
253 warn!("rx: len too big");
254 return;
255 }
256
257 let if_type_and_num = h.if_type_and_num;
258 let want_checksum = h.checksum;
259 h.checksum = 0;
260 let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]);
261 if want_checksum != got_checksum {
262 warn!("rx: bad checksum. Got {:04x}, want {:04x}", got_checksum, want_checksum);
263 return;
264 }
265
266 let payload = &mut buf[PayloadHeader::SIZE..][..payload_len];
267
268 match if_type_and_num & 0x0f {
269 // STA
270 0 => match self.ch.try_rx_buf() {
271 Some(buf) => {
272 buf[..payload.len()].copy_from_slice(payload);
273 self.ch.rx_done(payload.len())
274 }
275 None => warn!("failed to push rxd packet to the channel."),
276 },
277 // serial
278 2 => {
279 trace!("serial rx: {:02x}", payload);
280 if payload.len() < 14 {
281 warn!("serial rx: too short");
282 return;
283 }
284
285 let is_event = match &payload[..12] {
286 b"\x01\x08\x00ctrlResp\x02" => false,
287 b"\x01\x08\x00ctrlEvnt\x02" => true,
288 _ => {
289 warn!("serial rx: bad tlv");
290 return;
291 }
292 };
293
294 let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize;
295 if payload.len() < 14 + len {
296 warn!("serial rx: too short 2");
297 return;
298 }
299 let data = &payload[14..][..len];
300
301 if is_event {
302 self.handle_event(data);
303 } else {
304 self.shared.ioctl_done(data);
305 }
306 }
307 _ => warn!("unknown iftype {}", if_type_and_num),
308 }
309 }
310
311 fn handle_event(&self, data: &[u8]) {
312 let Ok(event) = noproto::read::<CtrlMsg>(data) else {
313 warn!("failed to parse event");
314 return
315 };
316
317 debug!("event: {:?}", &event);
318
319 let Some(payload) = &event.payload else {
320 warn!("event without payload?");
321 return
322 };
323
324 match payload {
325 CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(),
326 _ => {}
327 }
328 }
329}
330
331fn checksum(buf: &[u8]) -> u16 {
332 let mut res = 0u16;
333 for &b in buf {
334 res = res.wrapping_add(b as _);
335 }
336 res
337}
diff --git a/embassy-net-esp-hosted/src/proto.rs b/embassy-net-esp-hosted/src/proto.rs
new file mode 100644
index 000000000..8ceb1579d
--- /dev/null
+++ b/embassy-net-esp-hosted/src/proto.rs
@@ -0,0 +1,652 @@
1use heapless::{String, Vec};
2
3/// internal supporting structures for CtrlMsg
4
5#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub struct ScanResult {
8 #[noproto(tag = "1")]
9 pub ssid: String<32>,
10 #[noproto(tag = "2")]
11 pub chnl: u32,
12 #[noproto(tag = "3")]
13 pub rssi: u32,
14 #[noproto(tag = "4")]
15 pub bssid: String<32>,
16 #[noproto(tag = "5")]
17 pub sec_prot: CtrlWifiSecProt,
18}
19
20#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22pub struct ConnectedStaList {
23 #[noproto(tag = "1")]
24 pub mac: String<32>,
25 #[noproto(tag = "2")]
26 pub rssi: u32,
27}
28/// * Req/Resp structure *
29
30#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32pub struct CtrlMsgReqGetMacAddress {
33 #[noproto(tag = "1")]
34 pub mode: u32,
35}
36
37#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
38#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39pub struct CtrlMsgRespGetMacAddress {
40 #[noproto(tag = "1")]
41 pub mac: String<32>,
42 #[noproto(tag = "2")]
43 pub resp: u32,
44}
45
46#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub struct CtrlMsgReqGetMode {}
49
50#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
51#[cfg_attr(feature = "defmt", derive(defmt::Format))]
52pub struct CtrlMsgRespGetMode {
53 #[noproto(tag = "1")]
54 pub mode: u32,
55 #[noproto(tag = "2")]
56 pub resp: u32,
57}
58
59#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61pub struct CtrlMsgReqSetMode {
62 #[noproto(tag = "1")]
63 pub mode: u32,
64}
65
66#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68pub struct CtrlMsgRespSetMode {
69 #[noproto(tag = "1")]
70 pub resp: u32,
71}
72
73#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
74#[cfg_attr(feature = "defmt", derive(defmt::Format))]
75pub struct CtrlMsgReqGetStatus {}
76
77#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
78#[cfg_attr(feature = "defmt", derive(defmt::Format))]
79pub struct CtrlMsgRespGetStatus {
80 #[noproto(tag = "1")]
81 pub resp: u32,
82}
83
84#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub struct CtrlMsgReqSetMacAddress {
87 #[noproto(tag = "1")]
88 pub mac: String<32>,
89 #[noproto(tag = "2")]
90 pub mode: u32,
91}
92
93#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95pub struct CtrlMsgRespSetMacAddress {
96 #[noproto(tag = "1")]
97 pub resp: u32,
98}
99
100#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102pub struct CtrlMsgReqGetApConfig {}
103
104#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
105#[cfg_attr(feature = "defmt", derive(defmt::Format))]
106pub struct CtrlMsgRespGetApConfig {
107 #[noproto(tag = "1")]
108 pub ssid: String<32>,
109 #[noproto(tag = "2")]
110 pub bssid: String<32>,
111 #[noproto(tag = "3")]
112 pub rssi: u32,
113 #[noproto(tag = "4")]
114 pub chnl: u32,
115 #[noproto(tag = "5")]
116 pub sec_prot: CtrlWifiSecProt,
117 #[noproto(tag = "6")]
118 pub resp: u32,
119}
120
121#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
122#[cfg_attr(feature = "defmt", derive(defmt::Format))]
123pub struct CtrlMsgReqConnectAp {
124 #[noproto(tag = "1")]
125 pub ssid: String<32>,
126 #[noproto(tag = "2")]
127 pub pwd: String<32>,
128 #[noproto(tag = "3")]
129 pub bssid: String<32>,
130 #[noproto(tag = "4")]
131 pub is_wpa3_supported: bool,
132 #[noproto(tag = "5")]
133 pub listen_interval: u32,
134}
135
136#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub struct CtrlMsgRespConnectAp {
139 #[noproto(tag = "1")]
140 pub resp: u32,
141 #[noproto(tag = "2")]
142 pub mac: String<32>,
143}
144
145#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
146#[cfg_attr(feature = "defmt", derive(defmt::Format))]
147pub struct CtrlMsgReqGetSoftApConfig {}
148
149#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
150#[cfg_attr(feature = "defmt", derive(defmt::Format))]
151pub struct CtrlMsgRespGetSoftApConfig {
152 #[noproto(tag = "1")]
153 pub ssid: String<32>,
154 #[noproto(tag = "2")]
155 pub pwd: String<32>,
156 #[noproto(tag = "3")]
157 pub chnl: u32,
158 #[noproto(tag = "4")]
159 pub sec_prot: CtrlWifiSecProt,
160 #[noproto(tag = "5")]
161 pub max_conn: u32,
162 #[noproto(tag = "6")]
163 pub ssid_hidden: bool,
164 #[noproto(tag = "7")]
165 pub bw: u32,
166 #[noproto(tag = "8")]
167 pub resp: u32,
168}
169
170#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
171#[cfg_attr(feature = "defmt", derive(defmt::Format))]
172pub struct CtrlMsgReqStartSoftAp {
173 #[noproto(tag = "1")]
174 pub ssid: String<32>,
175 #[noproto(tag = "2")]
176 pub pwd: String<32>,
177 #[noproto(tag = "3")]
178 pub chnl: u32,
179 #[noproto(tag = "4")]
180 pub sec_prot: CtrlWifiSecProt,
181 #[noproto(tag = "5")]
182 pub max_conn: u32,
183 #[noproto(tag = "6")]
184 pub ssid_hidden: bool,
185 #[noproto(tag = "7")]
186 pub bw: u32,
187}
188
189#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
190#[cfg_attr(feature = "defmt", derive(defmt::Format))]
191pub struct CtrlMsgRespStartSoftAp {
192 #[noproto(tag = "1")]
193 pub resp: u32,
194 #[noproto(tag = "2")]
195 pub mac: String<32>,
196}
197
198#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
199#[cfg_attr(feature = "defmt", derive(defmt::Format))]
200pub struct CtrlMsgReqScanResult {}
201
202#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
204pub struct CtrlMsgRespScanResult {
205 #[noproto(tag = "1")]
206 pub count: u32,
207 #[noproto(repeated, tag = "2")]
208 pub entries: Vec<ScanResult, 16>,
209 #[noproto(tag = "3")]
210 pub resp: u32,
211}
212
213#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
214#[cfg_attr(feature = "defmt", derive(defmt::Format))]
215pub struct CtrlMsgReqSoftApConnectedSta {}
216
217#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
218#[cfg_attr(feature = "defmt", derive(defmt::Format))]
219pub struct CtrlMsgRespSoftApConnectedSta {
220 #[noproto(tag = "1")]
221 pub num: u32,
222 #[noproto(repeated, tag = "2")]
223 pub stations: Vec<ConnectedStaList, 16>,
224 #[noproto(tag = "3")]
225 pub resp: u32,
226}
227
228#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
229#[cfg_attr(feature = "defmt", derive(defmt::Format))]
230pub struct CtrlMsgReqOtaBegin {}
231
232#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
233#[cfg_attr(feature = "defmt", derive(defmt::Format))]
234pub struct CtrlMsgRespOtaBegin {
235 #[noproto(tag = "1")]
236 pub resp: u32,
237}
238
239#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
240#[cfg_attr(feature = "defmt", derive(defmt::Format))]
241pub struct CtrlMsgReqOtaWrite {
242 #[noproto(tag = "1")]
243 pub ota_data: Vec<u8, 1024>,
244}
245
246#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
247#[cfg_attr(feature = "defmt", derive(defmt::Format))]
248pub struct CtrlMsgRespOtaWrite {
249 #[noproto(tag = "1")]
250 pub resp: u32,
251}
252
253#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
254#[cfg_attr(feature = "defmt", derive(defmt::Format))]
255pub struct CtrlMsgReqOtaEnd {}
256
257#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
258#[cfg_attr(feature = "defmt", derive(defmt::Format))]
259pub struct CtrlMsgRespOtaEnd {
260 #[noproto(tag = "1")]
261 pub resp: u32,
262}
263
264#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
265#[cfg_attr(feature = "defmt", derive(defmt::Format))]
266pub struct CtrlMsgReqVendorIeData {
267 #[noproto(tag = "1")]
268 pub element_id: u32,
269 #[noproto(tag = "2")]
270 pub length: u32,
271 #[noproto(tag = "3")]
272 pub vendor_oui: Vec<u8, 8>,
273 #[noproto(tag = "4")]
274 pub vendor_oui_type: u32,
275 #[noproto(tag = "5")]
276 pub payload: Vec<u8, 64>,
277}
278
279#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
280#[cfg_attr(feature = "defmt", derive(defmt::Format))]
281pub struct CtrlMsgReqSetSoftApVendorSpecificIe {
282 #[noproto(tag = "1")]
283 pub enable: bool,
284 #[noproto(tag = "2")]
285 pub r#type: CtrlVendorIeType,
286 #[noproto(tag = "3")]
287 pub idx: CtrlVendorIeid,
288 #[noproto(optional, tag = "4")]
289 pub vendor_ie_data: Option<CtrlMsgReqVendorIeData>,
290}
291
292#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
293#[cfg_attr(feature = "defmt", derive(defmt::Format))]
294pub struct CtrlMsgRespSetSoftApVendorSpecificIe {
295 #[noproto(tag = "1")]
296 pub resp: u32,
297}
298
299#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
300#[cfg_attr(feature = "defmt", derive(defmt::Format))]
301pub struct CtrlMsgReqSetWifiMaxTxPower {
302 #[noproto(tag = "1")]
303 pub wifi_max_tx_power: u32,
304}
305
306#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
307#[cfg_attr(feature = "defmt", derive(defmt::Format))]
308pub struct CtrlMsgRespSetWifiMaxTxPower {
309 #[noproto(tag = "1")]
310 pub resp: u32,
311}
312
313#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
314#[cfg_attr(feature = "defmt", derive(defmt::Format))]
315pub struct CtrlMsgReqGetWifiCurrTxPower {}
316
317#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
318#[cfg_attr(feature = "defmt", derive(defmt::Format))]
319pub struct CtrlMsgRespGetWifiCurrTxPower {
320 #[noproto(tag = "1")]
321 pub wifi_curr_tx_power: u32,
322 #[noproto(tag = "2")]
323 pub resp: u32,
324}
325
326#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
327#[cfg_attr(feature = "defmt", derive(defmt::Format))]
328pub struct CtrlMsgReqConfigHeartbeat {
329 #[noproto(tag = "1")]
330 pub enable: bool,
331 #[noproto(tag = "2")]
332 pub duration: u32,
333}
334
335#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
336#[cfg_attr(feature = "defmt", derive(defmt::Format))]
337pub struct CtrlMsgRespConfigHeartbeat {
338 #[noproto(tag = "1")]
339 pub resp: u32,
340}
341/// * Event structure *
342
343#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
344#[cfg_attr(feature = "defmt", derive(defmt::Format))]
345pub struct CtrlMsgEventEspInit {
346 #[noproto(tag = "1")]
347 pub init_data: Vec<u8, 64>,
348}
349
350#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
351#[cfg_attr(feature = "defmt", derive(defmt::Format))]
352pub struct CtrlMsgEventHeartbeat {
353 #[noproto(tag = "1")]
354 pub hb_num: u32,
355}
356
357#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
358#[cfg_attr(feature = "defmt", derive(defmt::Format))]
359pub struct CtrlMsgEventStationDisconnectFromAp {
360 #[noproto(tag = "1")]
361 pub resp: u32,
362}
363
364#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
365#[cfg_attr(feature = "defmt", derive(defmt::Format))]
366pub struct CtrlMsgEventStationDisconnectFromEspSoftAp {
367 #[noproto(tag = "1")]
368 pub resp: u32,
369 #[noproto(tag = "2")]
370 pub mac: String<32>,
371}
372
373#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
374#[cfg_attr(feature = "defmt", derive(defmt::Format))]
375pub struct CtrlMsg {
376 /// msg_type could be req, resp or Event
377 #[noproto(tag = "1")]
378 pub msg_type: CtrlMsgType,
379 /// msg id
380 #[noproto(tag = "2")]
381 pub msg_id: CtrlMsgId,
382 /// union of all msg ids
383 #[noproto(
384 oneof,
385 tags = "101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 301, 302, 303, 304"
386 )]
387 pub payload: Option<CtrlMsgPayload>,
388}
389
390/// union of all msg ids
391#[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)]
392#[cfg_attr(feature = "defmt", derive(defmt::Format))]
393pub enum CtrlMsgPayload {
394 /// * Requests *
395 #[noproto(tag = "101")]
396 ReqGetMacAddress(CtrlMsgReqGetMacAddress),
397 #[noproto(tag = "102")]
398 ReqSetMacAddress(CtrlMsgReqSetMacAddress),
399 #[noproto(tag = "103")]
400 ReqGetWifiMode(CtrlMsgReqGetMode),
401 #[noproto(tag = "104")]
402 ReqSetWifiMode(CtrlMsgReqSetMode),
403 #[noproto(tag = "105")]
404 ReqScanApList(CtrlMsgReqScanResult),
405 #[noproto(tag = "106")]
406 ReqGetApConfig(CtrlMsgReqGetApConfig),
407 #[noproto(tag = "107")]
408 ReqConnectAp(CtrlMsgReqConnectAp),
409 #[noproto(tag = "108")]
410 ReqDisconnectAp(CtrlMsgReqGetStatus),
411 #[noproto(tag = "109")]
412 ReqGetSoftapConfig(CtrlMsgReqGetSoftApConfig),
413 #[noproto(tag = "110")]
414 ReqSetSoftapVendorSpecificIe(CtrlMsgReqSetSoftApVendorSpecificIe),
415 #[noproto(tag = "111")]
416 ReqStartSoftap(CtrlMsgReqStartSoftAp),
417 #[noproto(tag = "112")]
418 ReqSoftapConnectedStasList(CtrlMsgReqSoftApConnectedSta),
419 #[noproto(tag = "113")]
420 ReqStopSoftap(CtrlMsgReqGetStatus),
421 #[noproto(tag = "114")]
422 ReqSetPowerSaveMode(CtrlMsgReqSetMode),
423 #[noproto(tag = "115")]
424 ReqGetPowerSaveMode(CtrlMsgReqGetMode),
425 #[noproto(tag = "116")]
426 ReqOtaBegin(CtrlMsgReqOtaBegin),
427 #[noproto(tag = "117")]
428 ReqOtaWrite(CtrlMsgReqOtaWrite),
429 #[noproto(tag = "118")]
430 ReqOtaEnd(CtrlMsgReqOtaEnd),
431 #[noproto(tag = "119")]
432 ReqSetWifiMaxTxPower(CtrlMsgReqSetWifiMaxTxPower),
433 #[noproto(tag = "120")]
434 ReqGetWifiCurrTxPower(CtrlMsgReqGetWifiCurrTxPower),
435 #[noproto(tag = "121")]
436 ReqConfigHeartbeat(CtrlMsgReqConfigHeartbeat),
437 /// * Responses *
438 #[noproto(tag = "201")]
439 RespGetMacAddress(CtrlMsgRespGetMacAddress),
440 #[noproto(tag = "202")]
441 RespSetMacAddress(CtrlMsgRespSetMacAddress),
442 #[noproto(tag = "203")]
443 RespGetWifiMode(CtrlMsgRespGetMode),
444 #[noproto(tag = "204")]
445 RespSetWifiMode(CtrlMsgRespSetMode),
446 #[noproto(tag = "205")]
447 RespScanApList(CtrlMsgRespScanResult),
448 #[noproto(tag = "206")]
449 RespGetApConfig(CtrlMsgRespGetApConfig),
450 #[noproto(tag = "207")]
451 RespConnectAp(CtrlMsgRespConnectAp),
452 #[noproto(tag = "208")]
453 RespDisconnectAp(CtrlMsgRespGetStatus),
454 #[noproto(tag = "209")]
455 RespGetSoftapConfig(CtrlMsgRespGetSoftApConfig),
456 #[noproto(tag = "210")]
457 RespSetSoftapVendorSpecificIe(CtrlMsgRespSetSoftApVendorSpecificIe),
458 #[noproto(tag = "211")]
459 RespStartSoftap(CtrlMsgRespStartSoftAp),
460 #[noproto(tag = "212")]
461 RespSoftapConnectedStasList(CtrlMsgRespSoftApConnectedSta),
462 #[noproto(tag = "213")]
463 RespStopSoftap(CtrlMsgRespGetStatus),
464 #[noproto(tag = "214")]
465 RespSetPowerSaveMode(CtrlMsgRespSetMode),
466 #[noproto(tag = "215")]
467 RespGetPowerSaveMode(CtrlMsgRespGetMode),
468 #[noproto(tag = "216")]
469 RespOtaBegin(CtrlMsgRespOtaBegin),
470 #[noproto(tag = "217")]
471 RespOtaWrite(CtrlMsgRespOtaWrite),
472 #[noproto(tag = "218")]
473 RespOtaEnd(CtrlMsgRespOtaEnd),
474 #[noproto(tag = "219")]
475 RespSetWifiMaxTxPower(CtrlMsgRespSetWifiMaxTxPower),
476 #[noproto(tag = "220")]
477 RespGetWifiCurrTxPower(CtrlMsgRespGetWifiCurrTxPower),
478 #[noproto(tag = "221")]
479 RespConfigHeartbeat(CtrlMsgRespConfigHeartbeat),
480 /// * Notifications *
481 #[noproto(tag = "301")]
482 EventEspInit(CtrlMsgEventEspInit),
483 #[noproto(tag = "302")]
484 EventHeartbeat(CtrlMsgEventHeartbeat),
485 #[noproto(tag = "303")]
486 EventStationDisconnectFromAp(CtrlMsgEventStationDisconnectFromAp),
487 #[noproto(tag = "304")]
488 EventStationDisconnectFromEspSoftAp(CtrlMsgEventStationDisconnectFromEspSoftAp),
489}
490
491/// Enums similar to ESP IDF
492#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
493#[repr(u32)]
494#[cfg_attr(feature = "defmt", derive(defmt::Format))]
495pub enum CtrlVendorIeType {
496 #[default]
497 Beacon = 0,
498 ProbeReq = 1,
499 ProbeResp = 2,
500 AssocReq = 3,
501 AssocResp = 4,
502}
503
504#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
505#[repr(u32)]
506#[cfg_attr(feature = "defmt", derive(defmt::Format))]
507pub enum CtrlVendorIeid {
508 #[default]
509 Id0 = 0,
510 Id1 = 1,
511}
512
513#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
514#[repr(u32)]
515#[cfg_attr(feature = "defmt", derive(defmt::Format))]
516pub enum CtrlWifiMode {
517 #[default]
518 None = 0,
519 Sta = 1,
520 Ap = 2,
521 Apsta = 3,
522}
523
524#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
525#[repr(u32)]
526#[cfg_attr(feature = "defmt", derive(defmt::Format))]
527pub enum CtrlWifiBw {
528 #[default]
529 BwInvalid = 0,
530 Ht20 = 1,
531 Ht40 = 2,
532}
533
534#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
535#[repr(u32)]
536#[cfg_attr(feature = "defmt", derive(defmt::Format))]
537pub enum CtrlWifiPowerSave {
538 #[default]
539 PsInvalid = 0,
540 MinModem = 1,
541 MaxModem = 2,
542}
543
544#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
545#[repr(u32)]
546#[cfg_attr(feature = "defmt", derive(defmt::Format))]
547pub enum CtrlWifiSecProt {
548 #[default]
549 Open = 0,
550 Wep = 1,
551 WpaPsk = 2,
552 Wpa2Psk = 3,
553 WpaWpa2Psk = 4,
554 Wpa2Enterprise = 5,
555 Wpa3Psk = 6,
556 Wpa2Wpa3Psk = 7,
557}
558
559/// enums for Control path
560#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
561#[repr(u32)]
562#[cfg_attr(feature = "defmt", derive(defmt::Format))]
563pub enum CtrlStatus {
564 #[default]
565 Connected = 0,
566 NotConnected = 1,
567 NoApFound = 2,
568 ConnectionFail = 3,
569 InvalidArgument = 4,
570 OutOfRange = 5,
571}
572
573#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
574#[repr(u32)]
575#[cfg_attr(feature = "defmt", derive(defmt::Format))]
576pub enum CtrlMsgType {
577 #[default]
578 MsgTypeInvalid = 0,
579 Req = 1,
580 Resp = 2,
581 Event = 3,
582 MsgTypeMax = 4,
583}
584
585#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
586#[repr(u32)]
587#[cfg_attr(feature = "defmt", derive(defmt::Format))]
588pub enum CtrlMsgId {
589 #[default]
590 MsgIdInvalid = 0,
591 /// * Request Msgs *
592 ReqBase = 100,
593 ReqGetMacAddress = 101,
594 ReqSetMacAddress = 102,
595 ReqGetWifiMode = 103,
596 ReqSetWifiMode = 104,
597 ReqGetApScanList = 105,
598 ReqGetApConfig = 106,
599 ReqConnectAp = 107,
600 ReqDisconnectAp = 108,
601 ReqGetSoftApConfig = 109,
602 ReqSetSoftApVendorSpecificIe = 110,
603 ReqStartSoftAp = 111,
604 ReqGetSoftApConnectedStaList = 112,
605 ReqStopSoftAp = 113,
606 ReqSetPowerSaveMode = 114,
607 ReqGetPowerSaveMode = 115,
608 ReqOtaBegin = 116,
609 ReqOtaWrite = 117,
610 ReqOtaEnd = 118,
611 ReqSetWifiMaxTxPower = 119,
612 ReqGetWifiCurrTxPower = 120,
613 ReqConfigHeartbeat = 121,
614 /// Add new control path command response before Req_Max
615 /// and update Req_Max
616 ReqMax = 122,
617 /// * Response Msgs *
618 RespBase = 200,
619 RespGetMacAddress = 201,
620 RespSetMacAddress = 202,
621 RespGetWifiMode = 203,
622 RespSetWifiMode = 204,
623 RespGetApScanList = 205,
624 RespGetApConfig = 206,
625 RespConnectAp = 207,
626 RespDisconnectAp = 208,
627 RespGetSoftApConfig = 209,
628 RespSetSoftApVendorSpecificIe = 210,
629 RespStartSoftAp = 211,
630 RespGetSoftApConnectedStaList = 212,
631 RespStopSoftAp = 213,
632 RespSetPowerSaveMode = 214,
633 RespGetPowerSaveMode = 215,
634 RespOtaBegin = 216,
635 RespOtaWrite = 217,
636 RespOtaEnd = 218,
637 RespSetWifiMaxTxPower = 219,
638 RespGetWifiCurrTxPower = 220,
639 RespConfigHeartbeat = 221,
640 /// Add new control path command response before Resp_Max
641 /// and update Resp_Max
642 RespMax = 222,
643 /// * Event Msgs *
644 EventBase = 300,
645 EventEspInit = 301,
646 EventHeartbeat = 302,
647 EventStationDisconnectFromAp = 303,
648 EventStationDisconnectFromEspSoftAp = 304,
649 /// Add new control path command notification before Event_Max
650 /// and update Event_Max
651 EventMax = 305,
652}
diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs
index 6821373e3..efd9bed66 100644
--- a/embassy-net-w5500/src/lib.rs
+++ b/embassy-net-w5500/src/lib.rs
@@ -1,5 +1,6 @@
1//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
1#![no_std] 2#![no_std]
2/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip. 3
3mod device; 4mod device;
4mod socket; 5mod socket;
5mod spi; 6mod spi;
@@ -77,7 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
77 } 78 }
78} 79}
79 80
80/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). 81/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net).
81pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( 82pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
82 mac_addr: [u8; 6], 83 mac_addr: [u8; 6],
83 state: &'a mut State<N_RX, N_TX>, 84 state: &'a mut State<N_RX, N_TX>,
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 4ac572577..e89039daa 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -3,7 +3,13 @@ name = "embassy-net"
3version = "0.1.0" 3version = "0.1.0"
4edition = "2021" 4edition = "2021"
5license = "MIT OR Apache-2.0" 5license = "MIT OR Apache-2.0"
6 6description = "Async TCP/IP network stack for embedded systems"
7repository = "https://github.com/embassy-rs/embassy"
8categories = [
9 "embedded",
10 "no-std",
11 "asynchronous",
12]
7 13
8[package.metadata.embassy_docs] 14[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" 15src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
@@ -38,13 +44,12 @@ igmp = ["smoltcp/proto-igmp"]
38defmt = { version = "0.3", optional = true } 44defmt = { version = "0.3", optional = true }
39log = { version = "0.4.14", optional = true } 45log = { version = "0.4.14", optional = true }
40 46
41smoltcp = { version = "0.9.0", default-features = false, features = [ 47smoltcp = { version = "0.10.0", default-features = false, features = [
42 "socket", 48 "socket",
43 "async", 49 "async",
44]} 50] }
45 51
46embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } 52embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
47embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
48embassy-time = { version = "0.1.0", path = "../embassy-time" } 53embassy-time = { version = "0.1.0", path = "../embassy-time" }
49embassy-sync = { version = "0.2.0", path = "../embassy-sync" } 54embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
50embedded-io = { version = "0.4.0", optional = true } 55embedded-io = { version = "0.4.0", optional = true }
diff --git a/embassy-net/README.md b/embassy-net/README.md
index 470926c58..48f9fd832 100644
--- a/embassy-net/README.md
+++ b/embassy-net/README.md
@@ -1,30 +1,56 @@
1# embassy-net 1# embassy-net
2 2
3embassy-net contains an async network API based on smoltcp and embassy, designed 3`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.
4for embedded systems.
5 4
6## Running the example 5It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
6API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
7memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
7 8
8First, create the tap0 interface. You only need to do this once. 9## Features
9 10
10```sh 11- IPv4, IPv6
11sudo ip tuntap add name tap0 mode tap user $USER 12- Ethernet and bare-IP mediums.
12sudo ip link set tap0 up 13- TCP, UDP, DNS, DHCPv4, IGMPv4
13sudo ip addr add 192.168.69.100/24 dev tap0 14- TCP sockets implement the `embedded-io` async traits.
14sudo ip -6 addr add fe80::100/64 dev tap0
15sudo ip -6 addr add fdaa::100/64 dev tap0
16sudo ip -6 route add fe80::/64 dev tap0
17sudo ip -6 route add fdaa::/64 dev tap0
18```
19 15
20Second, have something listening there. For example `nc -l 8000` 16See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and
17unimplemented features of the network protocols.
21 18
22Then run the example located in the `examples` folder: 19## Hardware support
23 20
24```sh 21- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.
25cd $EMBASSY_ROOT/examples/std/ 22- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
26cargo run --bin net -- --static-ip 23- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
27``` 24- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
25- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
26- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
27
28## Examples
29
30- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.
31- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.
32- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)
33
34## Adding support for new hardware
35
36To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or
37an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)
38traits.
39
40Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API
41to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
42packet queues for RX and TX.
43
44Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.
45This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver
46trait has not had breaking changes.
47
48## Interoperability
49
50This crate can run on any executor.
51
52[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must
53link an `embassy-time` driver in your project to use this crate.
28 54
29## License 55## License
30 56
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
index 583cdc87f..4513c86d3 100644
--- a/embassy-net/src/device.rs
+++ b/embassy-net/src/device.rs
@@ -51,8 +51,9 @@ where
51 Medium::Ethernet => phy::Medium::Ethernet, 51 Medium::Ethernet => phy::Medium::Ethernet,
52 #[cfg(feature = "medium-ip")] 52 #[cfg(feature = "medium-ip")]
53 Medium::Ip => phy::Medium::Ip, 53 Medium::Ip => phy::Medium::Ip,
54 #[allow(unreachable_patterns)]
54 _ => panic!( 55 _ => panic!(
55 "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.", 56 "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
56 caps.medium 57 caps.medium
57 ), 58 ),
58 }; 59 };
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index 7e8f765f9..840d7a09a 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -57,7 +57,7 @@ pub struct StackResources<const SOCK: usize> {
57 57
58impl<const SOCK: usize> StackResources<SOCK> { 58impl<const SOCK: usize> StackResources<SOCK> {
59 /// Create a new set of stack resources. 59 /// Create a new set of stack resources.
60 pub fn new() -> Self { 60 pub const fn new() -> Self {
61 #[cfg(feature = "dns")] 61 #[cfg(feature = "dns")]
62 const INIT: Option<dns::DnsQuery> = None; 62 const INIT: Option<dns::DnsQuery> = None;
63 Self { 63 Self {
@@ -235,12 +235,19 @@ impl<D: Driver + 'static> Stack<D> {
235 #[cfg(feature = "medium-ethernet")] 235 #[cfg(feature = "medium-ethernet")]
236 let medium = device.capabilities().medium; 236 let medium = device.capabilities().medium;
237 237
238 let mut iface_cfg = smoltcp::iface::Config::new(); 238 let hardware_addr = match medium {
239 #[cfg(feature = "medium-ethernet")]
240 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
241 #[cfg(feature = "medium-ip")]
242 Medium::Ip => HardwareAddress::Ip,
243 #[allow(unreachable_patterns)]
244 _ => panic!(
245 "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
246 medium
247 ),
248 };
249 let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr);
239 iface_cfg.random_seed = random_seed; 250 iface_cfg.random_seed = random_seed;
240 #[cfg(feature = "medium-ethernet")]
241 if medium == Medium::Ethernet {
242 iface_cfg.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())));
243 }
244 251
245 let iface = Interface::new( 252 let iface = Interface::new(
246 iface_cfg, 253 iface_cfg,
@@ -248,6 +255,7 @@ impl<D: Driver + 'static> Stack<D> {
248 inner: &mut device, 255 inner: &mut device,
249 cx: None, 256 cx: None,
250 }, 257 },
258 instant_to_smoltcp(Instant::now()),
251 ); 259 );
252 260
253 let sockets = SocketSet::new(&mut resources.sockets[..]); 261 let sockets = SocketSet::new(&mut resources.sockets[..]);
@@ -411,7 +419,29 @@ impl<D: Driver + 'static> Stack<D> {
411 }) 419 })
412 .await?; 420 .await?;
413 421
414 use embassy_hal_common::drop::OnDrop; 422 #[must_use = "to delay the drop handler invocation to the end of the scope"]
423 struct OnDrop<F: FnOnce()> {
424 f: core::mem::MaybeUninit<F>,
425 }
426
427 impl<F: FnOnce()> OnDrop<F> {
428 fn new(f: F) -> Self {
429 Self {
430 f: core::mem::MaybeUninit::new(f),
431 }
432 }
433
434 fn defuse(self) {
435 core::mem::forget(self)
436 }
437 }
438
439 impl<F: FnOnce()> Drop for OnDrop<F> {
440 fn drop(&mut self) {
441 unsafe { self.f.as_ptr().read()() }
442 }
443 }
444
415 let drop = OnDrop::new(|| { 445 let drop = OnDrop::new(|| {
416 self.with_mut(|s, i| { 446 self.with_mut(|s, i| {
417 let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket); 447 let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index c9843cfe8..36f8d06f2 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -104,7 +104,7 @@ impl<'a> UdpSocket<'a> {
104 pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { 104 pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
105 poll_fn(move |cx| { 105 poll_fn(move |cx| {
106 self.with_mut(|s, _| match s.recv_slice(buf) { 106 self.with_mut(|s, _| match s.recv_slice(buf) {
107 Ok(x) => Poll::Ready(Ok(x)), 107 Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
108 // No data ready 108 // No data ready
109 Err(udp::RecvError::Exhausted) => { 109 Err(udp::RecvError::Exhausted) => {
110 s.register_recv_waker(cx.waker()); 110 s.register_recv_waker(cx.waker());
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index 691545662..d23759f9d 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -410,13 +410,13 @@ pub fn init(config: config::Config) -> Peripherals {
410 warn!( 410 warn!(
411 "You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\ 411 "You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
412 However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ 412 However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
413 To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." 413 To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
414 ); 414 );
415 #[cfg(feature = "reset-pin-as-gpio")] 415 #[cfg(feature = "reset-pin-as-gpio")]
416 warn!( 416 warn!(
417 "You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\ 417 "You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
418 However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ 418 However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
419 To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." 419 To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
420 ); 420 );
421 } 421 }
422 } 422 }
@@ -432,7 +432,7 @@ pub fn init(config: config::Config) -> Peripherals {
432 warn!( 432 warn!(
433 "You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\ 433 "You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
434 However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ 434 However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
435 To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." 435 To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
436 ); 436 );
437 } 437 }
438 } 438 }
diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs
index 7c18da6ee..76757a248 100644
--- a/embassy-nrf/src/ppi/mod.rs
+++ b/embassy-nrf/src/ppi/mod.rs
@@ -137,6 +137,11 @@ impl Task {
137 Self(ptr) 137 Self(ptr)
138 } 138 }
139 139
140 /// Triggers this task.
141 pub fn trigger(&mut self) {
142 unsafe { self.0.as_ptr().write_volatile(1) };
143 }
144
140 pub(crate) fn from_reg<T>(reg: &T) -> Self { 145 pub(crate) fn from_reg<T>(reg: &T) -> Self {
141 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) 146 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
142 } 147 }
@@ -173,6 +178,16 @@ impl Event {
173 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) 178 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
174 } 179 }
175 180
181 /// Describes whether this Event is currently in a triggered state.
182 pub fn is_triggered(&self) -> bool {
183 unsafe { self.0.as_ptr().read_volatile() == 1 }
184 }
185
186 /// Clear the current register's triggered state, reverting it to 0.
187 pub fn clear(&mut self) {
188 unsafe { self.0.as_ptr().write_volatile(0) };
189 }
190
176 /// Address of publish register for this event. 191 /// Address of publish register for this event.
177 #[cfg(feature = "_dppi")] 192 #[cfg(feature = "_dppi")]
178 pub fn publish_reg(&self) -> *mut u32 { 193 pub fn publish_reg(&self) -> *mut u32 {
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 49aa6a4d5..66823771a 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -76,7 +76,7 @@ embedded-storage = { version = "0.3" }
76rand_core = "0.6.4" 76rand_core = "0.6.4"
77fixed = "1.23.1" 77fixed = "1.23.1"
78 78
79rp-pac = { version = "5" } 79rp-pac = { version = "6" }
80 80
81embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 81embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
82embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} 82embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs
index b96d5a4a8..699a0d61d 100644
--- a/embassy-rp/src/adc.rs
+++ b/embassy-rp/src/adc.rs
@@ -112,8 +112,14 @@ impl<'d> Adc<'d> {
112 r.result().read().result().into() 112 r.result().read().result().into()
113 } 113 }
114 114
115 pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { 115 pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
116 let r = Self::regs(); 116 let r = Self::regs();
117 pin.pad_ctrl().modify(|w| {
118 w.set_ie(true);
119 let (pu, pd) = (false, false);
120 w.set_pue(pu);
121 w.set_pde(pd);
122 });
117 r.cs().modify(|w| { 123 r.cs().modify(|w| {
118 w.set_ainsel(PIN::channel()); 124 w.set_ainsel(PIN::channel());
119 w.set_start_once(true) 125 w.set_start_once(true)
@@ -166,7 +172,7 @@ impl_pin!(PIN_29, 3);
166impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static> 172impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
167where 173where
168 WORD: From<u16>, 174 WORD: From<u16>,
169 PIN: Channel<Adc<'static>, ID = u8>, 175 PIN: Channel<Adc<'static>, ID = u8> + Pin,
170{ 176{
171 type Error = (); 177 type Error = ();
172 fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> { 178 fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 4c6223107..ddd61d224 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -46,13 +46,13 @@ static CLOCKS: Clocks = Clocks {
46#[non_exhaustive] 46#[non_exhaustive]
47#[derive(Clone, Copy, Debug, PartialEq, Eq)] 47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum PeriClkSrc { 48pub enum PeriClkSrc {
49 Sys = ClkPeriCtrlAuxsrc::CLK_SYS.0, 49 Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _,
50 PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS.0, 50 PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _,
51 PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0, 51 PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _,
52 Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0, 52 Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _,
53 Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0, 53 Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _,
54 // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, 54 // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
55 // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, 55 // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
56} 56}
57 57
58#[non_exhaustive] 58#[non_exhaustive]
@@ -251,12 +251,12 @@ pub struct SysClkConfig {
251#[non_exhaustive] 251#[non_exhaustive]
252#[derive(Clone, Copy, Debug, PartialEq, Eq)] 252#[derive(Clone, Copy, Debug, PartialEq, Eq)]
253pub enum UsbClkSrc { 253pub enum UsbClkSrc {
254 PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB.0, 254 PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
255 PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0, 255 PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _,
256 Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0, 256 Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _,
257 Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0, 257 Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _,
258 // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, 258 // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
259 // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, 259 // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
260} 260}
261 261
262pub struct UsbClkConfig { 262pub struct UsbClkConfig {
@@ -269,12 +269,12 @@ pub struct UsbClkConfig {
269#[non_exhaustive] 269#[non_exhaustive]
270#[derive(Clone, Copy, Debug, PartialEq, Eq)] 270#[derive(Clone, Copy, Debug, PartialEq, Eq)]
271pub enum AdcClkSrc { 271pub enum AdcClkSrc {
272 PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB.0, 272 PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
273 PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0, 273 PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
274 Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0, 274 Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
275 Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0, 275 Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _,
276 // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, 276 // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
277 // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, 277 // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
278} 278}
279 279
280pub struct AdcClkConfig { 280pub struct AdcClkConfig {
@@ -287,12 +287,12 @@ pub struct AdcClkConfig {
287#[non_exhaustive] 287#[non_exhaustive]
288#[derive(Clone, Copy, Debug, PartialEq, Eq)] 288#[derive(Clone, Copy, Debug, PartialEq, Eq)]
289pub enum RtcClkSrc { 289pub enum RtcClkSrc {
290 PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB.0, 290 PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,
291 PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0, 291 PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
292 Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0, 292 Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
293 Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0, 293 Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _,
294 // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, 294 // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
295 // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, 295 // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
296} 296}
297 297
298pub struct RtcClkConfig { 298pub struct RtcClkConfig {
@@ -396,7 +396,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
396 w.set_src(ref_src); 396 w.set_src(ref_src);
397 w.set_auxsrc(ref_aux); 397 w.set_auxsrc(ref_aux);
398 }); 398 });
399 while c.clk_ref_selected().read() != 1 << ref_src.0 {} 399 while c.clk_ref_selected().read() != 1 << ref_src as u32 {}
400 c.clk_ref_div().write(|w| { 400 c.clk_ref_div().write(|w| {
401 w.set_int(config.ref_clk.div); 401 w.set_int(config.ref_clk.div);
402 }); 402 });
@@ -425,13 +425,13 @@ pub(crate) unsafe fn init(config: ClockConfig) {
425 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); 425 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
426 if sys_src != ClkSysCtrlSrc::CLK_REF { 426 if sys_src != ClkSysCtrlSrc::CLK_REF {
427 c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); 427 c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
428 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} 428 while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF as u32 {}
429 } 429 }
430 c.clk_sys_ctrl().write(|w| { 430 c.clk_sys_ctrl().write(|w| {
431 w.set_auxsrc(sys_aux); 431 w.set_auxsrc(sys_aux);
432 w.set_src(sys_src); 432 w.set_src(sys_src);
433 }); 433 });
434 while c.clk_sys_selected().read() != 1 << sys_src.0 {} 434 while c.clk_sys_selected().read() != 1 << sys_src as u32 {}
435 c.clk_sys_div().write(|w| { 435 c.clk_sys_div().write(|w| {
436 w.set_int(config.sys_clk.div_int); 436 w.set_int(config.sys_clk.div_int);
437 w.set_frac(config.sys_clk.div_frac); 437 w.set_frac(config.sys_clk.div_frac);
@@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
442 if let Some(src) = config.peri_clk_src { 442 if let Some(src) = config.peri_clk_src {
443 c.clk_peri_ctrl().write(|w| { 443 c.clk_peri_ctrl().write(|w| {
444 w.set_enable(true); 444 w.set_enable(true);
445 w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); 445 w.set_auxsrc(ClkPeriCtrlAuxsrc::from_bits(src as _));
446 }); 446 });
447 let peri_freq = match src { 447 let peri_freq = match src {
448 PeriClkSrc::Sys => clk_sys_freq, 448 PeriClkSrc::Sys => clk_sys_freq,
@@ -468,7 +468,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
468 c.clk_usb_ctrl().write(|w| { 468 c.clk_usb_ctrl().write(|w| {
469 w.set_phase(conf.phase); 469 w.set_phase(conf.phase);
470 w.set_enable(true); 470 w.set_enable(true);
471 w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); 471 w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
472 }); 472 });
473 let usb_freq = match conf.src { 473 let usb_freq = match conf.src {
474 UsbClkSrc::PllUsb => pll_usb_freq, 474 UsbClkSrc::PllUsb => pll_usb_freq,
@@ -491,7 +491,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
491 c.clk_adc_ctrl().write(|w| { 491 c.clk_adc_ctrl().write(|w| {
492 w.set_phase(conf.phase); 492 w.set_phase(conf.phase);
493 w.set_enable(true); 493 w.set_enable(true);
494 w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); 494 w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
495 }); 495 });
496 let adc_in_freq = match conf.src { 496 let adc_in_freq = match conf.src {
497 AdcClkSrc::PllUsb => pll_usb_freq, 497 AdcClkSrc::PllUsb => pll_usb_freq,
@@ -517,7 +517,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
517 c.clk_rtc_ctrl().write(|w| { 517 c.clk_rtc_ctrl().write(|w| {
518 w.set_phase(conf.phase); 518 w.set_phase(conf.phase);
519 w.set_enable(true); 519 w.set_enable(true);
520 w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); 520 w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
521 }); 521 });
522 let rtc_in_freq = match conf.src { 522 let rtc_in_freq = match conf.src {
523 RtcClkSrc::PllUsb => pll_usb_freq, 523 RtcClkSrc::PllUsb => pll_usb_freq,
@@ -718,7 +718,7 @@ impl<'d, T: Pin> Drop for Gpin<'d, T> {
718 self.gpin 718 self.gpin
719 .io() 719 .io()
720 .ctrl() 720 .ctrl()
721 .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); 721 .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
722 } 722 }
723} 723}
724 724
@@ -743,17 +743,17 @@ impl_gpoutpin!(PIN_25, 3);
743 743
744#[repr(u8)] 744#[repr(u8)]
745pub enum GpoutSrc { 745pub enum GpoutSrc {
746 PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0, 746 PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _,
747 // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, 747 // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
748 // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, 748 // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
749 PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0, 749 PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _,
750 Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0, 750 Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _,
751 Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0, 751 Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _,
752 Sys = ClkGpoutCtrlAuxsrc::CLK_SYS.0, 752 Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _,
753 Usb = ClkGpoutCtrlAuxsrc::CLK_USB.0, 753 Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,
754 Adc = ClkGpoutCtrlAuxsrc::CLK_ADC.0, 754 Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
755 Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC.0, 755 Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
756 Ref = ClkGpoutCtrlAuxsrc::CLK_REF.0, 756 Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
757} 757}
758 758
759pub struct Gpout<'d, T: GpoutPin> { 759pub struct Gpout<'d, T: GpoutPin> {
@@ -780,7 +780,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
780 pub fn set_src(&self, src: GpoutSrc) { 780 pub fn set_src(&self, src: GpoutSrc) {
781 let c = pac::CLOCKS; 781 let c = pac::CLOCKS;
782 c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { 782 c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
783 w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); 783 w.set_auxsrc(ClkGpoutCtrlAuxsrc::from_bits(src as _));
784 }); 784 });
785 } 785 }
786 786
@@ -831,7 +831,7 @@ impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
831 self.gpout 831 self.gpout
832 .io() 832 .io()
833 .ctrl() 833 .ctrl()
834 .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0)); 834 .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
835 } 835 }
836} 836}
837 837
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs
index ce0d02557..f8048a4dd 100644
--- a/embassy-rp/src/gpio.rs
+++ b/embassy-rp/src/gpio.rs
@@ -452,7 +452,7 @@ impl<'d, T: Pin> Flex<'d, T> {
452 }); 452 });
453 453
454 pin.io().ctrl().write(|w| { 454 pin.io().ctrl().write(|w| {
455 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0); 455 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
456 }); 456 });
457 457
458 Self { pin } 458 Self { pin }
@@ -618,7 +618,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> {
618 fn drop(&mut self) { 618 fn drop(&mut self) {
619 self.pin.pad_ctrl().write(|_| {}); 619 self.pin.pad_ctrl().write(|_| {});
620 self.pin.io().ctrl().write(|w| { 620 self.pin.io().ctrl().write(|w| {
621 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0); 621 w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _);
622 }); 622 });
623 } 623 }
624} 624}
diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs
index 1b36e0a54..30648e8ea 100644
--- a/embassy-rp/src/pio.rs
+++ b/embassy-rp/src/pio.rs
@@ -834,7 +834,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
834 /// of [`Pio`] do not keep pin registrations alive.** 834 /// of [`Pio`] do not keep pin registrations alive.**
835 pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> { 835 pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> {
836 into_ref!(pin); 836 into_ref!(pin);
837 pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); 837 pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
838 // we can be relaxed about this because we're &mut here and nothing is cached 838 // we can be relaxed about this because we're &mut here and nothing is cached
839 PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); 839 PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed);
840 Pin { 840 Pin {
@@ -998,7 +998,7 @@ fn on_pio_drop<PIO: Instance>() {
998 let state = PIO::state(); 998 let state = PIO::state();
999 if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { 999 if state.users.fetch_sub(1, Ordering::AcqRel) == 1 {
1000 let used_pins = state.used_pins.load(Ordering::Relaxed); 1000 let used_pins = state.used_pins.load(Ordering::Relaxed);
1001 let null = Gpio0ctrlFuncsel::NULL.0; 1001 let null = Gpio0ctrlFuncsel::NULL as _;
1002 // we only have 30 pins. don't test the other two since gpio() asserts. 1002 // we only have 30 pins. don't test the other two since gpio() asserts.
1003 for i in 0..30 { 1003 for i in 0..30 {
1004 if used_pins & (1 << i) != 0 { 1004 if used_pins & (1 << i) != 0 {
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 1900ab416..b3f3bd927 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -353,6 +353,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
353 poll_fn(move |cx| { 353 poll_fn(move |cx| {
354 BUS_WAKER.register(cx.waker()); 354 BUS_WAKER.register(cx.waker());
355 355
356 // TODO: implement VBUS detection.
356 if !self.inited { 357 if !self.inited {
357 self.inited = true; 358 self.inited = true;
358 return Poll::Ready(Event::PowerDetected); 359 return Poll::Ready(Event::PowerDetected);
diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml
index f9023ed79..4b830cab3 100644
--- a/embassy-stm32-wpan/Cargo.toml
+++ b/embassy-stm32-wpan/Cargo.toml
@@ -21,13 +21,16 @@ embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
21defmt = { version = "0.3", optional = true } 21defmt = { version = "0.3", optional = true }
22cortex-m = "0.7.6" 22cortex-m = "0.7.6"
23heapless = "0.7.16" 23heapless = "0.7.16"
24aligned = "0.4.1"
24 25
25bit_field = "0.10.2" 26bit_field = "0.10.2"
27stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
28stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true }
26 29
27[features] 30[features]
28defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] 31defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"]
29 32
30ble = [] 33ble = ["dep:stm32wb-hci"]
31mac = [] 34mac = []
32 35
33stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] 36stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ]
diff --git a/embassy-stm32-wpan/build.rs b/embassy-stm32-wpan/build.rs
index 4edf73d59..94aac070d 100644
--- a/embassy-stm32-wpan/build.rs
+++ b/embassy-stm32-wpan/build.rs
@@ -1,4 +1,5 @@
1use std::env; 1use std::path::PathBuf;
2use std::{env, fs};
2 3
3fn main() { 4fn main() {
4 match env::vars() 5 match env::vars()
@@ -10,6 +11,16 @@ fn main() {
10 Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"), 11 Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"),
11 Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), 12 Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
12 } 13 }
14
15 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
16
17 // ========
18 // stm32wb tl_mbox link sections
19
20 let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string();
21 fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap();
22 println!("cargo:rustc-link-search={}", out_dir.display());
23 println!("cargo:rerun-if-changed=tl_mbox.x.in");
13} 24}
14 25
15enum GetOneError { 26enum GetOneError {
diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs
index c8056aaa7..8428b6ffc 100644
--- a/embassy-stm32-wpan/src/cmd.rs
+++ b/embassy-stm32-wpan/src/cmd.rs
@@ -52,7 +52,7 @@ impl CmdPacket {
52 p_cmd_serial, 52 p_cmd_serial,
53 CmdSerialStub { 53 CmdSerialStub {
54 ty: packet_type as u8, 54 ty: packet_type as u8,
55 cmd_code: cmd_code, 55 cmd_code,
56 payload_len: payload.len() as u8, 56 payload_len: payload.len() as u8,
57 }, 57 },
58 ); 58 );
diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs
index 9a107306c..f234151d7 100644
--- a/embassy-stm32-wpan/src/consts.rs
+++ b/embassy-stm32-wpan/src/consts.rs
@@ -87,3 +87,7 @@ pub const fn divc(x: usize, y: usize) -> usize {
87pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; 87pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
88#[allow(dead_code)] 88#[allow(dead_code)]
89pub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE; 89pub const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;
90
91pub const TL_BLEEVT_CC_OPCODE: u8 = 0x0E;
92pub const TL_BLEEVT_CS_OPCODE: u8 = 0x0F;
93pub const TL_BLEEVT_VS_OPCODE: u8 = 0xFF;
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs
index 47bdc49bf..c6528413d 100644
--- a/embassy-stm32-wpan/src/evt.rs
+++ b/embassy-stm32-wpan/src/evt.rs
@@ -1,6 +1,8 @@
1use core::marker::PhantomData;
1use core::{ptr, slice}; 2use core::{ptr, slice};
2 3
3use super::PacketHeader; 4use super::PacketHeader;
5use crate::consts::TL_EVT_HEADER_SIZE;
4 6
5/** 7/**
6 * The payload of `Evt` for a command status event 8 * The payload of `Evt` for a command status event
@@ -92,17 +94,22 @@ impl EvtPacket {
92 } 94 }
93} 95}
94 96
97pub trait MemoryManager {
98 unsafe fn drop_event_packet(evt: *mut EvtPacket);
99}
100
95/// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically 101/// smart pointer to the [`EvtPacket`] that will dispose of [`EvtPacket`] buffer automatically
96/// on [`Drop`] 102/// on [`Drop`]
97#[derive(Debug)] 103#[derive(Debug)]
98pub struct EvtBox { 104pub struct EvtBox<T: MemoryManager> {
99 ptr: *mut EvtPacket, 105 ptr: *mut EvtPacket,
106 mm: PhantomData<T>,
100} 107}
101 108
102unsafe impl Send for EvtBox {} 109unsafe impl<T: MemoryManager> Send for EvtBox<T> {}
103impl EvtBox { 110impl<T: MemoryManager> EvtBox<T> {
104 pub(super) fn new(ptr: *mut EvtPacket) -> Self { 111 pub(super) fn new(ptr: *mut EvtPacket) -> Self {
105 Self { ptr } 112 Self { ptr, mm: PhantomData }
106 } 113 }
107 114
108 /// Returns information about the event 115 /// Returns information about the event
@@ -124,22 +131,21 @@ impl EvtBox {
124 slice::from_raw_parts(p_payload, payload_len as usize) 131 slice::from_raw_parts(p_payload, payload_len as usize)
125 } 132 }
126 } 133 }
127}
128 134
129impl Drop for EvtBox { 135 pub fn serial<'a>(&'a self) -> &'a [u8] {
130 fn drop(&mut self) {
131 #[cfg(feature = "ble")]
132 unsafe { 136 unsafe {
133 use crate::mm; 137 let evt_serial: *const EvtSerial = &(*self.ptr).evt_serial;
134 138 let evt_serial_buf: *const u8 = evt_serial.cast();
135 mm::MemoryManager::drop_event_packet(self.ptr)
136 };
137 139
138 #[cfg(feature = "mac")] 140 let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
139 unsafe {
140 use crate::mac;
141 141
142 mac::Mac::drop_event_packet(self.ptr) 142 slice::from_raw_parts(evt_serial_buf, len)
143 } 143 }
144 } 144 }
145} 145}
146
147impl<T: MemoryManager> Drop for EvtBox<T> {
148 fn drop(&mut self) {
149 unsafe { T::drop_event_packet(self.ptr) };
150 }
151}
diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/lhci.rs
new file mode 100644
index 000000000..89f204f99
--- /dev/null
+++ b/embassy-stm32-wpan/src/lhci.rs
@@ -0,0 +1,112 @@
1use core::ptr;
2
3use crate::cmd::CmdPacket;
4use crate::consts::{TlPacketType, TL_EVT_HEADER_SIZE};
5use crate::evt::{CcEvt, EvtPacket, EvtSerial};
6use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable, TL_DEVICE_INFO_TABLE};
7
8const TL_BLEEVT_CC_OPCODE: u8 = 0x0e;
9const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62;
10
11const PACKAGE_DATA_PTR: *const u8 = 0x1FFF_7500 as _;
12const UID64_PTR: *const u32 = 0x1FFF_7580 as _;
13
14#[derive(Debug, Copy, Clone)]
15#[repr(C, packed)]
16pub struct LhciC1DeviceInformationCcrp {
17 pub status: u8,
18 pub rev_id: u16,
19 pub dev_code_id: u16,
20 pub package_type: u8,
21 pub device_type_id: u8,
22 pub st_company_id: u32,
23 pub uid64: u32,
24
25 pub uid96_0: u32,
26 pub uid96_1: u32,
27 pub uid96_2: u32,
28
29 pub safe_boot_info_table: SafeBootInfoTable,
30 pub rss_info_table: RssInfoTable,
31 pub wireless_fw_info_table: WirelessFwInfoTable,
32
33 pub app_fw_inf: u32,
34}
35
36impl Default for LhciC1DeviceInformationCcrp {
37 fn default() -> Self {
38 let DeviceInfoTable {
39 safe_boot_info_table,
40 rss_info_table,
41 wireless_fw_info_table,
42 } = unsafe { ptr::read_volatile(TL_DEVICE_INFO_TABLE.as_ptr()) };
43
44 let device_id = stm32_device_signature::device_id();
45 let uid96_0 = (device_id[3] as u32) << 24
46 | (device_id[2] as u32) << 16
47 | (device_id[1] as u32) << 8
48 | device_id[0] as u32;
49 let uid96_1 = (device_id[7] as u32) << 24
50 | (device_id[6] as u32) << 16
51 | (device_id[5] as u32) << 8
52 | device_id[4] as u32;
53 let uid96_2 = (device_id[11] as u32) << 24
54 | (device_id[10] as u32) << 16
55 | (device_id[9] as u32) << 8
56 | device_id[8] as u32;
57
58 let package_type = unsafe { *PACKAGE_DATA_PTR };
59 let uid64 = unsafe { *UID64_PTR };
60 let st_company_id = unsafe { *UID64_PTR.offset(1) } >> 8 & 0x00FF_FFFF;
61 let device_type_id = (unsafe { *UID64_PTR.offset(1) } & 0x000000FF) as u8;
62
63 LhciC1DeviceInformationCcrp {
64 status: 0,
65 rev_id: 0,
66 dev_code_id: 0,
67 package_type,
68 device_type_id,
69 st_company_id,
70 uid64,
71 uid96_0,
72 uid96_1,
73 uid96_2,
74 safe_boot_info_table,
75 rss_info_table,
76 wireless_fw_info_table,
77 app_fw_inf: (1 << 8), // 0.0.1
78 }
79 }
80}
81
82impl LhciC1DeviceInformationCcrp {
83 pub fn new() -> Self {
84 Self::default()
85 }
86
87 pub fn write(&self, cmd_packet: &mut CmdPacket) {
88 let self_size = core::mem::size_of::<LhciC1DeviceInformationCcrp>();
89
90 unsafe {
91 let cmd_packet_ptr: *mut CmdPacket = cmd_packet;
92 let evet_packet_ptr: *mut EvtPacket = cmd_packet_ptr.cast();
93
94 let evt_serial: *mut EvtSerial = &mut (*evet_packet_ptr).evt_serial;
95 let evt_payload = (*evt_serial).evt.payload.as_mut_ptr();
96 let evt_cc: *mut CcEvt = evt_payload.cast();
97 let evt_cc_payload_buf = (*evt_cc).payload.as_mut_ptr();
98
99 (*evt_serial).kind = TlPacketType::LocRsp as u8;
100 (*evt_serial).evt.evt_code = TL_BLEEVT_CC_OPCODE;
101 (*evt_serial).evt.payload_len = TL_EVT_HEADER_SIZE as u8 + self_size as u8;
102
103 (*evt_cc).cmd_code = LHCI_OPCODE_C1_DEVICE_INF;
104 (*evt_cc).num_cmd = 1;
105
106 let self_ptr: *const LhciC1DeviceInformationCcrp = self;
107 let self_buf = self_ptr.cast();
108
109 ptr::copy(self_buf, evt_cc_payload_buf, self_size);
110 }
111 }
112}
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 5aec9933c..99c610583 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -1,4 +1,5 @@
1#![no_std] 1#![no_std]
2#![cfg_attr(feature = "ble", feature(async_fn_in_trait))]
2 3
3// This must go FIRST so that all the other modules see its macros. 4// This must go FIRST so that all the other modules see its macros.
4pub mod fmt; 5pub mod fmt;
@@ -10,25 +11,24 @@ use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
10use embassy_stm32::interrupt; 11use embassy_stm32::interrupt;
11use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; 12use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};
12use embassy_stm32::peripherals::IPCC; 13use embassy_stm32::peripherals::IPCC;
13use mm::MemoryManager; 14use sub::mm::MemoryManager;
14use sys::Sys; 15use sub::sys::Sys;
15use tables::*; 16use tables::*;
16use unsafe_linked_list::LinkedListNode; 17use unsafe_linked_list::LinkedListNode;
17 18
18#[cfg(feature = "ble")]
19pub mod ble;
20pub mod channels; 19pub mod channels;
21pub mod cmd; 20pub mod cmd;
22pub mod consts; 21pub mod consts;
23pub mod evt; 22pub mod evt;
24#[cfg(feature = "mac")] 23pub mod lhci;
25pub mod mac;
26pub mod mm;
27pub mod shci; 24pub mod shci;
28pub mod sys; 25pub mod sub;
29pub mod tables; 26pub mod tables;
30pub mod unsafe_linked_list; 27pub mod unsafe_linked_list;
31 28
29#[cfg(feature = "ble")]
30pub use crate::sub::ble::hci;
31
32type PacketHeader = LinkedListNode; 32type PacketHeader = LinkedListNode;
33 33
34pub struct TlMbox<'d> { 34pub struct TlMbox<'d> {
@@ -37,9 +37,9 @@ pub struct TlMbox<'d> {
37 pub sys_subsystem: Sys, 37 pub sys_subsystem: Sys,
38 pub mm_subsystem: MemoryManager, 38 pub mm_subsystem: MemoryManager,
39 #[cfg(feature = "ble")] 39 #[cfg(feature = "ble")]
40 pub ble_subsystem: ble::Ble, 40 pub ble_subsystem: sub::ble::Ble,
41 #[cfg(feature = "mac")] 41 #[cfg(feature = "mac")]
42 pub mac_subsystem: mac::Mac, 42 pub mac_subsystem: sub::mac::Mac,
43} 43}
44 44
45impl<'d> TlMbox<'d> { 45impl<'d> TlMbox<'d> {
@@ -126,12 +126,12 @@ impl<'d> TlMbox<'d> {
126 126
127 Self { 127 Self {
128 _ipcc: ipcc, 128 _ipcc: ipcc,
129 sys_subsystem: sys::Sys::new(), 129 sys_subsystem: sub::sys::Sys::new(),
130 #[cfg(feature = "ble")] 130 #[cfg(feature = "ble")]
131 ble_subsystem: ble::Ble::new(), 131 ble_subsystem: sub::ble::Ble::new(),
132 #[cfg(feature = "mac")] 132 #[cfg(feature = "mac")]
133 mac_subsystem: mac::Mac::new(), 133 mac_subsystem: sub::mac::Mac::new(),
134 mm_subsystem: mm::MemoryManager::new(), 134 mm_subsystem: sub::mm::MemoryManager::new(),
135 } 135 }
136 } 136 }
137} 137}
diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs
index 60e0cbdf8..cd32692e1 100644
--- a/embassy-stm32-wpan/src/ble.rs
+++ b/embassy-stm32-wpan/src/sub/ble.rs
@@ -1,13 +1,16 @@
1use core::marker::PhantomData; 1use core::marker::PhantomData;
2use core::ptr;
2 3
3use embassy_stm32::ipcc::Ipcc; 4use embassy_stm32::ipcc::Ipcc;
5use hci::Opcode;
4 6
5use crate::channels;
6use crate::cmd::CmdPacket; 7use crate::cmd::CmdPacket;
7use crate::consts::TlPacketType; 8use crate::consts::{TlPacketType, TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE};
8use crate::evt::EvtBox; 9use crate::evt::{EvtBox, EvtPacket, EvtStub};
10use crate::sub::mm;
9use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; 11use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
10use crate::unsafe_linked_list::LinkedListNode; 12use crate::unsafe_linked_list::LinkedListNode;
13use crate::{channels, evt};
11 14
12pub struct Ble { 15pub struct Ble {
13 phantom: PhantomData<Ble>, 16 phantom: PhantomData<Ble>,
@@ -29,7 +32,7 @@ impl Ble {
29 Self { phantom: PhantomData } 32 Self { phantom: PhantomData }
30 } 33 }
31 /// `HW_IPCC_BLE_EvtNot` 34 /// `HW_IPCC_BLE_EvtNot`
32 pub async fn read(&self) -> EvtBox { 35 pub async fn tl_read(&self) -> EvtBox<Self> {
33 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { 36 Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe {
34 if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { 37 if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) {
35 Some(EvtBox::new(node_ptr.cast())) 38 Some(EvtBox::new(node_ptr.cast()))
@@ -41,7 +44,7 @@ impl Ble {
41 } 44 }
42 45
43 /// `TL_BLE_SendCmd` 46 /// `TL_BLE_SendCmd`
44 pub async fn write(&self, opcode: u16, payload: &[u8]) { 47 pub async fn tl_write(&self, opcode: u16, payload: &[u8]) {
45 Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { 48 Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe {
46 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); 49 CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);
47 }) 50 })
@@ -61,3 +64,33 @@ impl Ble {
61 .await; 64 .await;
62 } 65 }
63} 66}
67
68impl evt::MemoryManager for Ble {
69 /// SAFETY: passing a pointer to something other than a managed event packet is UB
70 unsafe fn drop_event_packet(evt: *mut EvtPacket) {
71 let stub = unsafe {
72 let p_evt_stub = &(*evt).evt_serial as *const _ as *const EvtStub;
73
74 ptr::read_volatile(p_evt_stub)
75 };
76
77 if !(stub.evt_code == TL_BLEEVT_CS_OPCODE || stub.evt_code == TL_BLEEVT_CC_OPCODE) {
78 mm::MemoryManager::drop_event_packet(evt);
79 }
80 }
81}
82
83pub extern crate stm32wb_hci as hci;
84
85impl hci::Controller for Ble {
86 async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) {
87 self.tl_write(opcode.0, payload).await;
88 }
89
90 async fn controller_read_into(&self, buf: &mut [u8]) {
91 let evt_box = self.tl_read().await;
92 let evt_serial = evt_box.serial();
93
94 buf[..evt_serial.len()].copy_from_slice(evt_serial);
95 }
96}
diff --git a/embassy-stm32-wpan/src/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs
index d2be1b85c..fd8af8609 100644
--- a/embassy-stm32-wpan/src/mac.rs
+++ b/embassy-stm32-wpan/src/sub/mac.rs
@@ -8,13 +8,13 @@ use embassy_futures::poll_once;
8use embassy_stm32::ipcc::Ipcc; 8use embassy_stm32::ipcc::Ipcc;
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10 10
11use crate::channels;
12use crate::cmd::CmdPacket; 11use crate::cmd::CmdPacket;
13use crate::consts::TlPacketType; 12use crate::consts::TlPacketType;
14use crate::evt::{EvtBox, EvtPacket}; 13use crate::evt::{EvtBox, EvtPacket};
15use crate::tables::{ 14use crate::tables::{
16 Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, 15 Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE,
17}; 16};
17use crate::{channels, evt};
18 18
19static MAC_WAKER: AtomicWaker = AtomicWaker::new(); 19static MAC_WAKER: AtomicWaker = AtomicWaker::new();
20static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); 20static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false);
@@ -36,31 +36,10 @@ impl Mac {
36 Self { phantom: PhantomData } 36 Self { phantom: PhantomData }
37 } 37 }
38 38
39 /// SAFETY: passing a pointer to something other than a managed event packet is UB
40 pub(crate) unsafe fn drop_event_packet(_: *mut EvtPacket) {
41 // Write the ack
42 CmdPacket::write_into(
43 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
44 TlPacketType::OtAck,
45 0,
46 &[],
47 );
48
49 // Clear the rx flag
50 let _ = poll_once(Ipcc::receive::<bool>(
51 channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL,
52 || None,
53 ));
54
55 // Allow a new read call
56 MAC_EVT_OUT.store(false, Ordering::SeqCst);
57 MAC_WAKER.wake();
58 }
59
60 /// `HW_IPCC_MAC_802_15_4_EvtNot` 39 /// `HW_IPCC_MAC_802_15_4_EvtNot`
61 /// 40 ///
62 /// This function will stall if the previous `EvtBox` has not been dropped 41 /// This function will stall if the previous `EvtBox` has not been dropped
63 pub async fn read(&self) -> EvtBox { 42 pub async fn read(&self) -> EvtBox<Self> {
64 // Wait for the last event box to be dropped 43 // Wait for the last event box to be dropped
65 poll_fn(|cx| { 44 poll_fn(|cx| {
66 MAC_WAKER.register(cx.waker()); 45 MAC_WAKER.register(cx.waker());
@@ -109,3 +88,26 @@ impl Mac {
109 .await; 88 .await;
110 } 89 }
111} 90}
91
92impl evt::MemoryManager for Mac {
93 /// SAFETY: passing a pointer to something other than a managed event packet is UB
94 unsafe fn drop_event_packet(_: *mut EvtPacket) {
95 // Write the ack
96 CmdPacket::write_into(
97 MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
98 TlPacketType::OtAck,
99 0,
100 &[],
101 );
102
103 // Clear the rx flag
104 let _ = poll_once(Ipcc::receive::<bool>(
105 channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL,
106 || None,
107 ));
108
109 // Allow a new read call
110 MAC_EVT_OUT.store(false, Ordering::SeqCst);
111 MAC_WAKER.wake();
112 }
113}
diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs
index 047fddcd4..1f2ecac2e 100644
--- a/embassy-stm32-wpan/src/mm.rs
+++ b/embassy-stm32-wpan/src/sub/mm.rs
@@ -8,13 +8,13 @@ use cortex_m::interrupt;
8use embassy_stm32::ipcc::Ipcc; 8use embassy_stm32::ipcc::Ipcc;
9use embassy_sync::waitqueue::AtomicWaker; 9use embassy_sync::waitqueue::AtomicWaker;
10 10
11use crate::channels;
12use crate::consts::POOL_SIZE; 11use crate::consts::POOL_SIZE;
13use crate::evt::EvtPacket; 12use crate::evt::EvtPacket;
14use crate::tables::{ 13use crate::tables::{
15 MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, 14 MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE,
16}; 15};
17use crate::unsafe_linked_list::LinkedListNode; 16use crate::unsafe_linked_list::LinkedListNode;
17use crate::{channels, evt};
18 18
19static MM_WAKER: AtomicWaker = AtomicWaker::new(); 19static MM_WAKER: AtomicWaker = AtomicWaker::new();
20static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit(); 20static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
@@ -43,16 +43,6 @@ impl MemoryManager {
43 Self { phantom: PhantomData } 43 Self { phantom: PhantomData }
44 } 44 }
45 45
46 #[allow(dead_code)]
47 /// SAFETY: passing a pointer to something other than a managed event packet is UB
48 pub(crate) unsafe fn drop_event_packet(evt: *mut EvtPacket) {
49 interrupt::free(|_| unsafe {
50 LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
51 });
52
53 MM_WAKER.wake();
54 }
55
56 pub async fn run_queue(&self) { 46 pub async fn run_queue(&self) {
57 loop { 47 loop {
58 poll_fn(|cx| unsafe { 48 poll_fn(|cx| unsafe {
@@ -77,3 +67,14 @@ impl MemoryManager {
77 } 67 }
78 } 68 }
79} 69}
70
71impl evt::MemoryManager for MemoryManager {
72 /// SAFETY: passing a pointer to something other than a managed event packet is UB
73 unsafe fn drop_event_packet(evt: *mut EvtPacket) {
74 interrupt::free(|_| unsafe {
75 LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
76 });
77
78 MM_WAKER.wake();
79 }
80}
diff --git a/embassy-stm32-wpan/src/sub/mod.rs b/embassy-stm32-wpan/src/sub/mod.rs
new file mode 100644
index 000000000..bee3dbdf1
--- /dev/null
+++ b/embassy-stm32-wpan/src/sub/mod.rs
@@ -0,0 +1,6 @@
1#[cfg(feature = "ble")]
2pub mod ble;
3#[cfg(feature = "mac")]
4pub mod mac;
5pub mod mm;
6pub mod sys;
diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs
index 2b699b725..af652860d 100644
--- a/embassy-stm32-wpan/src/sys.rs
+++ b/embassy-stm32-wpan/src/sub/sys.rs
@@ -6,6 +6,7 @@ use crate::consts::TlPacketType;
6use crate::evt::{CcEvt, EvtBox, EvtPacket}; 6use crate::evt::{CcEvt, EvtBox, EvtPacket};
7#[allow(unused_imports)] 7#[allow(unused_imports)]
8use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; 8use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode};
9use crate::sub::mm;
9use crate::tables::{SysTable, WirelessFwInfoTable}; 10use crate::tables::{SysTable, WirelessFwInfoTable};
10use crate::unsafe_linked_list::LinkedListNode; 11use crate::unsafe_linked_list::LinkedListNode;
11use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; 12use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
@@ -73,7 +74,7 @@ impl Sys {
73 } 74 }
74 75
75 /// `HW_IPCC_SYS_EvtNot` 76 /// `HW_IPCC_SYS_EvtNot`
76 pub async fn read(&self) -> EvtBox { 77 pub async fn read(&self) -> EvtBox<mm::MemoryManager> {
77 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { 78 Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
78 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { 79 if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
79 Some(EvtBox::new(node_ptr.cast())) 80 Some(EvtBox::new(node_ptr.cast()))
diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs
index 3f26282c6..1b5dcdf2e 100644
--- a/embassy-stm32-wpan/src/tables.rs
+++ b/embassy-stm32-wpan/src/tables.rs
@@ -1,5 +1,6 @@
1use core::mem::MaybeUninit; 1use core::mem::MaybeUninit;
2 2
3use aligned::{Aligned, A4};
3use bit_field::BitField; 4use bit_field::BitField;
4 5
5use crate::cmd::{AclDataPacket, CmdPacket}; 6use crate::cmd::{AclDataPacket, CmdPacket};
@@ -164,9 +165,6 @@ pub struct Mac802_15_4Table {
164 pub evt_queue: *const u8, 165 pub evt_queue: *const u8,
165} 166}
166 167
167#[repr(C, align(4))]
168pub struct AlignedData<const L: usize>([u8; L]);
169
170/// Reference table. Contains pointers to all other tables. 168/// Reference table. Contains pointers to all other tables.
171#[derive(Debug, Copy, Clone)] 169#[derive(Debug, Copy, Clone)]
172#[repr(C)] 170#[repr(C)]
@@ -222,10 +220,9 @@ pub static mut FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit
222#[link_section = "MB_MEM1"] 220#[link_section = "MB_MEM1"]
223pub static mut TRACES_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit(); 221pub static mut TRACES_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
224 222
225const CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
226
227#[link_section = "MB_MEM2"] 223#[link_section = "MB_MEM2"]
228pub static mut CS_BUFFER: MaybeUninit<AlignedData<CS_BUFFER_SIZE>> = MaybeUninit::uninit(); 224pub static mut CS_BUFFER: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
225 MaybeUninit::uninit();
229 226
230#[link_section = "MB_MEM2"] 227#[link_section = "MB_MEM2"]
231pub static mut EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit(); 228pub static mut EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
@@ -239,34 +236,29 @@ pub static mut SYSTEM_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::unin
239pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit(); 236pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
240 237
241#[cfg(feature = "mac")] 238#[cfg(feature = "mac")]
242const MAC_802_15_4_NOTIF_RSP_EVT_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255;
243
244#[cfg(feature = "mac")]
245#[link_section = "MB_MEM2"] 239#[link_section = "MB_MEM2"]
246pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<AlignedData<MAC_802_15_4_NOTIF_RSP_EVT_BUFFER_SIZE>> = 240pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<
247 MaybeUninit::uninit(); 241 Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>,
242> = MaybeUninit::uninit();
248 243
249#[link_section = "MB_MEM2"] 244#[link_section = "MB_MEM2"]
250pub static mut EVT_POOL: MaybeUninit<[u8; POOL_SIZE]> = MaybeUninit::uninit(); 245pub static mut EVT_POOL: MaybeUninit<Aligned<A4, [u8; POOL_SIZE]>> = MaybeUninit::uninit();
251 246
252#[link_section = "MB_MEM2"] 247#[link_section = "MB_MEM2"]
253pub static mut SYS_CMD_BUF: MaybeUninit<CmdPacket> = MaybeUninit::uninit(); 248pub static mut SYS_CMD_BUF: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
254 249
255const SYS_SPARE_EVT_BUF_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255;
256
257#[link_section = "MB_MEM2"] 250#[link_section = "MB_MEM2"]
258pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<AlignedData<SYS_SPARE_EVT_BUF_SIZE>> = MaybeUninit::uninit(); 251pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
252 MaybeUninit::uninit();
259 253
260#[link_section = "MB_MEM1"] 254#[link_section = "MB_MEM1"]
261pub static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit(); 255pub static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
262 256
263const BLE_SPARE_EVT_BUF_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255;
264
265#[link_section = "MB_MEM2"] 257#[link_section = "MB_MEM2"]
266pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<AlignedData<BLE_SPARE_EVT_BUF_SIZE>> = MaybeUninit::uninit(); 258pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
267 259 MaybeUninit::uninit();
268const HCI_ACL_DATA_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + 5 + 251;
269 260
270#[link_section = "MB_MEM2"] 261#[link_section = "MB_MEM2"]
271// fuck these "magic" numbers from ST ---v---v 262// fuck these "magic" numbers from ST ---v---v
272pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; HCI_ACL_DATA_BUFFER_SIZE]> = MaybeUninit::uninit(); 263pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
264 MaybeUninit::uninit();
diff --git a/embassy-stm32/tl_mbox.x.in b/embassy-stm32-wpan/tl_mbox.x.in
index b6eecb429..b6eecb429 100644
--- a/embassy-stm32/tl_mbox.x.in
+++ b/embassy-stm32-wpan/tl_mbox.x.in
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 3d9ee8261..b3fe9c1f5 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -57,7 +57,7 @@ sdio-host = "0.5.0"
57embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } 57embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
58critical-section = "1.1" 58critical-section = "1.1"
59atomic-polyfill = "1.0.1" 59atomic-polyfill = "1.0.1"
60stm32-metapac = "10" 60stm32-metapac = "12"
61vcell = "0.1.3" 61vcell = "0.1.3"
62bxcan = "0.7.0" 62bxcan = "0.7.0"
63nb = "1.0.0" 63nb = "1.0.0"
@@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] }
74[build-dependencies] 74[build-dependencies]
75proc-macro2 = "1.0.36" 75proc-macro2 = "1.0.36"
76quote = "1.0.15" 76quote = "1.0.15"
77stm32-metapac = { version = "10", default-features = false, features = ["metadata"]} 77stm32-metapac = { version = "12", default-features = false, features = ["metadata"]}
78 78
79[features] 79[features]
80default = ["rt"] 80default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index f71074bcf..fa66da1f6 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -699,6 +699,8 @@ fn main() {
699 // SDMMCv1 uses the same channel for both directions, so just implement for RX 699 // SDMMCv1 uses the same channel for both directions, so just implement for RX
700 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), 700 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
701 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), 701 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
702 (("dac", "CH1"), quote!(crate::dac::DmaCh1)),
703 (("dac", "CH2"), quote!(crate::dac::DmaCh2)),
702 ] 704 ]
703 .into(); 705 .into();
704 706
@@ -911,16 +913,6 @@ fn main() {
911 println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); 913 println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
912 } 914 }
913 915
914 // ========
915 // stm32wb tl_mbox link sections
916
917 if chip_name.starts_with("stm32wb") {
918 let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string();
919 fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap();
920 println!("cargo:rustc-link-search={}", out_dir.display());
921 println!("cargo:rerun-if-changed=tl_mbox.x.in");
922 }
923
924 // ======= 916 // =======
925 // Features for targeting groups of chips 917 // Features for targeting groups of chips
926 918
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 94cdc86cd..3a6e58cf6 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -211,10 +211,8 @@ impl<'d, T: Instance> Adc<'d, T> {
211 #[cfg(not(stm32g0))] 211 #[cfg(not(stm32g0))]
212 fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { 212 fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
213 let sample_time = sample_time.into(); 213 let sample_time = sample_time.into();
214 if ch <= 9 { 214 T::regs()
215 T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time)); 215 .smpr(ch as usize / 10)
216 } else { 216 .modify(|reg| reg.set_smp(ch as usize % 10, sample_time));
217 T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
218 }
219 } 217 }
220} 218}
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs
index 88eef528f..73861776a 100644
--- a/embassy-stm32/src/can/bxcan.rs
+++ b/embassy-stm32/src/can/bxcan.rs
@@ -116,10 +116,10 @@ impl<'d, T: Instance> Can<'d, T> {
116 T::regs().ier().write(|w| { 116 T::regs().ier().write(|w| {
117 // TODO: fix metapac 117 // TODO: fix metapac
118 118
119 w.set_errie(Errie(1)); 119 w.set_errie(Errie::from_bits(1));
120 w.set_fmpie(0, Fmpie(1)); 120 w.set_fmpie(0, Fmpie::from_bits(1));
121 w.set_fmpie(1, Fmpie(1)); 121 w.set_fmpie(1, Fmpie::from_bits(1));
122 w.set_tmeie(Tmeie(1)); 122 w.set_tmeie(Tmeie::from_bits(1));
123 }); 123 });
124 124
125 T::regs().mcr().write(|w| { 125 T::regs().mcr().write(|w| {
diff --git a/embassy-stm32/src/dac.rs b/embassy-stm32/src/dac.rs
deleted file mode 100644
index 631118877..000000000
--- a/embassy-stm32/src/dac.rs
+++ /dev/null
@@ -1,260 +0,0 @@
1#![macro_use]
2
3use embassy_hal_common::{into_ref, PeripheralRef};
4
5use crate::pac::dac;
6use crate::rcc::RccPeripheral;
7use crate::{peripherals, Peripheral};
8
9#[derive(Debug, Copy, Clone, Eq, PartialEq)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11pub enum Error {
12 UnconfiguredChannel,
13 InvalidValue,
14}
15
16#[derive(Debug, Copy, Clone, Eq, PartialEq)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub enum Channel {
19 Ch1,
20 Ch2,
21}
22
23impl Channel {
24 fn index(&self) -> usize {
25 match self {
26 Channel::Ch1 => 0,
27 Channel::Ch2 => 1,
28 }
29 }
30}
31
32#[derive(Debug, Copy, Clone, Eq, PartialEq)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub enum Ch1Trigger {
35 Tim6,
36 Tim3,
37 Tim7,
38 Tim15,
39 Tim2,
40 Exti9,
41 Software,
42}
43
44impl Ch1Trigger {
45 fn tsel(&self) -> dac::vals::Tsel1 {
46 match self {
47 Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
48 Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
49 Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
50 Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
51 Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
52 Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
53 Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
54 }
55 }
56}
57
58#[derive(Debug, Copy, Clone, Eq, PartialEq)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub enum Ch2Trigger {
61 Tim6,
62 Tim8,
63 Tim7,
64 Tim5,
65 Tim2,
66 Tim4,
67 Exti9,
68 Software,
69}
70
71impl Ch2Trigger {
72 fn tsel(&self) -> dac::vals::Tsel2 {
73 match self {
74 Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
75 Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
76 Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
77 Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
78 Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
79 Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
80 Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
81 Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
82 }
83 }
84}
85
86#[derive(Debug, Copy, Clone, Eq, PartialEq)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub enum Alignment {
89 Left,
90 Right,
91}
92
93#[derive(Debug, Copy, Clone, Eq, PartialEq)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95pub enum Value {
96 Bit8(u8),
97 Bit12(u16, Alignment),
98}
99
100pub struct Dac<'d, T: Instance> {
101 channels: u8,
102 _peri: PeripheralRef<'d, T>,
103}
104
105impl<'d, T: Instance> Dac<'d, T> {
106 pub fn new_1ch(peri: impl Peripheral<P = T> + 'd, _ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd) -> Self {
107 into_ref!(peri);
108 Self::new_inner(peri, 1)
109 }
110
111 pub fn new_2ch(
112 peri: impl Peripheral<P = T> + 'd,
113 _ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
114 _ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
115 ) -> Self {
116 into_ref!(peri);
117 Self::new_inner(peri, 2)
118 }
119
120 fn new_inner(peri: PeripheralRef<'d, T>, channels: u8) -> Self {
121 T::enable();
122 T::reset();
123
124 T::regs().cr().modify(|reg| {
125 for ch in 0..channels {
126 reg.set_en(ch as usize, true);
127 }
128 });
129
130 Self { channels, _peri: peri }
131 }
132
133 /// Check the channel is configured
134 fn check_channel_exists(&self, ch: Channel) -> Result<(), Error> {
135 if ch == Channel::Ch2 && self.channels < 2 {
136 Err(Error::UnconfiguredChannel)
137 } else {
138 Ok(())
139 }
140 }
141
142 fn set_channel_enable(&mut self, ch: Channel, on: bool) -> Result<(), Error> {
143 self.check_channel_exists(ch)?;
144 T::regs().cr().modify(|reg| {
145 reg.set_en(ch.index(), on);
146 });
147 Ok(())
148 }
149
150 pub fn enable_channel(&mut self, ch: Channel) -> Result<(), Error> {
151 self.set_channel_enable(ch, true)
152 }
153
154 pub fn disable_channel(&mut self, ch: Channel) -> Result<(), Error> {
155 self.set_channel_enable(ch, false)
156 }
157
158 pub fn select_trigger_ch1(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
159 self.check_channel_exists(Channel::Ch1)?;
160 unwrap!(self.disable_channel(Channel::Ch1));
161 T::regs().cr().modify(|reg| {
162 reg.set_tsel1(trigger.tsel());
163 });
164 Ok(())
165 }
166
167 pub fn select_trigger_ch2(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
168 self.check_channel_exists(Channel::Ch2)?;
169 unwrap!(self.disable_channel(Channel::Ch2));
170 T::regs().cr().modify(|reg| {
171 reg.set_tsel2(trigger.tsel());
172 });
173 Ok(())
174 }
175
176 pub fn trigger(&mut self, ch: Channel) -> Result<(), Error> {
177 self.check_channel_exists(ch)?;
178 T::regs().swtrigr().write(|reg| {
179 reg.set_swtrig(ch.index(), true);
180 });
181 Ok(())
182 }
183
184 pub fn trigger_all(&mut self) {
185 T::regs().swtrigr().write(|reg| {
186 reg.set_swtrig(Channel::Ch1.index(), true);
187 reg.set_swtrig(Channel::Ch2.index(), true);
188 });
189 }
190
191 pub fn set(&mut self, ch: Channel, value: Value) -> Result<(), Error> {
192 self.check_channel_exists(ch)?;
193 match value {
194 Value::Bit8(v) => T::regs().dhr8r(ch.index()).write(|reg| reg.set_dhr(v)),
195 Value::Bit12(v, Alignment::Left) => T::regs().dhr12l(ch.index()).write(|reg| reg.set_dhr(v)),
196 Value::Bit12(v, Alignment::Right) => T::regs().dhr12r(ch.index()).write(|reg| reg.set_dhr(v)),
197 }
198 Ok(())
199 }
200}
201
202pub(crate) mod sealed {
203 pub trait Instance {
204 fn regs() -> &'static crate::pac::dac::Dac;
205 }
206}
207
208pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
209
210pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
211
212foreach_peripheral!(
213 (dac, $inst:ident) => {
214 // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
215 #[cfg(rcc_h7)]
216 impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
217 fn frequency() -> crate::time::Hertz {
218 critical_section::with(|_| unsafe {
219 crate::rcc::get_freqs().apb1
220 })
221 }
222
223 fn reset() {
224 critical_section::with(|_| {
225 crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
226 crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
227 })
228 }
229
230 fn enable() {
231 critical_section::with(|_| {
232 crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
233 })
234 }
235
236 fn disable() {
237 critical_section::with(|_| {
238 crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false));
239 })
240 }
241 }
242
243 #[cfg(rcc_h7)]
244 impl crate::rcc::RccPeripheral for peripherals::$inst {}
245
246 impl crate::dac::sealed::Instance for peripherals::$inst {
247 fn regs() -> &'static crate::pac::dac::Dac {
248 &crate::pac::$inst
249 }
250 }
251
252 impl crate::dac::Instance for peripherals::$inst {}
253 };
254);
255
256macro_rules! impl_dac_pin {
257 ($inst:ident, $pin:ident, $ch:expr) => {
258 impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
259 };
260}
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs
new file mode 100644
index 000000000..1dc13949d
--- /dev/null
+++ b/embassy-stm32/src/dac/mod.rs
@@ -0,0 +1,570 @@
1#![macro_use]
2
3//! Provide access to the STM32 digital-to-analog converter (DAC).
4use core::marker::PhantomData;
5
6use embassy_hal_common::{into_ref, PeripheralRef};
7
8use crate::pac::dac;
9use crate::rcc::RccPeripheral;
10use crate::{peripherals, Peripheral};
11
12#[derive(Debug, Copy, Clone, Eq, PartialEq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14/// Curstom Errors
15pub enum Error {
16 UnconfiguredChannel,
17 InvalidValue,
18}
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22/// DAC Channels
23pub enum Channel {
24 Ch1,
25 Ch2,
26}
27
28impl Channel {
29 const fn index(&self) -> usize {
30 match self {
31 Channel::Ch1 => 0,
32 Channel::Ch2 => 1,
33 }
34 }
35}
36
37#[derive(Debug, Copy, Clone, Eq, PartialEq)]
38#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39/// Trigger sources for CH1
40pub enum Ch1Trigger {
41 Tim6,
42 Tim3,
43 Tim7,
44 Tim15,
45 Tim2,
46 Exti9,
47 Software,
48}
49
50impl Ch1Trigger {
51 fn tsel(&self) -> dac::vals::Tsel1 {
52 match self {
53 Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
54 Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
55 Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
56 Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
57 Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
58 Ch1Trigger::Exti9 => dac::vals::Tsel1::EXTI9,
59 Ch1Trigger::Software => dac::vals::Tsel1::SOFTWARE,
60 }
61 }
62}
63
64#[derive(Debug, Copy, Clone, Eq, PartialEq)]
65#[cfg_attr(feature = "defmt", derive(defmt::Format))]
66/// Trigger sources for CH2
67pub enum Ch2Trigger {
68 Tim6,
69 Tim8,
70 Tim7,
71 Tim5,
72 Tim2,
73 Tim4,
74 Exti9,
75 Software,
76}
77
78impl Ch2Trigger {
79 fn tsel(&self) -> dac::vals::Tsel2 {
80 match self {
81 Ch2Trigger::Tim6 => dac::vals::Tsel2::TIM6_TRGO,
82 Ch2Trigger::Tim8 => dac::vals::Tsel2::TIM8_TRGO,
83 Ch2Trigger::Tim7 => dac::vals::Tsel2::TIM7_TRGO,
84 Ch2Trigger::Tim5 => dac::vals::Tsel2::TIM5_TRGO,
85 Ch2Trigger::Tim2 => dac::vals::Tsel2::TIM2_TRGO,
86 Ch2Trigger::Tim4 => dac::vals::Tsel2::TIM4_TRGO,
87 Ch2Trigger::Exti9 => dac::vals::Tsel2::EXTI9,
88 Ch2Trigger::Software => dac::vals::Tsel2::SOFTWARE,
89 }
90 }
91}
92
93#[derive(Debug, Copy, Clone, Eq, PartialEq)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95/// Single 8 or 12 bit value that can be output by the DAC
96pub enum Value {
97 // 8 bit value
98 Bit8(u8),
99 // 12 bit value stored in a u16, left-aligned
100 Bit12Left(u16),
101 // 12 bit value stored in a u16, right-aligned
102 Bit12Right(u16),
103}
104
105#[derive(Debug, Copy, Clone, Eq, PartialEq)]
106#[cfg_attr(feature = "defmt", derive(defmt::Format))]
107/// Array variant of [`Value`]
108pub enum ValueArray<'a> {
109 // 8 bit values
110 Bit8(&'a [u8]),
111 // 12 bit value stored in a u16, left-aligned
112 Bit12Left(&'a [u16]),
113 // 12 bit values stored in a u16, right-aligned
114 Bit12Right(&'a [u16]),
115}
116/// Provide common functions for DAC channels
117pub trait DacChannel<T: Instance, Tx> {
118 const CHANNEL: Channel;
119
120 /// Enable trigger of the given channel
121 fn set_trigger_enable(&mut self, on: bool) -> Result<(), Error> {
122 T::regs().cr().modify(|reg| {
123 reg.set_ten(Self::CHANNEL.index(), on);
124 });
125 Ok(())
126 }
127
128 /// Set mode register of the given channel
129 #[cfg(dac_v2)]
130 fn set_channel_mode(&mut self, val: u8) -> Result<(), Error> {
131 T::regs().mcr().modify(|reg| {
132 reg.set_mode(Self::CHANNEL.index(), val);
133 });
134 Ok(())
135 }
136
137 /// Set enable register of the given channel
138 fn set_channel_enable(&mut self, on: bool) -> Result<(), Error> {
139 T::regs().cr().modify(|reg| {
140 reg.set_en(Self::CHANNEL.index(), on);
141 });
142 Ok(())
143 }
144
145 /// Enable the DAC channel `ch`
146 fn enable_channel(&mut self) -> Result<(), Error> {
147 self.set_channel_enable(true)
148 }
149
150 /// Disable the DAC channel `ch`
151 fn disable_channel(&mut self) -> Result<(), Error> {
152 self.set_channel_enable(false)
153 }
154
155 /// Perform a software trigger on `ch`
156 fn trigger(&mut self) {
157 T::regs().swtrigr().write(|reg| {
158 reg.set_swtrig(Self::CHANNEL.index(), true);
159 });
160 }
161
162 /// Set a value to be output by the DAC on trigger.
163 ///
164 /// The `value` is written to the corresponding "data holding register".
165 fn set(&mut self, value: Value) -> Result<(), Error> {
166 match value {
167 Value::Bit8(v) => T::regs().dhr8r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
168 Value::Bit12Left(v) => T::regs().dhr12l(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
169 Value::Bit12Right(v) => T::regs().dhr12r(Self::CHANNEL.index()).write(|reg| reg.set_dhr(v)),
170 }
171 Ok(())
172 }
173}
174
175/// Hold two DAC channels
176///
177/// Note: This consumes the DAC `Instance` only once, allowing to get both channels simultaneously.
178///
179/// # Example for obtaining both DAC channels
180///
181/// ```ignore
182/// // DMA channels and pins may need to be changed for your controller
183/// let (dac_ch1, dac_ch2) =
184/// embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
185/// ```
186pub struct Dac<'d, T: Instance, TxCh1, TxCh2> {
187 ch1: DacCh1<'d, T, TxCh1>,
188 ch2: DacCh2<'d, T, TxCh2>,
189}
190
191/// DAC CH1
192///
193/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
194pub struct DacCh1<'d, T: Instance, Tx> {
195 /// To consume T
196 _peri: PeripheralRef<'d, T>,
197 #[allow(unused)] // For chips whose DMA is not (yet) supported
198 dma: PeripheralRef<'d, Tx>,
199}
200
201/// DAC CH2
202///
203/// Note: This consumes the DAC `Instance`. Use [`Dac::new`] to get both channels simultaneously.
204pub struct DacCh2<'d, T: Instance, Tx> {
205 /// Instead of PeripheralRef to consume T
206 phantom: PhantomData<&'d mut T>,
207 #[allow(unused)] // For chips whose DMA is not (yet) supported
208 dma: PeripheralRef<'d, Tx>,
209}
210
211impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
212 /// Obtain DAC CH1
213 pub fn new(
214 peri: impl Peripheral<P = T> + 'd,
215 dma: impl Peripheral<P = Tx> + 'd,
216 _pin: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
217 ) -> Self {
218 into_ref!(peri, dma);
219 T::enable();
220 T::reset();
221
222 let mut dac = Self { _peri: peri, dma };
223
224 // Configure each activated channel. All results can be `unwrap`ed since they
225 // will only error if the channel is not configured (i.e. ch1, ch2 are false)
226 #[cfg(dac_v2)]
227 dac.set_channel_mode(0).unwrap();
228 dac.enable_channel().unwrap();
229 dac.set_trigger_enable(true).unwrap();
230
231 dac
232 }
233
234 /// Select a new trigger for this channel
235 ///
236 /// **Important**: This disables the channel!
237 pub fn select_trigger(&mut self, trigger: Ch1Trigger) -> Result<(), Error> {
238 unwrap!(self.disable_channel());
239 T::regs().cr().modify(|reg| {
240 reg.set_tsel1(trigger.tsel());
241 });
242 Ok(())
243 }
244
245 /// Write `data` to the DAC CH1 via DMA.
246 ///
247 /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
248 /// This will configure a circular DMA transfer that periodically outputs the `data`.
249 /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
250 ///
251 /// **Important:** Channel 1 has to be configured for the DAC instance!
252 #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though)
253 pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
254 where
255 Tx: DmaCh1<T>,
256 {
257 let channel = Channel::Ch1.index();
258 debug!("Writing to channel {}", channel);
259
260 // Enable DAC and DMA
261 T::regs().cr().modify(|w| {
262 w.set_en(channel, true);
263 w.set_dmaen(channel, true);
264 });
265
266 let tx_request = self.dma.request();
267 let dma_channel = &self.dma;
268
269 let tx_options = crate::dma::TransferOptions {
270 circular,
271 half_transfer_ir: false,
272 complete_transfer_ir: !circular,
273 ..Default::default()
274 };
275
276 // Initiate the correct type of DMA transfer depending on what data is passed
277 let tx_f = match data {
278 ValueArray::Bit8(buf) => unsafe {
279 crate::dma::Transfer::new_write(
280 dma_channel,
281 tx_request,
282 buf,
283 T::regs().dhr8r(channel).as_ptr() as *mut u8,
284 tx_options,
285 )
286 },
287 ValueArray::Bit12Left(buf) => unsafe {
288 crate::dma::Transfer::new_write(
289 dma_channel,
290 tx_request,
291 buf,
292 T::regs().dhr12l(channel).as_ptr() as *mut u16,
293 tx_options,
294 )
295 },
296 ValueArray::Bit12Right(buf) => unsafe {
297 crate::dma::Transfer::new_write(
298 dma_channel,
299 tx_request,
300 buf,
301 T::regs().dhr12r(channel).as_ptr() as *mut u16,
302 tx_options,
303 )
304 },
305 };
306
307 tx_f.await;
308
309 // finish dma
310 // TODO: Do we need to check any status registers here?
311 T::regs().cr().modify(|w| {
312 // Disable the DAC peripheral
313 w.set_en(channel, false);
314 // Disable the DMA. TODO: Is this necessary?
315 w.set_dmaen(channel, false);
316 });
317
318 Ok(())
319 }
320}
321
322impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
323 /// Obtain DAC CH2
324 pub fn new(
325 _peri: impl Peripheral<P = T> + 'd,
326 dma: impl Peripheral<P = Tx> + 'd,
327 _pin: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
328 ) -> Self {
329 into_ref!(_peri, dma);
330 T::enable();
331 T::reset();
332
333 let mut dac = Self {
334 phantom: PhantomData,
335 dma,
336 };
337
338 // Configure each activated channel. All results can be `unwrap`ed since they
339 // will only error if the channel is not configured (i.e. ch1, ch2 are false)
340 #[cfg(dac_v2)]
341 dac.set_channel_mode(0).unwrap();
342 dac.enable_channel().unwrap();
343 dac.set_trigger_enable(true).unwrap();
344
345 dac
346 }
347
348 /// Select a new trigger for this channel
349 pub fn select_trigger(&mut self, trigger: Ch2Trigger) -> Result<(), Error> {
350 unwrap!(self.disable_channel());
351 T::regs().cr().modify(|reg| {
352 reg.set_tsel2(trigger.tsel());
353 });
354 Ok(())
355 }
356
357 /// Write `data` to the DAC CH2 via DMA.
358 ///
359 /// To prevent delays/glitches when outputting a periodic waveform, the `circular` flag can be set.
360 /// This will configure a circular DMA transfer that periodically outputs the `data`.
361 /// Note that for performance reasons in circular mode the transfer complete interrupt is disabled.
362 ///
363 /// **Important:** Channel 2 has to be configured for the DAC instance!
364 #[cfg(all(bdma, not(dma)))] // It currently only works with BDMA-only chips (DMA should theoretically work though)
365 pub async fn write(&mut self, data: ValueArray<'_>, circular: bool) -> Result<(), Error>
366 where
367 Tx: DmaCh2<T>,
368 {
369 let channel = Channel::Ch2.index();
370 debug!("Writing to channel {}", channel);
371
372 // Enable DAC and DMA
373 T::regs().cr().modify(|w| {
374 w.set_en(channel, true);
375 w.set_dmaen(channel, true);
376 });
377
378 let tx_request = self.dma.request();
379 let dma_channel = &self.dma;
380
381 let tx_options = crate::dma::TransferOptions {
382 circular,
383 half_transfer_ir: false,
384 complete_transfer_ir: !circular,
385 ..Default::default()
386 };
387
388 // Initiate the correct type of DMA transfer depending on what data is passed
389 let tx_f = match data {
390 ValueArray::Bit8(buf) => unsafe {
391 crate::dma::Transfer::new_write(
392 dma_channel,
393 tx_request,
394 buf,
395 T::regs().dhr8r(channel).as_ptr() as *mut u8,
396 tx_options,
397 )
398 },
399 ValueArray::Bit12Left(buf) => unsafe {
400 crate::dma::Transfer::new_write(
401 dma_channel,
402 tx_request,
403 buf,
404 T::regs().dhr12l(channel).as_ptr() as *mut u16,
405 tx_options,
406 )
407 },
408 ValueArray::Bit12Right(buf) => unsafe {
409 crate::dma::Transfer::new_write(
410 dma_channel,
411 tx_request,
412 buf,
413 T::regs().dhr12r(channel).as_ptr() as *mut u16,
414 tx_options,
415 )
416 },
417 };
418
419 tx_f.await;
420
421 // finish dma
422 // TODO: Do we need to check any status registers here?
423 T::regs().cr().modify(|w| {
424 // Disable the DAC peripheral
425 w.set_en(channel, false);
426 // Disable the DMA. TODO: Is this necessary?
427 w.set_dmaen(channel, false);
428 });
429
430 Ok(())
431 }
432}
433
434impl<'d, T: Instance, TxCh1, TxCh2> Dac<'d, T, TxCh1, TxCh2> {
435 /// Create a new DAC instance with both channels.
436 ///
437 /// This is used to obtain two independent channels via `split()` for use e.g. with DMA.
438 pub fn new(
439 peri: impl Peripheral<P = T> + 'd,
440 dma_ch1: impl Peripheral<P = TxCh1> + 'd,
441 dma_ch2: impl Peripheral<P = TxCh2> + 'd,
442 _pin_ch1: impl Peripheral<P = impl DacPin<T, 1>> + 'd,
443 _pin_ch2: impl Peripheral<P = impl DacPin<T, 2>> + 'd,
444 ) -> Self {
445 into_ref!(peri, dma_ch1, dma_ch2);
446 T::enable();
447 T::reset();
448
449 let mut dac_ch1 = DacCh1 {
450 _peri: peri,
451 dma: dma_ch1,
452 };
453
454 let mut dac_ch2 = DacCh2 {
455 phantom: PhantomData,
456 dma: dma_ch2,
457 };
458
459 // Configure each activated channel. All results can be `unwrap`ed since they
460 // will only error if the channel is not configured (i.e. ch1, ch2 are false)
461 #[cfg(dac_v2)]
462 dac_ch1.set_channel_mode(0).unwrap();
463 dac_ch1.enable_channel().unwrap();
464 dac_ch1.set_trigger_enable(true).unwrap();
465
466 #[cfg(dac_v2)]
467 dac_ch2.set_channel_mode(0).unwrap();
468 dac_ch2.enable_channel().unwrap();
469 dac_ch2.set_trigger_enable(true).unwrap();
470
471 Self {
472 ch1: dac_ch1,
473 ch2: dac_ch2,
474 }
475 }
476
477 /// Split the DAC into CH1 and CH2 for independent use.
478 pub fn split(self) -> (DacCh1<'d, T, TxCh1>, DacCh2<'d, T, TxCh2>) {
479 (self.ch1, self.ch2)
480 }
481
482 /// Get mutable reference to CH1
483 pub fn ch1_mut(&mut self) -> &mut DacCh1<'d, T, TxCh1> {
484 &mut self.ch1
485 }
486
487 /// Get mutable reference to CH2
488 pub fn ch2_mut(&mut self) -> &mut DacCh2<'d, T, TxCh2> {
489 &mut self.ch2
490 }
491
492 /// Get reference to CH1
493 pub fn ch1(&mut self) -> &DacCh1<'d, T, TxCh1> {
494 &self.ch1
495 }
496
497 /// Get reference to CH2
498 pub fn ch2(&mut self) -> &DacCh2<'d, T, TxCh2> {
499 &self.ch2
500 }
501}
502
503impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh1<'d, T, Tx> {
504 const CHANNEL: Channel = Channel::Ch1;
505}
506
507impl<'d, T: Instance, Tx> DacChannel<T, Tx> for DacCh2<'d, T, Tx> {
508 const CHANNEL: Channel = Channel::Ch2;
509}
510
511pub(crate) mod sealed {
512 pub trait Instance {
513 fn regs() -> &'static crate::pac::dac::Dac;
514 }
515}
516
517pub trait Instance: sealed::Instance + RccPeripheral + 'static {}
518dma_trait!(DmaCh1, Instance);
519dma_trait!(DmaCh2, Instance);
520
521/// Marks a pin that can be used with the DAC
522pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
523
524foreach_peripheral!(
525 (dac, $inst:ident) => {
526 // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
527 #[cfg(rcc_h7)]
528 impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
529 fn frequency() -> crate::time::Hertz {
530 critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
531 }
532
533 fn reset() {
534 critical_section::with(|_| {
535 crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(true));
536 crate::pac::RCC.apb1lrstr().modify(|w| w.set_dac12rst(false));
537 })
538 }
539
540 fn enable() {
541 critical_section::with(|_| {
542 crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true));
543 })
544 }
545
546 fn disable() {
547 critical_section::with(|_| {
548 crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(false))
549 })
550 }
551 }
552
553 #[cfg(rcc_h7)]
554 impl crate::rcc::RccPeripheral for peripherals::$inst {}
555
556 impl crate::dac::sealed::Instance for peripherals::$inst {
557 fn regs() -> &'static crate::pac::dac::Dac {
558 &crate::pac::$inst
559 }
560 }
561
562 impl crate::dac::Instance for peripherals::$inst {}
563 };
564);
565
566macro_rules! impl_dac_pin {
567 ($inst:ident, $pin:ident, $ch:expr) => {
568 impl crate::dac::DacPin<peripherals::$inst, $ch> for crate::peripherals::$pin {}
569 };
570}
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs
index a307c803c..5a87888b7 100644
--- a/embassy-stm32/src/dma/bdma.rs
+++ b/embassy-stm32/src/dma/bdma.rs
@@ -21,11 +21,22 @@ use crate::pac::bdma::{regs, vals};
21#[derive(Debug, Copy, Clone, PartialEq, Eq)] 21#[derive(Debug, Copy, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))] 22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23#[non_exhaustive] 23#[non_exhaustive]
24pub struct TransferOptions {} 24pub struct TransferOptions {
25 /// Enable circular DMA
26 pub circular: bool,
27 /// Enable half transfer interrupt
28 pub half_transfer_ir: bool,
29 /// Enable transfer complete interrupt
30 pub complete_transfer_ir: bool,
31}
25 32
26impl Default for TransferOptions { 33impl Default for TransferOptions {
27 fn default() -> Self { 34 fn default() -> Self {
28 Self {} 35 Self {
36 circular: false,
37 half_transfer_ir: false,
38 complete_transfer_ir: true,
39 }
29 } 40 }
30} 41}
31 42
@@ -253,7 +264,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
253 mem_len: usize, 264 mem_len: usize,
254 incr_mem: bool, 265 incr_mem: bool,
255 data_size: WordSize, 266 data_size: WordSize,
256 _options: TransferOptions, 267 options: TransferOptions,
257 ) -> Self { 268 ) -> Self {
258 let ch = channel.regs().ch(channel.num()); 269 let ch = channel.regs().ch(channel.num());
259 270
@@ -283,7 +294,15 @@ impl<'a, C: Channel> Transfer<'a, C> {
283 } 294 }
284 w.set_dir(dir.into()); 295 w.set_dir(dir.into());
285 w.set_teie(true); 296 w.set_teie(true);
286 w.set_tcie(true); 297 w.set_tcie(options.complete_transfer_ir);
298 w.set_htie(options.half_transfer_ir);
299 if options.circular {
300 w.set_circ(vals::Circ::ENABLED);
301 debug!("Setting circular mode");
302 } else {
303 w.set_circ(vals::Circ::DISABLED);
304 }
305 w.set_pl(vals::Pl::VERYHIGH);
287 w.set_en(true); 306 w.set_en(true);
288 }); 307 });
289 308
@@ -310,8 +329,9 @@ impl<'a, C: Channel> Transfer<'a, C> {
310 pub fn is_running(&mut self) -> bool { 329 pub fn is_running(&mut self) -> bool {
311 let ch = self.channel.regs().ch(self.channel.num()); 330 let ch = self.channel.regs().ch(self.channel.num());
312 let en = ch.cr().read().en(); 331 let en = ch.cr().read().en();
332 let circular = ch.cr().read().circ() == vals::Circ::ENABLED;
313 let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0; 333 let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
314 en && !tcif 334 en && (circular || !tcif)
315 } 335 }
316 336
317 /// Gets the total remaining transfers for the channel 337 /// Gets the total remaining transfers for the channel
@@ -477,6 +497,8 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
477 let ch = self.channel.regs().ch(self.channel.num()); 497 let ch = self.channel.regs().ch(self.channel.num());
478 498
479 // Disable the channel. Keep the IEs enabled so the irqs still fire. 499 // Disable the channel. Keep the IEs enabled so the irqs still fire.
500 // If the channel is enabled and transfer is not completed, we need to perform
501 // two separate write access to the CR register to disable the channel.
480 ch.cr().write(|w| { 502 ch.cr().write(|w| {
481 w.set_teie(true); 503 w.set_teie(true);
482 w.set_htie(true); 504 w.set_htie(true);
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs
index 01a073bb9..668378bea 100644
--- a/embassy-stm32/src/eth/v1/rx_desc.rs
+++ b/embassy-stm32/src/eth/v1/rx_desc.rs
@@ -174,7 +174,7 @@ impl<'a> RDesRing<'a> {
174 // Receive descriptor unavailable 174 // Receive descriptor unavailable
175 Rps::SUSPENDED => RunningState::Stopped, 175 Rps::SUSPENDED => RunningState::Stopped,
176 // Closing receive descriptor 176 // Closing receive descriptor
177 Rps(0b101) => RunningState::Running, 177 Rps::_RESERVED_5 => RunningState::Running,
178 // Transferring the receive packet data from receive buffer to host memory 178 // Transferring the receive packet data from receive buffer to host memory
179 Rps::RUNNINGWRITING => RunningState::Running, 179 Rps::RUNNINGWRITING => RunningState::Running,
180 _ => RunningState::Unknown, 180 _ => RunningState::Unknown,
diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs
index 0e153202e..4d64d005c 100644
--- a/embassy-stm32/src/pwm/complementary_pwm.rs
+++ b/embassy-stm32/src/pwm/complementary_pwm.rs
@@ -243,7 +243,7 @@ mod tests {
243 for test_run in fn_results { 243 for test_run in fn_results {
244 let (ckd, bits) = compute_dead_time_value(test_run.value); 244 let (ckd, bits) = compute_dead_time_value(test_run.value);
245 245
246 assert_eq!(ckd.0, test_run.ckd.0); 246 assert_eq!(ckd.to_bits(), test_run.ckd.to_bits());
247 assert_eq!(bits, test_run.bits); 247 assert_eq!(bits, test_run.bits);
248 } 248 }
249 } 249 }
diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs
index 6c7b36647..df6e9047c 100644
--- a/embassy-stm32/src/rcc/c0.rs
+++ b/embassy-stm32/src/rcc/c0.rs
@@ -126,7 +126,7 @@ pub(crate) unsafe fn init(config: Config) {
126 }); 126 });
127 while !RCC.cr().read().hsirdy() {} 127 while !RCC.cr().read().hsirdy() {}
128 128
129 (HSI_FREQ.0 >> div.0, Sw::HSI) 129 (HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
130 } 130 }
131 ClockSrc::HSE(freq) => { 131 ClockSrc::HSE(freq) => {
132 // Enable HSE 132 // Enable HSE
@@ -157,7 +157,7 @@ pub(crate) unsafe fn init(config: Config) {
157 let mut set_flash_latency_after = false; 157 let mut set_flash_latency_after = false;
158 FLASH.acr().modify(|w| { 158 FLASH.acr().modify(|w| {
159 // Is the current flash latency less than what we need at the new SYSCLK? 159 // Is the current flash latency less than what we need at the new SYSCLK?
160 if w.latency().0 <= target_flash_latency.0 { 160 if w.latency().to_bits() <= target_flash_latency.to_bits() {
161 // We must increase the number of wait states now 161 // We must increase the number of wait states now
162 w.set_latency(target_flash_latency) 162 w.set_latency(target_flash_latency)
163 } else { 163 } else {
@@ -171,12 +171,12 @@ pub(crate) unsafe fn init(config: Config) {
171 // > Flash memory. 171 // > Flash memory.
172 // 172 //
173 // Enable flash prefetching if we have at least one wait state, and disable it otherwise. 173 // Enable flash prefetching if we have at least one wait state, and disable it otherwise.
174 w.set_prften(target_flash_latency.0 > 0); 174 w.set_prften(target_flash_latency.to_bits() > 0);
175 }); 175 });
176 176
177 if !set_flash_latency_after { 177 if !set_flash_latency_after {
178 // Spin until the effective flash latency is compatible with the clock change 178 // Spin until the effective flash latency is compatible with the clock change
179 while FLASH.acr().read().latency().0 < target_flash_latency.0 {} 179 while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
180 } 180 }
181 181
182 // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once 182 // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
@@ -218,7 +218,7 @@ pub(crate) unsafe fn init(config: Config) {
218 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 218 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
219 pre => { 219 pre => {
220 let pre: Ppre = pre.into(); 220 let pre: Ppre = pre.into();
221 let pre: u8 = 1 << (pre.0 - 3); 221 let pre: u8 = 1 << (pre.to_bits() - 3);
222 let freq = ahb_freq / pre as u32; 222 let freq = ahb_freq / pre as u32;
223 (freq, freq * 2) 223 (freq, freq * 2)
224 } 224 }
diff --git a/embassy-stm32/src/rcc/f0.rs b/embassy-stm32/src/rcc/f0.rs
index eb62ab661..ca6eed284 100644
--- a/embassy-stm32/src/rcc/f0.rs
+++ b/embassy-stm32/src/rcc/f0.rs
@@ -1,3 +1,5 @@
1use stm32_metapac::flash::vals::Latency;
2
1use super::{set_freqs, Clocks}; 3use super::{set_freqs, Clocks};
2use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw}; 4use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
3use crate::pac::{FLASH, RCC}; 5use crate::pac::{FLASH, RCC};
@@ -85,14 +87,11 @@ pub(crate) unsafe fn init(config: Config) {
85 let timer_mul = if ppre == 1 { 1 } else { 2 }; 87 let timer_mul = if ppre == 1 { 1 } else { 2 };
86 88
87 FLASH.acr().write(|w| { 89 FLASH.acr().write(|w| {
88 let latency = if real_sysclk <= 24_000_000 { 90 w.set_latency(if real_sysclk <= 24_000_000 {
89 0 91 Latency::WS0
90 } else if real_sysclk <= 48_000_000 {
91 1
92 } else { 92 } else {
93 2 93 Latency::WS1
94 }; 94 });
95 w.latency().0 = latency;
96 }); 95 });
97 96
98 match (config.hse.is_some(), use_hsi48) { 97 match (config.hse.is_some(), use_hsi48) {
@@ -134,20 +133,20 @@ pub(crate) unsafe fn init(config: Config) {
134 // TODO: Option to use CRS (Clock Recovery) 133 // TODO: Option to use CRS (Clock Recovery)
135 134
136 if let Some(pllmul_bits) = pllmul_bits { 135 if let Some(pllmul_bits) = pllmul_bits {
137 RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits))); 136 RCC.cfgr().modify(|w| w.set_pllmul(Pllmul::from_bits(pllmul_bits)));
138 137
139 RCC.cr().modify(|w| w.set_pllon(true)); 138 RCC.cr().modify(|w| w.set_pllon(true));
140 while !RCC.cr().read().pllrdy() {} 139 while !RCC.cr().read().pllrdy() {}
141 140
142 RCC.cfgr().modify(|w| { 141 RCC.cfgr().modify(|w| {
143 w.set_ppre(Ppre(ppre_bits)); 142 w.set_ppre(Ppre::from_bits(ppre_bits));
144 w.set_hpre(Hpre(hpre_bits)); 143 w.set_hpre(Hpre::from_bits(hpre_bits));
145 w.set_sw(Sw::PLL) 144 w.set_sw(Sw::PLL)
146 }); 145 });
147 } else { 146 } else {
148 RCC.cfgr().modify(|w| { 147 RCC.cfgr().modify(|w| {
149 w.set_ppre(Ppre(ppre_bits)); 148 w.set_ppre(Ppre::from_bits(ppre_bits));
150 w.set_hpre(Hpre(hpre_bits)); 149 w.set_hpre(Hpre::from_bits(hpre_bits));
151 150
152 if config.hse.is_some() { 151 if config.hse.is_some() {
153 w.set_sw(Sw::HSE); 152 w.set_sw(Sw::HSE);
diff --git a/embassy-stm32/src/rcc/f1.rs b/embassy-stm32/src/rcc/f1.rs
index 4769b7059..b6200231e 100644
--- a/embassy-stm32/src/rcc/f1.rs
+++ b/embassy-stm32/src/rcc/f1.rs
@@ -106,11 +106,11 @@ pub(crate) unsafe fn init(config: Config) {
106 // Only needed for stm32f103? 106 // Only needed for stm32f103?
107 FLASH.acr().write(|w| { 107 FLASH.acr().write(|w| {
108 w.set_latency(if real_sysclk <= 24_000_000 { 108 w.set_latency(if real_sysclk <= 24_000_000 {
109 Latency(0b000) 109 Latency::WS0
110 } else if real_sysclk <= 48_000_000 { 110 } else if real_sysclk <= 48_000_000 {
111 Latency(0b001) 111 Latency::WS1
112 } else { 112 } else {
113 Latency(0b010) 113 Latency::WS2
114 }); 114 });
115 }); 115 });
116 116
@@ -147,12 +147,13 @@ pub(crate) unsafe fn init(config: Config) {
147 147
148 if let Some(pllmul_bits) = pllmul_bits { 148 if let Some(pllmul_bits) = pllmul_bits {
149 let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 }; 149 let pllctpre_flag: u8 = if config.pllxtpre { 1 } else { 0 };
150 RCC.cfgr().modify(|w| w.set_pllxtpre(Pllxtpre(pllctpre_flag))); 150 RCC.cfgr()
151 .modify(|w| w.set_pllxtpre(Pllxtpre::from_bits(pllctpre_flag)));
151 152
152 // enable PLL and wait for it to be ready 153 // enable PLL and wait for it to be ready
153 RCC.cfgr().modify(|w| { 154 RCC.cfgr().modify(|w| {
154 w.set_pllmul(Pllmul(pllmul_bits)); 155 w.set_pllmul(Pllmul::from_bits(pllmul_bits));
155 w.set_pllsrc(Pllsrc(config.hse.is_some() as u8)); 156 w.set_pllsrc(Pllsrc::from_bits(config.hse.is_some() as u8));
156 }); 157 });
157 158
158 RCC.cr().modify(|w| w.set_pllon(true)); 159 RCC.cr().modify(|w| w.set_pllon(true));
@@ -161,22 +162,19 @@ pub(crate) unsafe fn init(config: Config) {
161 162
162 // Only needed for stm32f103? 163 // Only needed for stm32f103?
163 RCC.cfgr().modify(|w| { 164 RCC.cfgr().modify(|w| {
164 w.set_adcpre(Adcpre(apre_bits)); 165 w.set_adcpre(Adcpre::from_bits(apre_bits));
165 w.set_ppre2(Ppre1(ppre2_bits)); 166 w.set_ppre2(Ppre1::from_bits(ppre2_bits));
166 w.set_ppre1(Ppre1(ppre1_bits)); 167 w.set_ppre1(Ppre1::from_bits(ppre1_bits));
167 w.set_hpre(Hpre(hpre_bits)); 168 w.set_hpre(Hpre::from_bits(hpre_bits));
168 #[cfg(not(rcc_f100))] 169 #[cfg(not(rcc_f100))]
169 w.set_usbpre(Usbpre(usbpre as u8)); 170 w.set_usbpre(Usbpre::from_bits(usbpre as u8));
170 w.set_sw(Sw(if pllmul_bits.is_some() { 171 w.set_sw(if pllmul_bits.is_some() {
171 // PLL 172 Sw::PLL
172 0b10
173 } else if config.hse.is_some() { 173 } else if config.hse.is_some() {
174 // HSE 174 Sw::HSE
175 0b1
176 } else { 175 } else {
177 // HSI 176 Sw::HSI
178 0b0 177 });
179 }));
180 }); 178 });
181 179
182 set_freqs(Clocks { 180 set_freqs(Clocks {
diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs
index bcae64d0f..1525cc3c3 100644
--- a/embassy-stm32/src/rcc/f2.rs
+++ b/embassy-stm32/src/rcc/f2.rs
@@ -485,7 +485,7 @@ pub(crate) unsafe fn init(config: Config) {
485 w.set_ppre1(config.apb1_pre.into()); 485 w.set_ppre1(config.apb1_pre.into());
486 w.set_ppre2(config.apb2_pre.into()); 486 w.set_ppre2(config.apb2_pre.into());
487 }); 487 });
488 while RCC.cfgr().read().sws() != sw.0 {} 488 while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
489 489
490 // Turn off HSI to save power if we don't need it 490 // Turn off HSI to save power if we don't need it
491 if !config.hsi { 491 if !config.hsi {
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index bc430afb2..b84470440 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -87,7 +87,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
87 87
88 let sysclk = pllsysclk.unwrap_or(pllsrcclk); 88 let sysclk = pllsysclk.unwrap_or(pllsrcclk);
89 if pllsysclk.is_none() && !pll48clk { 89 if pllsysclk.is_none() && !pll48clk {
90 RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); 90 RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
91 91
92 return PllResults { 92 return PllResults {
93 use_pll: false, 93 use_pll: false,
@@ -141,9 +141,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Opti
141 RCC.pllcfgr().modify(|w| { 141 RCC.pllcfgr().modify(|w| {
142 w.set_pllm(pllm as u8); 142 w.set_pllm(pllm as u8);
143 w.set_plln(plln as u16); 143 w.set_plln(plln as u16);
144 w.set_pllp(Pllp(pllp as u8)); 144 w.set_pllp(Pllp::from_bits(pllp as u8));
145 w.set_pllq(pllq as u8); 145 w.set_pllq(pllq as u8);
146 w.set_pllsrc(Pllsrc(use_hse as u8)); 146 w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
147 }); 147 });
148 148
149 let real_pllsysclk = vco_in * plln / sysclk_div; 149 let real_pllsysclk = vco_in * plln / sysclk_div;
@@ -323,7 +323,7 @@ fn flash_setup(sysclk: u32) {
323 critical_section::with(|_| { 323 critical_section::with(|_| {
324 FLASH 324 FLASH
325 .acr() 325 .acr()
326 .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); 326 .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
327 }); 327 });
328} 328}
329 329
@@ -440,8 +440,8 @@ pub(crate) unsafe fn init(config: Config) {
440 } 440 }
441 441
442 RCC.cfgr().modify(|w| { 442 RCC.cfgr().modify(|w| {
443 w.set_ppre2(Ppre(ppre2_bits)); 443 w.set_ppre2(Ppre::from_bits(ppre2_bits));
444 w.set_ppre1(Ppre(ppre1_bits)); 444 w.set_ppre1(Ppre::from_bits(ppre1_bits));
445 w.set_hpre(hpre_bits); 445 w.set_hpre(hpre_bits);
446 }); 446 });
447 447
diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs
index 71215cac5..85cb9c661 100644
--- a/embassy-stm32/src/rcc/f7.rs
+++ b/embassy-stm32/src/rcc/f7.rs
@@ -30,7 +30,7 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
30 30
31 let sysclk = pllsysclk.unwrap_or(pllsrcclk); 31 let sysclk = pllsysclk.unwrap_or(pllsrcclk);
32 if pllsysclk.is_none() && !pll48clk { 32 if pllsysclk.is_none() && !pll48clk {
33 RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); 33 RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
34 34
35 return PllResults { 35 return PllResults {
36 use_pll: false, 36 use_pll: false,
@@ -83,9 +83,9 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
83 RCC.pllcfgr().modify(|w| { 83 RCC.pllcfgr().modify(|w| {
84 w.set_pllm(pllm as u8); 84 w.set_pllm(pllm as u8);
85 w.set_plln(plln as u16); 85 w.set_plln(plln as u16);
86 w.set_pllp(Pllp(pllp as u8)); 86 w.set_pllp(Pllp::from_bits(pllp as u8));
87 w.set_pllq(pllq as u8); 87 w.set_pllq(pllq as u8);
88 w.set_pllsrc(Pllsrc(use_hse as u8)); 88 w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
89 }); 89 });
90 90
91 let real_pllsysclk = vco_in * plln / sysclk_div; 91 let real_pllsysclk = vco_in * plln / sysclk_div;
@@ -106,7 +106,7 @@ fn flash_setup(sysclk: u32) {
106 critical_section::with(|_| { 106 critical_section::with(|_| {
107 FLASH 107 FLASH
108 .acr() 108 .acr()
109 .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); 109 .modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
110 }); 110 });
111} 111}
112 112
@@ -246,8 +246,8 @@ pub(crate) unsafe fn init(config: Config) {
246 } 246 }
247 247
248 RCC.cfgr().modify(|w| { 248 RCC.cfgr().modify(|w| {
249 w.set_ppre2(Ppre(ppre2_bits)); 249 w.set_ppre2(Ppre::from_bits(ppre2_bits));
250 w.set_ppre1(Ppre(ppre1_bits)); 250 w.set_ppre1(Ppre::from_bits(ppre1_bits));
251 w.set_hpre(hpre_bits); 251 w.set_hpre(hpre_bits);
252 }); 252 });
253 253
diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs
index 17c73c36b..5e3a7911a 100644
--- a/embassy-stm32/src/rcc/g0.rs
+++ b/embassy-stm32/src/rcc/g0.rs
@@ -344,7 +344,7 @@ pub(crate) unsafe fn init(config: Config) {
344 }); 344 });
345 while !RCC.cr().read().hsirdy() {} 345 while !RCC.cr().read().hsirdy() {}
346 346
347 (HSI_FREQ.0 >> div.0, Sw::HSI) 347 (HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
348 } 348 }
349 ClockSrc::HSE(freq) => { 349 ClockSrc::HSE(freq) => {
350 // Enable HSE 350 // Enable HSE
@@ -381,7 +381,7 @@ pub(crate) unsafe fn init(config: Config) {
381 let mut set_flash_latency_after = false; 381 let mut set_flash_latency_after = false;
382 FLASH.acr().modify(|w| { 382 FLASH.acr().modify(|w| {
383 // Is the current flash latency less than what we need at the new SYSCLK? 383 // Is the current flash latency less than what we need at the new SYSCLK?
384 if w.latency().0 <= target_flash_latency.0 { 384 if w.latency().to_bits() <= target_flash_latency.to_bits() {
385 // We must increase the number of wait states now 385 // We must increase the number of wait states now
386 w.set_latency(target_flash_latency) 386 w.set_latency(target_flash_latency)
387 } else { 387 } else {
@@ -395,12 +395,12 @@ pub(crate) unsafe fn init(config: Config) {
395 // > Flash memory. 395 // > Flash memory.
396 // 396 //
397 // Enable flash prefetching if we have at least one wait state, and disable it otherwise. 397 // Enable flash prefetching if we have at least one wait state, and disable it otherwise.
398 w.set_prften(target_flash_latency.0 > 0); 398 w.set_prften(target_flash_latency.to_bits() > 0);
399 }); 399 });
400 400
401 if !set_flash_latency_after { 401 if !set_flash_latency_after {
402 // Spin until the effective flash latency is compatible with the clock change 402 // Spin until the effective flash latency is compatible with the clock change
403 while FLASH.acr().read().latency().0 < target_flash_latency.0 {} 403 while FLASH.acr().read().latency().to_bits() < target_flash_latency.to_bits() {}
404 } 404 }
405 405
406 // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once 406 // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
@@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: Config) {
442 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 442 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
443 pre => { 443 pre => {
444 let pre: Ppre = pre.into(); 444 let pre: Ppre = pre.into();
445 let pre: u8 = 1 << (pre.0 - 3); 445 let pre: u8 = 1 << (pre.to_bits() - 3);
446 let freq = ahb_freq / pre as u32; 446 let freq = ahb_freq / pre as u32;
447 (freq, freq * 2) 447 (freq, freq * 2)
448 } 448 }
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs
index 9401af4c3..ff8f97541 100644
--- a/embassy-stm32/src/rcc/g4.rs
+++ b/embassy-stm32/src/rcc/g4.rs
@@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
3use stm32_metapac::FLASH; 3use stm32_metapac::FLASH;
4 4
5use crate::pac::{PWR, RCC}; 5use crate::pac::{PWR, RCC};
6use crate::rcc::sealed::RccPeripheral;
6use crate::rcc::{set_freqs, Clocks}; 7use crate::rcc::{set_freqs, Clocks};
7use crate::time::Hertz; 8use crate::time::Hertz;
8 9
@@ -316,6 +317,27 @@ impl Into<Hpre> for AHBPrescaler {
316 } 317 }
317} 318}
318 319
320/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
321pub enum Clock48MhzSrc {
322 /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
323 /// oscillator to comply with the USB specification for oscillator tolerance.
324 Hsi48(Option<CrsConfig>),
325 /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
326 /// PLL needs to be using the HSE source to comply with the USB specification for oscillator
327 /// tolerance.
328 PllQ,
329}
330
331/// Sets the sync source for the Clock Recovery System (CRS).
332pub enum CrsSyncSource {
333 /// Use an external GPIO to sync the CRS.
334 Gpio,
335 /// Use the Low Speed External oscillator to sync the CRS.
336 Lse,
337 /// Use the USB SOF to sync the CRS.
338 Usb,
339}
340
319/// Clocks configutation 341/// Clocks configutation
320pub struct Config { 342pub struct Config {
321 pub mux: ClockSrc, 343 pub mux: ClockSrc,
@@ -326,6 +348,14 @@ pub struct Config {
326 /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration 348 /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
327 /// MUST turn on the PLLR output. 349 /// MUST turn on the PLLR output.
328 pub pll: Option<Pll>, 350 pub pll: Option<Pll>,
351 /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
352 pub clock_48mhz_src: Option<Clock48MhzSrc>,
353}
354
355/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
356pub struct CrsConfig {
357 /// Sync source for the CRS.
358 pub sync_src: CrsSyncSource,
329} 359}
330 360
331impl Default for Config { 361impl Default for Config {
@@ -338,6 +368,7 @@ impl Default for Config {
338 apb2_pre: APBPrescaler::NotDivided, 368 apb2_pre: APBPrescaler::NotDivided,
339 low_power_run: false, 369 low_power_run: false,
340 pll: None, 370 pll: None,
371 clock_48mhz_src: None,
341 } 372 }
342 } 373 }
343} 374}
@@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
430 assert!(pll_freq.is_some()); 461 assert!(pll_freq.is_some());
431 assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); 462 assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
432 463
433 let freq = pll_freq.unwrap().pll_r.unwrap().0; 464 let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0;
434 465
435 assert!(freq <= 170_000_000); 466 assert!(freq <= 170_000_000);
436 467
@@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) {
497 } 528 }
498 }; 529 };
499 530
531 // Setup the 48 MHz clock if needed
532 if let Some(clock_48mhz_src) = config.clock_48mhz_src {
533 let source = match clock_48mhz_src {
534 Clock48MhzSrc::PllQ => {
535 // Make sure the PLLQ is enabled and running at 48Mhz
536 let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
537 assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
538
539 crate::pac::rcc::vals::Clk48sel::PLLQCLK
540 }
541 Clock48MhzSrc::Hsi48(crs_config) => {
542 // Enable HSI48
543 RCC.crrcr().modify(|w| w.set_hsi48on(true));
544 // Wait for HSI48 to turn on
545 while RCC.crrcr().read().hsi48rdy() == false {}
546
547 // Enable and setup CRS if needed
548 if let Some(crs_config) = crs_config {
549 crate::peripherals::CRS::enable();
550
551 let sync_src = match crs_config.sync_src {
552 CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
553 CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
554 CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
555 };
556
557 crate::pac::CRS.cfgr().modify(|w| {
558 w.set_syncsrc(sync_src);
559 });
560
561 // These are the correct settings for standard USB operation. If other settings
562 // are needed there will need to be additional config options for the CRS.
563 crate::pac::CRS.cr().modify(|w| {
564 w.set_autotrimen(true);
565 w.set_cen(true);
566 });
567 }
568 crate::pac::rcc::vals::Clk48sel::HSI48
569 }
570 };
571
572 RCC.ccipr().modify(|w| w.set_clk48sel(source));
573 }
574
500 if config.low_power_run { 575 if config.low_power_run {
501 assert!(sys_clk <= 2_000_000); 576 assert!(sys_clk <= 2_000_000);
502 PWR.cr1().modify(|w| w.set_lpr(true)); 577 PWR.cr1().modify(|w| w.set_lpr(true));
diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs
index daa1cd61f..f3a98c794 100644
--- a/embassy-stm32/src/rcc/h7.rs
+++ b/embassy-stm32/src/rcc/h7.rs
@@ -601,22 +601,22 @@ pub(crate) unsafe fn init(mut config: Config) {
601 601
602 // Core Prescaler / AHB Prescaler / APB3 Prescaler 602 // Core Prescaler / AHB Prescaler / APB3 Prescaler
603 RCC.d1cfgr().modify(|w| { 603 RCC.d1cfgr().modify(|w| {
604 w.set_d1cpre(Hpre(d1cpre_bits)); 604 w.set_d1cpre(Hpre::from_bits(d1cpre_bits));
605 w.set_d1ppre(Dppre(ppre3_bits)); 605 w.set_d1ppre(Dppre::from_bits(ppre3_bits));
606 w.set_hpre(hpre_bits) 606 w.set_hpre(hpre_bits)
607 }); 607 });
608 // Ensure core prescaler value is valid before future lower 608 // Ensure core prescaler value is valid before future lower
609 // core voltage 609 // core voltage
610 while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {} 610 while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
611 611
612 // APB1 / APB2 Prescaler 612 // APB1 / APB2 Prescaler
613 RCC.d2cfgr().modify(|w| { 613 RCC.d2cfgr().modify(|w| {
614 w.set_d2ppre1(Dppre(ppre1_bits)); 614 w.set_d2ppre1(Dppre::from_bits(ppre1_bits));
615 w.set_d2ppre2(Dppre(ppre2_bits)); 615 w.set_d2ppre2(Dppre::from_bits(ppre2_bits));
616 }); 616 });
617 617
618 // APB4 Prescaler 618 // APB4 Prescaler
619 RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits))); 619 RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre::from_bits(ppre4_bits)));
620 620
621 // Peripheral Clock (per_ck) 621 // Peripheral Clock (per_ck)
622 RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel)); 622 RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
@@ -640,7 +640,7 @@ pub(crate) unsafe fn init(mut config: Config) {
640 _ => Sw::HSI, 640 _ => Sw::HSI,
641 }; 641 };
642 RCC.cfgr().modify(|w| w.set_sw(sw)); 642 RCC.cfgr().modify(|w| w.set_sw(sw));
643 while RCC.cfgr().read().sws() != sw.0 {} 643 while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
644 644
645 // IO compensation cell - Requires CSI clock and SYSCFG 645 // IO compensation cell - Requires CSI clock and SYSCFG
646 assert!(RCC.cr().read().csirdy()); 646 assert!(RCC.cr().read().csirdy());
@@ -806,7 +806,8 @@ mod pll {
806 RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false)); 806 RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
807 let vco_ck = ref_x_ck * pll_x_n; 807 let vco_ck = ref_x_ck * pll_x_n;
808 808
809 RCC.plldivr(plln).modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8))); 809 RCC.plldivr(plln)
810 .modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8)));
810 RCC.pllcfgr().modify(|w| w.set_divpen(plln, true)); 811 RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
811 812
812 // Calulate additional output dividers 813 // Calulate additional output dividers
diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs
index 42a481a74..46a528e31 100644
--- a/embassy-stm32/src/rcc/l0.rs
+++ b/embassy-stm32/src/rcc/l0.rs
@@ -1,7 +1,7 @@
1use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; 1use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
2use crate::pac::RCC; 2use crate::pac::RCC;
3#[cfg(crs)] 3#[cfg(crs)]
4use crate::pac::{CRS, SYSCFG}; 4use crate::pac::{crs, CRS, SYSCFG};
5use crate::rcc::{set_freqs, Clocks}; 5use crate::rcc::{set_freqs, Clocks};
6use crate::time::Hertz; 6use crate::time::Hertz;
7 7
@@ -293,7 +293,7 @@ pub(crate) unsafe fn init(config: Config) {
293 AHBPrescaler::NotDivided => sys_clk, 293 AHBPrescaler::NotDivided => sys_clk,
294 pre => { 294 pre => {
295 let pre: Hpre = pre.into(); 295 let pre: Hpre = pre.into();
296 let pre = 1 << (pre.0 as u32 - 7); 296 let pre = 1 << (pre.to_bits() as u32 - 7);
297 sys_clk / pre 297 sys_clk / pre
298 } 298 }
299 }; 299 };
@@ -302,7 +302,7 @@ pub(crate) unsafe fn init(config: Config) {
302 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 302 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
303 pre => { 303 pre => {
304 let pre: Ppre = pre.into(); 304 let pre: Ppre = pre.into();
305 let pre: u8 = 1 << (pre.0 - 3); 305 let pre: u8 = 1 << (pre.to_bits() - 3);
306 let freq = ahb_freq / pre as u32; 306 let freq = ahb_freq / pre as u32;
307 (freq, freq * 2) 307 (freq, freq * 2)
308 } 308 }
@@ -312,7 +312,7 @@ pub(crate) unsafe fn init(config: Config) {
312 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 312 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
313 pre => { 313 pre => {
314 let pre: Ppre = pre.into(); 314 let pre: Ppre = pre.into();
315 let pre: u8 = 1 << (pre.0 - 3); 315 let pre: u8 = 1 << (pre.to_bits() - 3);
316 let freq = ahb_freq / pre as u32; 316 let freq = ahb_freq / pre as u32;
317 (freq, freq * 2) 317 (freq, freq * 2)
318 } 318 }
@@ -338,7 +338,7 @@ pub(crate) unsafe fn init(config: Config) {
338 CRS.cfgr().write(|w| 338 CRS.cfgr().write(|w|
339 339
340 // Select LSE as synchronization source 340 // Select LSE as synchronization source
341 w.set_syncsrc(0b01)); 341 w.set_syncsrc(crs::vals::Syncsrc::LSE));
342 CRS.cr().modify(|w| { 342 CRS.cr().modify(|w| {
343 w.set_autotrimen(true); 343 w.set_autotrimen(true);
344 w.set_cen(true); 344 w.set_cen(true);
diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs
index c907fa88a..59a6eac8f 100644
--- a/embassy-stm32/src/rcc/l1.rs
+++ b/embassy-stm32/src/rcc/l1.rs
@@ -294,7 +294,7 @@ pub(crate) unsafe fn init(config: Config) {
294 AHBPrescaler::NotDivided => sys_clk, 294 AHBPrescaler::NotDivided => sys_clk,
295 pre => { 295 pre => {
296 let pre: Hpre = pre.into(); 296 let pre: Hpre = pre.into();
297 let pre = 1 << (pre.0 as u32 - 7); 297 let pre = 1 << (pre.to_bits() as u32 - 7);
298 sys_clk / pre 298 sys_clk / pre
299 } 299 }
300 }; 300 };
@@ -303,7 +303,7 @@ pub(crate) unsafe fn init(config: Config) {
303 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 303 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
304 pre => { 304 pre => {
305 let pre: Ppre = pre.into(); 305 let pre: Ppre = pre.into();
306 let pre: u8 = 1 << (pre.0 - 3); 306 let pre: u8 = 1 << (pre.to_bits() - 3);
307 let freq = ahb_freq / pre as u32; 307 let freq = ahb_freq / pre as u32;
308 (freq, freq * 2) 308 (freq, freq * 2)
309 } 309 }
@@ -313,7 +313,7 @@ pub(crate) unsafe fn init(config: Config) {
313 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 313 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
314 pre => { 314 pre => {
315 let pre: Ppre = pre.into(); 315 let pre: Ppre = pre.into();
316 let pre: u8 = 1 << (pre.0 - 3); 316 let pre: u8 = 1 << (pre.to_bits() - 3);
317 let freq = ahb_freq / pre as u32; 317 let freq = ahb_freq / pre as u32;
318 (freq, freq * 2) 318 (freq, freq * 2)
319 } 319 }
diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs
index f8c1a6e06..20cb8c91c 100644
--- a/embassy-stm32/src/rcc/l4.rs
+++ b/embassy-stm32/src/rcc/l4.rs
@@ -635,7 +635,7 @@ pub(crate) unsafe fn init(config: Config) {
635 AHBPrescaler::NotDivided => sys_clk, 635 AHBPrescaler::NotDivided => sys_clk,
636 pre => { 636 pre => {
637 let pre: Hpre = pre.into(); 637 let pre: Hpre = pre.into();
638 let pre = 1 << (pre.0 as u32 - 7); 638 let pre = 1 << (pre.to_bits() as u32 - 7);
639 sys_clk / pre 639 sys_clk / pre
640 } 640 }
641 }; 641 };
@@ -644,7 +644,7 @@ pub(crate) unsafe fn init(config: Config) {
644 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 644 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
645 pre => { 645 pre => {
646 let pre: Ppre = pre.into(); 646 let pre: Ppre = pre.into();
647 let pre: u8 = 1 << (pre.0 - 3); 647 let pre: u8 = 1 << (pre.to_bits() - 3);
648 let freq = ahb_freq / pre as u32; 648 let freq = ahb_freq / pre as u32;
649 (freq, freq * 2) 649 (freq, freq * 2)
650 } 650 }
@@ -654,7 +654,7 @@ pub(crate) unsafe fn init(config: Config) {
654 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 654 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
655 pre => { 655 pre => {
656 let pre: Ppre = pre.into(); 656 let pre: Ppre = pre.into();
657 let pre: u8 = 1 << (pre.0 - 3); 657 let pre: u8 = 1 << (pre.to_bits() - 3);
658 let freq = ahb_freq / pre as u32; 658 let freq = ahb_freq / pre as u32;
659 (freq, freq * 2) 659 (freq, freq * 2)
660 } 660 }
diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs
index f56fce365..16da65d5e 100644
--- a/embassy-stm32/src/rcc/l5.rs
+++ b/embassy-stm32/src/rcc/l5.rs
@@ -461,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
461 AHBPrescaler::NotDivided => sys_clk, 461 AHBPrescaler::NotDivided => sys_clk,
462 pre => { 462 pre => {
463 let pre: Hpre = pre.into(); 463 let pre: Hpre = pre.into();
464 let pre = 1 << (pre.0 as u32 - 7); 464 let pre = 1 << (pre.to_bits() as u32 - 7);
465 sys_clk / pre 465 sys_clk / pre
466 } 466 }
467 }; 467 };
@@ -470,7 +470,7 @@ pub(crate) unsafe fn init(config: Config) {
470 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 470 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
471 pre => { 471 pre => {
472 let pre: Ppre = pre.into(); 472 let pre: Ppre = pre.into();
473 let pre: u8 = 1 << (pre.0 - 3); 473 let pre: u8 = 1 << (pre.to_bits() - 3);
474 let freq = ahb_freq / pre as u32; 474 let freq = ahb_freq / pre as u32;
475 (freq, freq * 2) 475 (freq, freq * 2)
476 } 476 }
@@ -480,7 +480,7 @@ pub(crate) unsafe fn init(config: Config) {
480 APBPrescaler::NotDivided => (ahb_freq, ahb_freq), 480 APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
481 pre => { 481 pre => {
482 let pre: Ppre = pre.into(); 482 let pre: Ppre = pre.into();
483 let pre: u8 = 1 << (pre.0 - 3); 483 let pre: u8 = 1 << (pre.to_bits() - 3);
484 let freq = ahb_freq / pre as u32; 484 let freq = ahb_freq / pre as u32;
485 (freq, freq * 2) 485 (freq, freq * 2)
486 } 486 }
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs
index 81507a4d6..cfc07f069 100644
--- a/embassy-stm32/src/rcc/u5.rs
+++ b/embassy-stm32/src/rcc/u5.rs
@@ -126,7 +126,7 @@ pub enum PllM {
126 126
127impl Into<Pllm> for PllM { 127impl Into<Pllm> for PllM {
128 fn into(self) -> Pllm { 128 fn into(self) -> Pllm {
129 Pllm(self as u8) 129 Pllm::from_bits(self as u8)
130 } 130 }
131} 131}
132 132
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index e1615b34c..a2eace6d3 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -36,7 +36,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
36 #[cfg(rtc_v2wb)] 36 #[cfg(rtc_v2wb)]
37 let rtcsel = reg.rtcsel(); 37 let rtcsel = reg.rtcsel();
38 #[cfg(not(rtc_v2wb))] 38 #[cfg(not(rtc_v2wb))]
39 let rtcsel = reg.rtcsel().0; 39 let rtcsel = reg.rtcsel().to_bits();
40 40
41 if !reg.rtcen() || rtcsel != clock_config { 41 if !reg.rtcen() || rtcsel != clock_config {
42 #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] 42 #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
@@ -54,7 +54,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
54 54
55 // Select RTC source 55 // Select RTC source
56 #[cfg(not(rtc_v2wb))] 56 #[cfg(not(rtc_v2wb))]
57 w.set_rtcsel(Rtcsel(clock_config)); 57 w.set_rtcsel(Rtcsel::from_bits(clock_config));
58 #[cfg(rtc_v2wb)] 58 #[cfg(rtc_v2wb)]
59 w.set_rtcsel(clock_config); 59 w.set_rtcsel(clock_config);
60 w.set_rtcen(true); 60 w.set_rtcen(true);
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index 7c91046a2..7e5c64d90 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -26,7 +26,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
26 26
27 let config_rtcsel = rtc_config.clock_config as u8; 27 let config_rtcsel = rtc_config.clock_config as u8;
28 #[cfg(not(any(rcc_wl5, rcc_wle)))] 28 #[cfg(not(any(rcc_wl5, rcc_wle)))]
29 let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel); 29 let config_rtcsel = crate::pac::rcc::vals::Rtcsel::from_bits(config_rtcsel);
30 30
31 if !reg.rtcen() || reg.rtcsel() != config_rtcsel { 31 if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
32 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); 32 crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 80a336a48..698292bff 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -227,7 +227,11 @@ const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOp
227 fifo_threshold: Some(crate::dma::FifoThreshold::Full), 227 fifo_threshold: Some(crate::dma::FifoThreshold::Full),
228}; 228};
229#[cfg(all(sdmmc_v1, not(dma)))] 229#[cfg(all(sdmmc_v1, not(dma)))]
230const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {}; 230const DMA_TRANSFER_OPTIONS: crate::dma::TransferOptions = crate::dma::TransferOptions {
231 circular: false,
232 half_transfer_ir: false,
233 complete_transfer_ir: true,
234};
231 235
232/// SDMMC configuration 236/// SDMMC configuration
233/// 237///
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 1cddac992..c3224073d 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -650,7 +650,7 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br {
650 _ => 0b111, 650 _ => 0b111,
651 }; 651 };
652 652
653 Br(val) 653 Br::from_bits(val)
654} 654}
655 655
656trait RegsExt { 656trait RegsExt {
@@ -772,7 +772,7 @@ fn set_rxdmaen(regs: Regs, val: bool) {
772 772
773fn finish_dma(regs: Regs) { 773fn finish_dma(regs: Regs) {
774 #[cfg(spi_v2)] 774 #[cfg(spi_v2)]
775 while regs.sr().read().ftlvl() > 0 {} 775 while regs.sr().read().ftlvl().to_bits() > 0 {}
776 776
777 #[cfg(any(spi_v3, spi_v4, spi_v5))] 777 #[cfg(any(spi_v3, spi_v4, spi_v5))]
778 while !regs.sr().read().txc() {} 778 while !regs.sr().read().txc() {}
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 47a79c187..c97efbf0a 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -869,7 +869,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
869 _ => vals::Ps::EVEN, 869 _ => vals::Ps::EVEN,
870 }); 870 });
871 #[cfg(not(usart_v1))] 871 #[cfg(not(usart_v1))]
872 w.set_over8(vals::Over8(over8 as _)); 872 w.set_over8(vals::Over8::from_bits(over8 as _));
873 }); 873 });
874 874
875 #[cfg(not(usart_v1))] 875 #[cfg(not(usart_v1))]
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 2367127e8..ecdd1d0b8 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -97,8 +97,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
97 } 97 }
98 epr.set_dtog_rx(false); 98 epr.set_dtog_rx(false);
99 epr.set_dtog_tx(false); 99 epr.set_dtog_tx(false);
100 epr.set_stat_rx(Stat(0)); 100 epr.set_stat_rx(Stat::from_bits(0));
101 epr.set_stat_tx(Stat(0)); 101 epr.set_stat_tx(Stat::from_bits(0));
102 epr.set_ctr_rx(!epr.ctr_rx()); 102 epr.set_ctr_rx(!epr.ctr_rx());
103 epr.set_ctr_tx(!epr.ctr_tx()); 103 epr.set_ctr_tx(!epr.ctr_tx());
104 regs.epr(index).write_value(epr); 104 regs.epr(index).write_value(epr);
@@ -143,8 +143,8 @@ fn invariant(mut r: regs::Epr) -> regs::Epr {
143 r.set_ctr_tx(true); // don't clear 143 r.set_ctr_tx(true); // don't clear
144 r.set_dtog_rx(false); // don't toggle 144 r.set_dtog_rx(false); // don't toggle
145 r.set_dtog_tx(false); // don't toggle 145 r.set_dtog_tx(false); // don't toggle
146 r.set_stat_rx(Stat(0)); 146 r.set_stat_rx(Stat::from_bits(0));
147 r.set_stat_tx(Stat(0)); 147 r.set_stat_tx(Stat::from_bits(0));
148 r 148 r
149} 149}
150 150
@@ -480,56 +480,57 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
480 poll_fn(move |cx| { 480 poll_fn(move |cx| {
481 BUS_WAKER.register(cx.waker()); 481 BUS_WAKER.register(cx.waker());
482 482
483 if self.inited { 483 // TODO: implement VBUS detection.
484 let regs = T::regs(); 484 if !self.inited {
485 485 self.inited = true;
486 if IRQ_RESUME.load(Ordering::Acquire) { 486 return Poll::Ready(Event::PowerDetected);
487 IRQ_RESUME.store(false, Ordering::Relaxed); 487 }
488 return Poll::Ready(Event::Resume);
489 }
490 488
491 if IRQ_RESET.load(Ordering::Acquire) { 489 let regs = T::regs();
492 IRQ_RESET.store(false, Ordering::Relaxed);
493
494 trace!("RESET");
495 regs.daddr().write(|w| {
496 w.set_ef(true);
497 w.set_add(0);
498 });
499
500 regs.epr(0).write(|w| {
501 w.set_ep_type(EpType::CONTROL);
502 w.set_stat_rx(Stat::NAK);
503 w.set_stat_tx(Stat::NAK);
504 });
505
506 for i in 1..EP_COUNT {
507 regs.epr(i).write(|w| {
508 w.set_ea(i as _);
509 w.set_ep_type(self.ep_types[i - 1]);
510 })
511 }
512 490
513 for w in &EP_IN_WAKERS { 491 if IRQ_RESUME.load(Ordering::Acquire) {
514 w.wake() 492 IRQ_RESUME.store(false, Ordering::Relaxed);
515 } 493 return Poll::Ready(Event::Resume);
516 for w in &EP_OUT_WAKERS { 494 }
517 w.wake()
518 }
519 495
520 return Poll::Ready(Event::Reset); 496 if IRQ_RESET.load(Ordering::Acquire) {
497 IRQ_RESET.store(false, Ordering::Relaxed);
498
499 trace!("RESET");
500 regs.daddr().write(|w| {
501 w.set_ef(true);
502 w.set_add(0);
503 });
504
505 regs.epr(0).write(|w| {
506 w.set_ep_type(EpType::CONTROL);
507 w.set_stat_rx(Stat::NAK);
508 w.set_stat_tx(Stat::NAK);
509 });
510
511 for i in 1..EP_COUNT {
512 regs.epr(i).write(|w| {
513 w.set_ea(i as _);
514 w.set_ep_type(self.ep_types[i - 1]);
515 })
521 } 516 }
522 517
523 if IRQ_SUSPEND.load(Ordering::Acquire) { 518 for w in &EP_IN_WAKERS {
524 IRQ_SUSPEND.store(false, Ordering::Relaxed); 519 w.wake()
525 return Poll::Ready(Event::Suspend); 520 }
521 for w in &EP_OUT_WAKERS {
522 w.wake()
526 } 523 }
527 524
528 Poll::Pending 525 return Poll::Ready(Event::Reset);
529 } else { 526 }
530 self.inited = true; 527
531 return Poll::Ready(Event::PowerDetected); 528 if IRQ_SUSPEND.load(Ordering::Acquire) {
529 IRQ_SUSPEND.store(false, Ordering::Relaxed);
530 return Poll::Ready(Event::Suspend);
532 } 531 }
532
533 Poll::Pending
533 }) 534 })
534 .await 535 .await
535 } 536 }
@@ -550,7 +551,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
550 true => Stat::STALL, 551 true => Stat::STALL,
551 }; 552 };
552 let mut w = invariant(r); 553 let mut w = invariant(r);
553 w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); 554 w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));
554 reg.write_value(w); 555 reg.write_value(w);
555 } 556 }
556 } 557 }
@@ -569,7 +570,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
569 true => Stat::STALL, 570 true => Stat::STALL,
570 }; 571 };
571 let mut w = invariant(r); 572 let mut w = invariant(r);
572 w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); 573 w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));
573 reg.write_value(w); 574 reg.write_value(w);
574 } 575 }
575 } 576 }
@@ -605,7 +606,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
605 break; 606 break;
606 } 607 }
607 let mut w = invariant(r); 608 let mut w = invariant(r);
608 w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); 609 w.set_stat_tx(Stat::from_bits(r.stat_tx().to_bits() ^ want_stat.to_bits()));
609 reg.write_value(w); 610 reg.write_value(w);
610 } 611 }
611 EP_IN_WAKERS[ep_addr.index()].wake(); 612 EP_IN_WAKERS[ep_addr.index()].wake();
@@ -621,7 +622,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
621 break; 622 break;
622 } 623 }
623 let mut w = invariant(r); 624 let mut w = invariant(r);
624 w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); 625 w.set_stat_rx(Stat::from_bits(r.stat_rx().to_bits() ^ want_stat.to_bits()));
625 reg.write_value(w); 626 reg.write_value(w);
626 } 627 }
627 EP_OUT_WAKERS[ep_addr.index()].wake(); 628 EP_OUT_WAKERS[ep_addr.index()].wake();
@@ -762,8 +763,8 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
762 regs.epr(index).write(|w| { 763 regs.epr(index).write(|w| {
763 w.set_ep_type(convert_type(self.info.ep_type)); 764 w.set_ep_type(convert_type(self.info.ep_type));
764 w.set_ea(self.info.addr.index() as _); 765 w.set_ea(self.info.addr.index() as _);
765 w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); 766 w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
766 w.set_stat_tx(Stat(0)); 767 w.set_stat_tx(Stat::from_bits(0));
767 w.set_ctr_rx(true); // don't clear 768 w.set_ctr_rx(true); // don't clear
768 w.set_ctr_tx(true); // don't clear 769 w.set_ctr_tx(true); // don't clear
769 }); 770 });
@@ -804,8 +805,8 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
804 regs.epr(index).write(|w| { 805 regs.epr(index).write(|w| {
805 w.set_ep_type(convert_type(self.info.ep_type)); 806 w.set_ep_type(convert_type(self.info.ep_type));
806 w.set_ea(self.info.addr.index() as _); 807 w.set_ea(self.info.addr.index() as _);
807 w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); 808 w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
808 w.set_stat_rx(Stat(0)); 809 w.set_stat_rx(Stat::from_bits(0));
809 w.set_ctr_rx(true); // don't clear 810 w.set_ctr_rx(true); // don't clear
810 w.set_ctr_tx(true); // don't clear 811 w.set_ctr_tx(true); // don't clear
811 }); 812 });
@@ -868,19 +869,19 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
868 let mut stat_tx = 0; 869 let mut stat_tx = 0;
869 if first { 870 if first {
870 // change NAK -> VALID 871 // change NAK -> VALID
871 stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0; 872 stat_rx ^= Stat::NAK.to_bits() ^ Stat::VALID.to_bits();
872 stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0; 873 stat_tx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();
873 } 874 }
874 if last { 875 if last {
875 // change STALL -> VALID 876 // change STALL -> VALID
876 stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; 877 stat_tx ^= Stat::STALL.to_bits() ^ Stat::NAK.to_bits();
877 } 878 }
878 // Note: if this is the first AND last transfer, the above effectively 879 // Note: if this is the first AND last transfer, the above effectively
879 // changes stat_tx like NAK -> NAK, so noop. 880 // changes stat_tx like NAK -> NAK, so noop.
880 regs.epr(0).write(|w| { 881 regs.epr(0).write(|w| {
881 w.set_ep_type(EpType::CONTROL); 882 w.set_ep_type(EpType::CONTROL);
882 w.set_stat_rx(Stat(stat_rx)); 883 w.set_stat_rx(Stat::from_bits(stat_rx));
883 w.set_stat_tx(Stat(stat_tx)); 884 w.set_stat_tx(Stat::from_bits(stat_tx));
884 w.set_ctr_rx(true); // don't clear 885 w.set_ctr_rx(true); // don't clear
885 w.set_ctr_tx(true); // don't clear 886 w.set_ctr_tx(true); // don't clear
886 }); 887 });
@@ -907,11 +908,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
907 908
908 regs.epr(0).write(|w| { 909 regs.epr(0).write(|w| {
909 w.set_ep_type(EpType::CONTROL); 910 w.set_ep_type(EpType::CONTROL);
910 w.set_stat_rx(Stat(match last { 911 w.set_stat_rx(Stat::from_bits(match last {
911 // If last, set STAT_RX=STALL. 912 // If last, set STAT_RX=STALL.
912 true => Stat::NAK.0 ^ Stat::STALL.0, 913 true => Stat::NAK.to_bits() ^ Stat::STALL.to_bits(),
913 // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. 914 // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
914 false => Stat::NAK.0 ^ Stat::VALID.0, 915 false => Stat::NAK.to_bits() ^ Stat::VALID.to_bits(),
915 })); 916 }));
916 w.set_ctr_rx(true); // don't clear 917 w.set_ctr_rx(true); // don't clear
917 w.set_ctr_tx(true); // don't clear 918 w.set_ctr_tx(true); // don't clear
@@ -936,17 +937,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
936 let mut stat_rx = 0; 937 let mut stat_rx = 0;
937 if first { 938 if first {
938 // change NAK -> STALL 939 // change NAK -> STALL
939 stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; 940 stat_rx ^= Stat::NAK.to_bits() ^ Stat::STALL.to_bits();
940 } 941 }
941 if last { 942 if last {
942 // change STALL -> VALID 943 // change STALL -> VALID
943 stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; 944 stat_rx ^= Stat::STALL.to_bits() ^ Stat::VALID.to_bits();
944 } 945 }
945 // Note: if this is the first AND last transfer, the above effectively 946 // Note: if this is the first AND last transfer, the above effectively
946 // does a change of NAK -> VALID. 947 // does a change of NAK -> VALID.
947 regs.epr(0).write(|w| { 948 regs.epr(0).write(|w| {
948 w.set_ep_type(EpType::CONTROL); 949 w.set_ep_type(EpType::CONTROL);
949 w.set_stat_rx(Stat(stat_rx)); 950 w.set_stat_rx(Stat::from_bits(stat_rx));
950 w.set_ep_kind(last); // set OUT_STATUS if last. 951 w.set_ep_kind(last); // set OUT_STATUS if last.
951 w.set_ctr_rx(true); // don't clear 952 w.set_ctr_rx(true); // don't clear
952 w.set_ctr_tx(true); // don't clear 953 w.set_ctr_tx(true); // don't clear
@@ -976,7 +977,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
976 let regs = T::regs(); 977 let regs = T::regs();
977 regs.epr(0).write(|w| { 978 regs.epr(0).write(|w| {
978 w.set_ep_type(EpType::CONTROL); 979 w.set_ep_type(EpType::CONTROL);
979 w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); 980 w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits()));
980 w.set_ep_kind(last); // set OUT_STATUS if last. 981 w.set_ep_kind(last); // set OUT_STATUS if last.
981 w.set_ctr_rx(true); // don't clear 982 w.set_ctr_rx(true); // don't clear
982 w.set_ctr_tx(true); // don't clear 983 w.set_ctr_tx(true); // don't clear
@@ -997,8 +998,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
997 let epr = regs.epr(0).read(); 998 let epr = regs.epr(0).read();
998 regs.epr(0).write(|w| { 999 regs.epr(0).write(|w| {
999 w.set_ep_type(EpType::CONTROL); 1000 w.set_ep_type(EpType::CONTROL);
1000 w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); 1001 w.set_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));
1001 w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0)); 1002 w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::VALID.to_bits()));
1002 w.set_ctr_rx(true); // don't clear 1003 w.set_ctr_rx(true); // don't clear
1003 w.set_ctr_tx(true); // don't clear 1004 w.set_ctr_tx(true); // don't clear
1004 }); 1005 });
@@ -1028,8 +1029,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
1028 let epr = regs.epr(0).read(); 1029 let epr = regs.epr(0).read();
1029 regs.epr(0).write(|w| { 1030 regs.epr(0).write(|w| {
1030 w.set_ep_type(EpType::CONTROL); 1031 w.set_ep_type(EpType::CONTROL);
1031 w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); 1032 w.set_stat_rx(Stat::from_bits(epr.stat_rx().to_bits() ^ Stat::STALL.to_bits()));
1032 w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0)); 1033 w.set_stat_tx(Stat::from_bits(epr.stat_tx().to_bits() ^ Stat::STALL.to_bits()));
1033 w.set_ctr_rx(true); // don't clear 1034 w.set_ctr_rx(true); // don't clear
1034 w.set_ctr_tx(true); // don't clear 1035 w.set_ctr_tx(true); // don't clear
1035 }); 1036 });
diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs
index 8af5c7bd5..6783db28d 100644
--- a/embassy-stm32/src/usb_otg/usb.rs
+++ b/embassy-stm32/src/usb_otg/usb.rs
@@ -6,8 +6,8 @@ use atomic_polyfill::{AtomicBool, AtomicU16, Ordering};
6use embassy_hal_common::{into_ref, Peripheral}; 6use embassy_hal_common::{into_ref, Peripheral};
7use embassy_sync::waitqueue::AtomicWaker; 7use embassy_sync::waitqueue::AtomicWaker;
8use embassy_usb_driver::{ 8use embassy_usb_driver::{
9 self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, 9 self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo,
10 EndpointType, Event, Unsupported, 10 EndpointOut, EndpointType, Event, Unsupported,
11}; 11};
12use futures::future::poll_fn; 12use futures::future::poll_fn;
13 13
@@ -31,7 +31,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
31 let state = T::state(); 31 let state = T::state();
32 32
33 let ints = r.gintsts().read(); 33 let ints = r.gintsts().read();
34 if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() { 34 if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() {
35 // Mask interrupts and notify `Bus` to process them 35 // Mask interrupts and notify `Bus` to process them
36 r.gintmsk().write(|_| {}); 36 r.gintmsk().write(|_| {});
37 T::state().bus_waker.wake(); 37 T::state().bus_waker.wake();
@@ -97,7 +97,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
97 vals::Pktstsd::SETUP_DATA_DONE => { 97 vals::Pktstsd::SETUP_DATA_DONE => {
98 trace!("SETUP_DATA_DONE ep={}", ep_num); 98 trace!("SETUP_DATA_DONE ep={}", ep_num);
99 } 99 }
100 x => trace!("unknown PKTSTS: {}", x.0), 100 x => trace!("unknown PKTSTS: {}", x.to_bits()),
101 } 101 }
102 } 102 }
103 103
@@ -124,7 +124,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
124 } 124 }
125 125
126 state.ep_in_wakers[ep_num].wake(); 126 state.ep_in_wakers[ep_num].wake();
127 trace!("in ep={} irq val={:b}", ep_num, ep_ints.0); 127 trace!("in ep={} irq val={:08x}", ep_num, ep_ints.0);
128 } 128 }
129 129
130 ep_mask >>= 1; 130 ep_mask >>= 1;
@@ -144,7 +144,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
144 // // clear all 144 // // clear all
145 // r.doepint(ep_num).write_value(ep_ints); 145 // r.doepint(ep_num).write_value(ep_ints);
146 // state.ep_out_wakers[ep_num].wake(); 146 // state.ep_out_wakers[ep_num].wake();
147 // trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0); 147 // trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0);
148 // } 148 // }
149 149
150 // ep_mask >>= 1; 150 // ep_mask >>= 1;
@@ -256,7 +256,34 @@ struct EndpointData {
256 fifo_size_words: u16, 256 fifo_size_words: u16,
257} 257}
258 258
259#[non_exhaustive]
260#[derive(Clone, Copy, PartialEq, Eq, Debug)]
261pub struct Config {
262 /// Enable VBUS detection.
263 ///
264 /// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly.
265 /// This is done by checkihg whether there is 5V on the VBUS pin or not.
266 ///
267 /// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional.
268 /// (if there's no power in VBUS your device would be off anyway, so it's fine to always assume
269 /// there's power in VBUS, i.e. the USB cable is always plugged in.)
270 ///
271 /// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and
272 /// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true.
273 ///
274 /// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a
275 /// voltage divider. See ST application note AN4879 and the reference manual for more details.
276 pub vbus_detection: bool,
277}
278
279impl Default for Config {
280 fn default() -> Self {
281 Self { vbus_detection: true }
282 }
283}
284
259pub struct Driver<'d, T: Instance> { 285pub struct Driver<'d, T: Instance> {
286 config: Config,
260 phantom: PhantomData<&'d mut T>, 287 phantom: PhantomData<&'d mut T>,
261 ep_in: [Option<EndpointData>; MAX_EP_COUNT], 288 ep_in: [Option<EndpointData>; MAX_EP_COUNT],
262 ep_out: [Option<EndpointData>; MAX_EP_COUNT], 289 ep_out: [Option<EndpointData>; MAX_EP_COUNT],
@@ -279,6 +306,7 @@ impl<'d, T: Instance> Driver<'d, T> {
279 dp: impl Peripheral<P = impl DpPin<T>> + 'd, 306 dp: impl Peripheral<P = impl DpPin<T>> + 'd,
280 dm: impl Peripheral<P = impl DmPin<T>> + 'd, 307 dm: impl Peripheral<P = impl DmPin<T>> + 'd,
281 ep_out_buffer: &'d mut [u8], 308 ep_out_buffer: &'d mut [u8],
309 config: Config,
282 ) -> Self { 310 ) -> Self {
283 into_ref!(dp, dm); 311 into_ref!(dp, dm);
284 312
@@ -286,6 +314,7 @@ impl<'d, T: Instance> Driver<'d, T> {
286 dm.set_as_af(dm.af_num(), AFType::OutputPushPull); 314 dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
287 315
288 Self { 316 Self {
317 config,
289 phantom: PhantomData, 318 phantom: PhantomData,
290 ep_in: [None; MAX_EP_COUNT], 319 ep_in: [None; MAX_EP_COUNT],
291 ep_out: [None; MAX_EP_COUNT], 320 ep_out: [None; MAX_EP_COUNT],
@@ -318,6 +347,7 @@ impl<'d, T: Instance> Driver<'d, T> {
318 ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd, 347 ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
319 ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd, 348 ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
320 ep_out_buffer: &'d mut [u8], 349 ep_out_buffer: &'d mut [u8],
350 config: Config,
321 ) -> Self { 351 ) -> Self {
322 assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB"); 352 assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB");
323 353
@@ -327,6 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> {
327 ); 357 );
328 358
329 Self { 359 Self {
360 config,
330 phantom: PhantomData, 361 phantom: PhantomData,
331 ep_in: [None; MAX_EP_COUNT], 362 ep_in: [None; MAX_EP_COUNT],
332 ep_out: [None; MAX_EP_COUNT], 363 ep_out: [None; MAX_EP_COUNT],
@@ -464,11 +495,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
464 495
465 ( 496 (
466 Bus { 497 Bus {
498 config: self.config,
467 phantom: PhantomData, 499 phantom: PhantomData,
468 ep_in: self.ep_in, 500 ep_in: self.ep_in,
469 ep_out: self.ep_out, 501 ep_out: self.ep_out,
470 phy_type: self.phy_type, 502 phy_type: self.phy_type,
471 enabled: false, 503 inited: false,
472 }, 504 },
473 ControlPipe { 505 ControlPipe {
474 _phantom: PhantomData, 506 _phantom: PhantomData,
@@ -481,11 +513,12 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
481} 513}
482 514
483pub struct Bus<'d, T: Instance> { 515pub struct Bus<'d, T: Instance> {
516 config: Config,
484 phantom: PhantomData<&'d mut T>, 517 phantom: PhantomData<&'d mut T>,
485 ep_in: [Option<EndpointData>; MAX_EP_COUNT], 518 ep_in: [Option<EndpointData>; MAX_EP_COUNT],
486 ep_out: [Option<EndpointData>; MAX_EP_COUNT], 519 ep_out: [Option<EndpointData>; MAX_EP_COUNT],
487 phy_type: PhyType, 520 phy_type: PhyType,
488 enabled: bool, 521 inited: bool,
489} 522}
490 523
491impl<'d, T: Instance> Bus<'d, T> { 524impl<'d, T: Instance> Bus<'d, T> {
@@ -498,11 +531,202 @@ impl<'d, T: Instance> Bus<'d, T> {
498 w.set_iepint(true); 531 w.set_iepint(true);
499 w.set_oepint(true); 532 w.set_oepint(true);
500 w.set_rxflvlm(true); 533 w.set_rxflvlm(true);
534 w.set_srqim(true);
535 w.set_otgint(true);
501 }); 536 });
502 } 537 }
503} 538}
504 539
505impl<'d, T: Instance> Bus<'d, T> { 540impl<'d, T: Instance> Bus<'d, T> {
541 fn init(&mut self) {
542 #[cfg(stm32l4)]
543 {
544 crate::peripherals::PWR::enable();
545 critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
546 }
547
548 #[cfg(stm32f7)]
549 {
550 // Enable ULPI clock if external PHY is used
551 let ulpien = !self.phy_type.internal();
552 critical_section::with(|_| {
553 crate::pac::RCC.ahb1enr().modify(|w| {
554 if T::HIGH_SPEED {
555 w.set_usb_otg_hsulpien(ulpien);
556 } else {
557 w.set_usb_otg_hsen(ulpien);
558 }
559 });
560
561 // Low power mode
562 crate::pac::RCC.ahb1lpenr().modify(|w| {
563 if T::HIGH_SPEED {
564 w.set_usb_otg_hsulpilpen(ulpien);
565 } else {
566 w.set_usb_otg_hslpen(ulpien);
567 }
568 });
569 });
570 }
571
572 #[cfg(stm32h7)]
573 {
574 // If true, VDD33USB is generated by internal regulator from VDD50USB
575 // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
576 // TODO: unhardcode
577 let internal_regulator = false;
578
579 // Enable USB power
580 critical_section::with(|_| {
581 crate::pac::PWR.cr3().modify(|w| {
582 w.set_usb33den(true);
583 w.set_usbregen(internal_regulator);
584 })
585 });
586
587 // Wait for USB power to stabilize
588 while !crate::pac::PWR.cr3().read().usb33rdy() {}
589
590 // Use internal 48MHz HSI clock. Should be enabled in RCC by default.
591 critical_section::with(|_| {
592 crate::pac::RCC
593 .d2ccip2r()
594 .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48))
595 });
596
597 // Enable ULPI clock if external PHY is used
598 let ulpien = !self.phy_type.internal();
599 critical_section::with(|_| {
600 crate::pac::RCC.ahb1enr().modify(|w| {
601 if T::HIGH_SPEED {
602 w.set_usb_otg_hs_ulpien(ulpien);
603 } else {
604 w.set_usb_otg_fs_ulpien(ulpien);
605 }
606 });
607 crate::pac::RCC.ahb1lpenr().modify(|w| {
608 if T::HIGH_SPEED {
609 w.set_usb_otg_hs_ulpilpen(ulpien);
610 } else {
611 w.set_usb_otg_fs_ulpilpen(ulpien);
612 }
613 });
614 });
615 }
616
617 #[cfg(stm32u5)]
618 {
619 // Enable USB power
620 critical_section::with(|_| {
621 crate::pac::RCC.ahb3enr().modify(|w| {
622 w.set_pwren(true);
623 });
624 cortex_m::asm::delay(2);
625
626 crate::pac::PWR.svmcr().modify(|w| {
627 w.set_usv(true);
628 w.set_uvmen(true);
629 });
630 });
631
632 // Wait for USB power to stabilize
633 while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
634
635 // Select HSI48 as USB clock source.
636 critical_section::with(|_| {
637 crate::pac::RCC.ccipr1().modify(|w| {
638 w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48);
639 })
640 });
641 }
642
643 <T as RccPeripheral>::enable();
644 <T as RccPeripheral>::reset();
645
646 T::Interrupt::unpend();
647 unsafe { T::Interrupt::enable() };
648
649 let r = T::regs();
650 let core_id = r.cid().read().0;
651 info!("Core id {:08x}", core_id);
652
653 // Wait for AHB ready.
654 while !r.grstctl().read().ahbidl() {}
655
656 // Configure as device.
657 r.gusbcfg().write(|w| {
658 // Force device mode
659 w.set_fdmod(true);
660 // Enable internal full-speed PHY
661 w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed());
662 });
663
664 // Configuring Vbus sense and SOF output
665 match core_id {
666 0x0000_1200 | 0x0000_1100 => {
667 assert!(self.phy_type != PhyType::InternalHighSpeed);
668
669 r.gccfg_v1().modify(|w| {
670 // Enable internal full-speed PHY, logic is inverted
671 w.set_pwrdwn(self.phy_type.internal());
672 });
673
674 // F429-like chips have the GCCFG.NOVBUSSENS bit
675 r.gccfg_v1().modify(|w| {
676 w.set_novbussens(!self.config.vbus_detection);
677 w.set_vbusasen(false);
678 w.set_vbusbsen(self.config.vbus_detection);
679 w.set_sofouten(false);
680 });
681 }
682 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => {
683 // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning
684 r.gccfg_v2().modify(|w| {
685 // Enable internal full-speed PHY, logic is inverted
686 w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed());
687 w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed());
688 });
689
690 r.gccfg_v2().modify(|w| {
691 w.set_vbden(self.config.vbus_detection);
692 });
693
694 // Force B-peripheral session
695 r.gotgctl().modify(|w| {
696 w.set_bvaloen(!self.config.vbus_detection);
697 w.set_bvaloval(true);
698 });
699 }
700 _ => unimplemented!("Unknown USB core id {:X}", core_id),
701 }
702
703 // Soft disconnect.
704 r.dctl().write(|w| w.set_sdis(true));
705
706 // Set speed.
707 r.dcfg().write(|w| {
708 w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80);
709 w.set_dspd(self.phy_type.to_dspd());
710 });
711
712 // Unmask transfer complete EP interrupt
713 r.diepmsk().write(|w| {
714 w.set_xfrcm(true);
715 });
716
717 // Unmask and clear core interrupts
718 Bus::<T>::restore_irqs();
719 r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF));
720
721 // Unmask global interrupt
722 r.gahbcfg().write(|w| {
723 w.set_gint(true); // unmask global interrupt
724 });
725
726 // Connect
727 r.dctl().write(|w| w.set_sdis(false));
728 }
729
506 fn init_fifo(&mut self) { 730 fn init_fifo(&mut self) {
507 trace!("init_fifo"); 731 trace!("init_fifo");
508 732
@@ -540,6 +764,19 @@ impl<'d, T: Instance> Bus<'d, T> {
540 fifo_top <= T::FIFO_DEPTH_WORDS, 764 fifo_top <= T::FIFO_DEPTH_WORDS,
541 "FIFO allocations exceeded maximum capacity" 765 "FIFO allocations exceeded maximum capacity"
542 ); 766 );
767
768 // Flush fifos
769 r.grstctl().write(|w| {
770 w.set_rxfflsh(true);
771 w.set_txfflsh(true);
772 w.set_txfnum(0x10);
773 });
774 loop {
775 let x = r.grstctl().read();
776 if !x.rxfflsh() && !x.txfflsh() {
777 break;
778 }
779 }
543 } 780 }
544 781
545 fn configure_endpoints(&mut self) { 782 fn configure_endpoints(&mut self) {
@@ -558,6 +795,8 @@ impl<'d, T: Instance> Bus<'d, T> {
558 w.set_mpsiz(ep.max_packet_size); 795 w.set_mpsiz(ep.max_packet_size);
559 w.set_eptyp(to_eptyp(ep.ep_type)); 796 w.set_eptyp(to_eptyp(ep.ep_type));
560 w.set_sd0pid_sevnfrm(true); 797 w.set_sd0pid_sevnfrm(true);
798 w.set_txfnum(index as _);
799 w.set_snak(true);
561 } 800 }
562 }); 801 });
563 }); 802 });
@@ -598,6 +837,13 @@ impl<'d, T: Instance> Bus<'d, T> {
598 }); 837 });
599 } 838 }
600 839
840 fn disable_all_endpoints(&mut self) {
841 for i in 0..T::ENDPOINT_COUNT {
842 self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false);
843 self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false);
844 }
845 }
846
601 fn disable(&mut self) { 847 fn disable(&mut self) {
602 T::Interrupt::disable(); 848 T::Interrupt::disable();
603 849
@@ -612,9 +858,14 @@ impl<'d, T: Instance> Bus<'d, T> {
612impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> { 858impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
613 async fn poll(&mut self) -> Event { 859 async fn poll(&mut self) -> Event {
614 poll_fn(move |cx| { 860 poll_fn(move |cx| {
615 // TODO: implement VBUS detection 861 if !self.inited {
616 if !self.enabled { 862 self.init();
617 return Poll::Ready(Event::PowerDetected); 863 self.inited = true;
864
865 // If no vbus detection, just return a single PowerDetected event at startup.
866 if !self.config.vbus_detection {
867 return Poll::Ready(Event::PowerDetected);
868 }
618 } 869 }
619 870
620 let r = T::regs(); 871 let r = T::regs();
@@ -622,6 +873,32 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
622 T::state().bus_waker.register(cx.waker()); 873 T::state().bus_waker.register(cx.waker());
623 874
624 let ints = r.gintsts().read(); 875 let ints = r.gintsts().read();
876
877 if ints.srqint() {
878 trace!("vbus detected");
879
880 r.gintsts().write(|w| w.set_srqint(true)); // clear
881 Self::restore_irqs();
882
883 if self.config.vbus_detection {
884 return Poll::Ready(Event::PowerDetected);
885 }
886 }
887
888 if ints.otgint() {
889 let otgints = r.gotgint().read();
890 r.gotgint().write_value(otgints); // clear all
891 Self::restore_irqs();
892
893 if otgints.sedet() {
894 trace!("vbus removed");
895 if self.config.vbus_detection {
896 self.disable_all_endpoints();
897 return Poll::Ready(Event::PowerRemoved);
898 }
899 }
900 }
901
625 if ints.usbrst() { 902 if ints.usbrst() {
626 trace!("reset"); 903 trace!("reset");
627 904
@@ -643,7 +920,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
643 trace!("enumdne"); 920 trace!("enumdne");
644 921
645 let speed = r.dsts().read().enumspd(); 922 let speed = r.dsts().read().enumspd();
646 trace!(" speed={}", speed.0); 923 trace!(" speed={}", speed.to_bits());
647 924
648 r.gusbcfg().modify(|w| { 925 r.gusbcfg().modify(|w| {
649 w.set_trdt(calculate_trdt(speed, T::frequency())); 926 w.set_trdt(calculate_trdt(speed, T::frequency()));
@@ -744,7 +1021,19 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
744 1021
745 r.doepctl(ep_addr.index()).modify(|w| { 1022 r.doepctl(ep_addr.index()).modify(|w| {
746 w.set_usbaep(enabled); 1023 w.set_usbaep(enabled);
747 }) 1024 });
1025
1026 // Flush tx fifo
1027 r.grstctl().write(|w| {
1028 w.set_txfflsh(true);
1029 w.set_txfnum(ep_addr.index() as _);
1030 });
1031 loop {
1032 let x = r.grstctl().read();
1033 if !x.txfflsh() {
1034 break;
1035 }
1036 }
748 }); 1037 });
749 1038
750 // Wake `Endpoint::wait_enabled()` 1039 // Wake `Endpoint::wait_enabled()`
@@ -755,13 +1044,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
755 // cancel transfer if active 1044 // cancel transfer if active
756 if !enabled && r.diepctl(ep_addr.index()).read().epena() { 1045 if !enabled && r.diepctl(ep_addr.index()).read().epena() {
757 r.diepctl(ep_addr.index()).modify(|w| { 1046 r.diepctl(ep_addr.index()).modify(|w| {
758 w.set_snak(true); 1047 w.set_snak(true); // set NAK
759 w.set_epdis(true); 1048 w.set_epdis(true);
760 }) 1049 })
761 } 1050 }
762 1051
763 r.diepctl(ep_addr.index()).modify(|w| { 1052 r.diepctl(ep_addr.index()).modify(|w| {
764 w.set_usbaep(enabled); 1053 w.set_usbaep(enabled);
1054 w.set_cnak(enabled); // clear NAK that might've been set by SNAK above.
765 }) 1055 })
766 }); 1056 });
767 1057
@@ -773,203 +1063,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
773 1063
774 async fn enable(&mut self) { 1064 async fn enable(&mut self) {
775 trace!("enable"); 1065 trace!("enable");
776 1066 // TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb
777 #[cfg(stm32l4)]
778 {
779 crate::peripherals::PWR::enable();
780 critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
781 }
782
783 #[cfg(stm32f7)]
784 {
785 // Enable ULPI clock if external PHY is used
786 let ulpien = !self.phy_type.internal();
787 critical_section::with(|_| {
788 crate::pac::RCC.ahb1enr().modify(|w| {
789 if T::HIGH_SPEED {
790 w.set_usb_otg_hsulpien(ulpien);
791 } else {
792 w.set_usb_otg_hsen(ulpien);
793 }
794 });
795
796 // Low power mode
797 crate::pac::RCC.ahb1lpenr().modify(|w| {
798 if T::HIGH_SPEED {
799 w.set_usb_otg_hsulpilpen(ulpien);
800 } else {
801 w.set_usb_otg_hslpen(ulpien);
802 }
803 });
804 });
805 }
806
807 #[cfg(stm32h7)]
808 {
809 // If true, VDD33USB is generated by internal regulator from VDD50USB
810 // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
811 // TODO: unhardcode
812 let internal_regulator = false;
813
814 // Enable USB power
815 critical_section::with(|_| {
816 crate::pac::PWR.cr3().modify(|w| {
817 w.set_usb33den(true);
818 w.set_usbregen(internal_regulator);
819 })
820 });
821
822 // Wait for USB power to stabilize
823 while !crate::pac::PWR.cr3().read().usb33rdy() {}
824
825 // Use internal 48MHz HSI clock. Should be enabled in RCC by default.
826 critical_section::with(|_| {
827 crate::pac::RCC
828 .d2ccip2r()
829 .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48))
830 });
831
832 // Enable ULPI clock if external PHY is used
833 let ulpien = !self.phy_type.internal();
834 critical_section::with(|_| {
835 crate::pac::RCC.ahb1enr().modify(|w| {
836 if T::HIGH_SPEED {
837 w.set_usb_otg_hs_ulpien(ulpien);
838 } else {
839 w.set_usb_otg_fs_ulpien(ulpien);
840 }
841 });
842 crate::pac::RCC.ahb1lpenr().modify(|w| {
843 if T::HIGH_SPEED {
844 w.set_usb_otg_hs_ulpilpen(ulpien);
845 } else {
846 w.set_usb_otg_fs_ulpilpen(ulpien);
847 }
848 });
849 });
850 }
851
852 #[cfg(stm32u5)]
853 {
854 // Enable USB power
855 critical_section::with(|_| {
856 crate::pac::RCC.ahb3enr().modify(|w| {
857 w.set_pwren(true);
858 });
859 cortex_m::asm::delay(2);
860
861 crate::pac::PWR.svmcr().modify(|w| {
862 w.set_usv(true);
863 w.set_uvmen(true);
864 });
865 });
866
867 // Wait for USB power to stabilize
868 while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
869
870 // Select HSI48 as USB clock source.
871 critical_section::with(|_| {
872 crate::pac::RCC.ccipr1().modify(|w| {
873 w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48);
874 })
875 });
876 }
877
878 <T as RccPeripheral>::enable();
879 <T as RccPeripheral>::reset();
880
881 T::Interrupt::unpend();
882 unsafe { T::Interrupt::enable() };
883
884 let r = T::regs();
885 let core_id = r.cid().read().0;
886 info!("Core id {:08x}", core_id);
887
888 // Wait for AHB ready.
889 while !r.grstctl().read().ahbidl() {}
890
891 // Configure as device.
892 r.gusbcfg().write(|w| {
893 // Force device mode
894 w.set_fdmod(true);
895 // Enable internal full-speed PHY
896 w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed());
897 });
898
899 // Configuring Vbus sense and SOF output
900 match core_id {
901 0x0000_1200 | 0x0000_1100 => {
902 assert!(self.phy_type != PhyType::InternalHighSpeed);
903
904 r.gccfg_v1().modify(|w| {
905 // Enable internal full-speed PHY, logic is inverted
906 w.set_pwrdwn(self.phy_type.internal());
907 });
908
909 // F429-like chips have the GCCFG.NOVBUSSENS bit
910 r.gccfg_v1().modify(|w| {
911 w.set_novbussens(true);
912 w.set_vbusasen(false);
913 w.set_vbusbsen(false);
914 w.set_sofouten(false);
915 });
916 }
917 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => {
918 // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning
919 r.gccfg_v2().modify(|w| {
920 // Enable internal full-speed PHY, logic is inverted
921 w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed());
922 w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed());
923 });
924
925 r.gccfg_v2().modify(|w| {
926 w.set_vbden(false);
927 });
928
929 // Force B-peripheral session
930 r.gotgctl().modify(|w| {
931 w.set_bvaloen(true);
932 w.set_bvaloval(true);
933 });
934 }
935 _ => unimplemented!("Unknown USB core id {:X}", core_id),
936 }
937
938 // Soft disconnect.
939 r.dctl().write(|w| w.set_sdis(true));
940
941 // Set speed.
942 r.dcfg().write(|w| {
943 w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80);
944 w.set_dspd(self.phy_type.to_dspd());
945 });
946
947 // Unmask transfer complete EP interrupt
948 r.diepmsk().write(|w| {
949 w.set_xfrcm(true);
950 });
951
952 // Unmask and clear core interrupts
953 Bus::<T>::restore_irqs();
954 r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF));
955
956 // Unmask global interrupt
957 r.gahbcfg().write(|w| {
958 w.set_gint(true); // unmask global interrupt
959 });
960
961 // Connect
962 r.dctl().write(|w| w.set_sdis(false));
963
964 self.enabled = true;
965 } 1067 }
966 1068
967 async fn disable(&mut self) { 1069 async fn disable(&mut self) {
968 trace!("disable"); 1070 trace!("disable");
969 1071
970 Bus::disable(self); 1072 // TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb
971 1073 //Bus::disable(self);
972 self.enabled = false;
973 } 1074 }
974 1075
975 async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { 1076 async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
@@ -1112,11 +1213,16 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
1112 state.ep_in_wakers[index].register(cx.waker()); 1213 state.ep_in_wakers[index].register(cx.waker());
1113 1214
1114 let diepctl = r.diepctl(index).read(); 1215 let diepctl = r.diepctl(index).read();
1216 let dtxfsts = r.dtxfsts(index).read();
1217 info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0);
1115 if !diepctl.usbaep() { 1218 if !diepctl.usbaep() {
1219 trace!("write ep={:?} wait for prev: error disabled", self.info.addr);
1116 Poll::Ready(Err(EndpointError::Disabled)) 1220 Poll::Ready(Err(EndpointError::Disabled))
1117 } else if !diepctl.epena() { 1221 } else if !diepctl.epena() {
1222 trace!("write ep={:?} wait for prev: ready", self.info.addr);
1118 Poll::Ready(Ok(())) 1223 Poll::Ready(Ok(()))
1119 } else { 1224 } else {
1225 trace!("write ep={:?} wait for prev: pending", self.info.addr);
1120 Poll::Pending 1226 Poll::Pending
1121 } 1227 }
1122 }) 1228 })
@@ -1141,6 +1247,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
1141 1247
1142 Poll::Pending 1248 Poll::Pending
1143 } else { 1249 } else {
1250 trace!("write ep={:?} wait for fifo: ready", self.info.addr);
1144 Poll::Ready(()) 1251 Poll::Ready(())
1145 } 1252 }
1146 }) 1253 })
diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs
index 5907a4e54..b03e81d6e 100644
--- a/embassy-stm32/src/wdg/mod.rs
+++ b/embassy-stm32/src/wdg/mod.rs
@@ -49,7 +49,7 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
49 49
50 let wdg = T::regs(); 50 let wdg = T::regs();
51 wdg.kr().write(|w| w.set_key(Key::ENABLE)); 51 wdg.kr().write(|w| w.set_key(Key::ENABLE));
52 wdg.pr().write(|w| w.set_pr(Pr(pr))); 52 wdg.pr().write(|w| w.set_pr(Pr::from_bits(pr)));
53 wdg.rlr().write(|w| w.set_rl(rl)); 53 wdg.rlr().write(|w| w.set_rl(rl));
54 54
55 trace!( 55 trace!(
diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs
index db6ebb08b..13bf4ef01 100644
--- a/embassy-sync/src/pipe.rs
+++ b/embassy-sync/src/pipe.rs
@@ -282,7 +282,7 @@ where
282 /// returns the amount of bytes written. 282 /// returns the amount of bytes written.
283 /// 283 ///
284 /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full, 284 /// If it is not possible to write a nonzero amount of bytes because the pipe's buffer is full,
285 /// this method will wait until it is. See [`try_write`](Self::try_write) for a variant that 285 /// this method will wait until it isn't. See [`try_write`](Self::try_write) for a variant that
286 /// returns an error instead of waiting. 286 /// returns an error instead of waiting.
287 /// 287 ///
288 /// It is not guaranteed that all bytes in the buffer are written, even if there's enough 288 /// It is not guaranteed that all bytes in the buffer are written, even if there's enough
@@ -319,7 +319,7 @@ where
319 /// returns the amount of bytes read. 319 /// returns the amount of bytes read.
320 /// 320 ///
321 /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty, 321 /// If it is not possible to read a nonzero amount of bytes because the pipe's buffer is empty,
322 /// this method will wait until it is. See [`try_read`](Self::try_read) for a variant that 322 /// this method will wait until it isn't. See [`try_read`](Self::try_read) for a variant that
323 /// returns an error instead of waiting. 323 /// returns an error instead of waiting.
324 /// 324 ///
325 /// It is not guaranteed that all bytes in the buffer are read, even if there's enough 325 /// It is not guaranteed that all bytes in the buffer are read, even if there's enough
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index bc79b3671..670709021 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -1,4 +1,5 @@
1//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class. 1//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class.
2
2use embassy_futures::select::{select, Either}; 3use embassy_futures::select::{select, Either};
3use embassy_net_driver_channel as ch; 4use embassy_net_driver_channel as ch;
4use embassy_net_driver_channel::driver::LinkState; 5use embassy_net_driver_channel::driver::LinkState;
@@ -79,7 +80,7 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
79pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; 80pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
80 81
81impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 82impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
82 /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net). 83 /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](https://crates.io/crates/embassy-net).
83 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( 84 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
84 self, 85 self,
85 state: &'d mut State<MTU, N_RX, N_TX>, 86 state: &'d mut State<MTU, N_RX, N_TX>,
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index d5556dd0b..fcfa0bfcd 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -11,8 +11,8 @@
11//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12. 11//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
12//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), 12//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
13//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. 13//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
14//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 14//! This is due to regex spaghetti: <https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417>
15//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 15//! and this nonsense in the linux kernel: <https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757>
16 16
17use core::intrinsics::copy_nonoverlapping; 17use core::intrinsics::copy_nonoverlapping;
18use core::mem::{size_of, MaybeUninit}; 18use core::mem::{size_of, MaybeUninit};
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index d8563d59a..1180b9b66 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -23,7 +23,7 @@ mod config {
23use embassy_futures::select::{select, Either}; 23use embassy_futures::select::{select, Either};
24use heapless::Vec; 24use heapless::Vec;
25 25
26pub use crate::builder::{Builder, Config}; 26pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder};
27use crate::config::*; 27use crate::config::*;
28use crate::control::*; 28use crate::control::*;
29use crate::descriptor::*; 29use crate::descriptor::*;
diff --git a/examples/boot/application/nrf/.cargo/config.toml b/examples/boot/application/nrf/.cargo/config.toml
index 3872e7189..17616a054 100644
--- a/examples/boot/application/nrf/.cargo/config.toml
+++ b/examples/boot/application/nrf/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` 2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip nRF52840_xxAA" 3runner = "probe-rs run --chip nRF52840_xxAA"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
diff --git a/examples/boot/application/rp/.cargo/config.toml b/examples/boot/application/rp/.cargo/config.toml
index 278c24a57..cd8d1ef02 100644
--- a/examples/boot/application/rp/.cargo/config.toml
+++ b/examples/boot/application/rp/.cargo/config.toml
@@ -3,7 +3,7 @@ build-std = ["core"]
3build-std-features = ["panic_immediate_abort"] 3build-std-features = ["panic_immediate_abort"]
4 4
5[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 5[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6runner = "probe-rs-cli run --chip RP2040" 6runner = "probe-rs run --chip RP2040"
7 7
8[build] 8[build]
9target = "thumbv6m-none-eabi" 9target = "thumbv6m-none-eabi"
diff --git a/examples/boot/application/stm32f3/.cargo/config.toml b/examples/boot/application/stm32f3/.cargo/config.toml
index 9fc2396e8..4a7ec0a5b 100644
--- a/examples/boot/application/stm32f3/.cargo/config.toml
+++ b/examples/boot/application/stm32f3/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F303VCTx" 3runner = "probe-rs run --chip STM32F303VCTx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/application/stm32f7/.cargo/config.toml b/examples/boot/application/stm32f7/.cargo/config.toml
index 7d6c88a99..9088eea6e 100644
--- a/examples/boot/application/stm32f7/.cargo/config.toml
+++ b/examples/boot/application/stm32f7/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F767ZITx" 3runner = "probe-rs run --chip STM32F767ZITx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/application/stm32h7/.cargo/config.toml b/examples/boot/application/stm32h7/.cargo/config.toml
index 067a5c89b..caa0d3a93 100644
--- a/examples/boot/application/stm32h7/.cargo/config.toml
+++ b/examples/boot/application/stm32h7/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32H743ZITx" 3runner = "probe-rs run --chip STM32H743ZITx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/application/stm32h7/flash-boot.sh b/examples/boot/application/stm32h7/flash-boot.sh
index a3003681a..4912a50b7 100755
--- a/examples/boot/application/stm32h7/flash-boot.sh
+++ b/examples/boot/application/stm32h7/flash-boot.sh
@@ -1,5 +1,5 @@
1#!/bin/bash 1#!/bin/bash
2probe-rs-cli erase --chip STM32H743ZITx 2probe-rs erase --chip STM32H743ZITx
3mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x 3mv ../../bootloader/stm32/memory.x ../../bootloader/stm32/memory-old.x
4cp memory-bl.x ../../bootloader/stm32/memory.x 4cp memory-bl.x ../../bootloader/stm32/memory.x
5 5
diff --git a/examples/boot/application/stm32l0/.cargo/config.toml b/examples/boot/application/stm32l0/.cargo/config.toml
index ce0e460bd..6099f015c 100644
--- a/examples/boot/application/stm32l0/.cargo/config.toml
+++ b/examples/boot/application/stm32l0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32L072CZTx" 3runner = "probe-rs run --chip STM32L072CZTx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/boot/application/stm32l1/.cargo/config.toml b/examples/boot/application/stm32l1/.cargo/config.toml
index 1401500a0..9cabd14ba 100644
--- a/examples/boot/application/stm32l1/.cargo/config.toml
+++ b/examples/boot/application/stm32l1/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32L151CBxxA" 3runner = "probe-rs run --chip STM32L151CBxxA"
4 4
5[build] 5[build]
6target = "thumbv7m-none-eabi" 6target = "thumbv7m-none-eabi"
diff --git a/examples/boot/application/stm32l4/.cargo/config.toml b/examples/boot/application/stm32l4/.cargo/config.toml
index 48ff3734b..c803215f6 100644
--- a/examples/boot/application/stm32l4/.cargo/config.toml
+++ b/examples/boot/application/stm32l4/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32L475VG" 3runner = "probe-rs run --chip STM32L475VG"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/application/stm32wl/.cargo/config.toml b/examples/boot/application/stm32wl/.cargo/config.toml
index b49b582e0..4f8094ff2 100644
--- a/examples/boot/application/stm32wl/.cargo/config.toml
+++ b/examples/boot/application/stm32wl/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32WLE5JCIx" 3runner = "probe-rs run --chip STM32WLE5JCIx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/boot/bootloader/nrf/.cargo/config.toml b/examples/boot/bootloader/nrf/.cargo/config.toml
index d636b1d23..c292846aa 100644
--- a/examples/boot/bootloader/nrf/.cargo/config.toml
+++ b/examples/boot/bootloader/nrf/.cargo/config.toml
@@ -4,7 +4,7 @@ build-std-features = ["panic_immediate_abort"]
4 4
5[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 5[target.'cfg(all(target_arch = "arm", target_os = "none"))']
6#runner = "./fruitrunner" 6#runner = "./fruitrunner"
7runner = "probe-rs-cli run --chip nrf52840_xxAA" 7runner = "probe-rs run --chip nrf52840_xxAA"
8 8
9rustflags = [ 9rustflags = [
10 # Code-size optimizations. 10 # Code-size optimizations.
diff --git a/examples/boot/bootloader/rp/.cargo/config.toml b/examples/boot/bootloader/rp/.cargo/config.toml
index 795ee043a..9d48ecdc9 100644
--- a/examples/boot/bootloader/rp/.cargo/config.toml
+++ b/examples/boot/bootloader/rp/.cargo/config.toml
@@ -1,5 +1,5 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2runner = "probe-rs-cli run --chip RP2040" 2runner = "probe-rs run --chip RP2040"
3 3
4[build] 4[build]
5target = "thumbv6m-none-eabi" 5target = "thumbv6m-none-eabi"
diff --git a/examples/nrf-rtos-trace/.cargo/config.toml b/examples/nrf-rtos-trace/.cargo/config.toml
index 3872e7189..17616a054 100644
--- a/examples/nrf-rtos-trace/.cargo/config.toml
+++ b/examples/nrf-rtos-trace/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` 2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip nRF52840_xxAA" 3runner = "probe-rs run --chip nRF52840_xxAA"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
diff --git a/examples/nrf52840-rtic/.cargo/config.toml b/examples/nrf52840-rtic/.cargo/config.toml
index 3872e7189..17616a054 100644
--- a/examples/nrf52840-rtic/.cargo/config.toml
+++ b/examples/nrf52840-rtic/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` 2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip nRF52840_xxAA" 3runner = "probe-rs run --chip nRF52840_xxAA"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
diff --git a/examples/nrf52840/.cargo/config.toml b/examples/nrf52840/.cargo/config.toml
index 3872e7189..17616a054 100644
--- a/examples/nrf52840/.cargo/config.toml
+++ b/examples/nrf52840/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF82840_xxAA with your chip as listed in `probe-rs-cli chip list` 2# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip nRF52840_xxAA" 3runner = "probe-rs run --chip nRF52840_xxAA"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 6627b7861..8c4175966 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -6,8 +6,24 @@ license = "MIT OR Apache-2.0"
6 6
7[features] 7[features]
8default = ["nightly"] 8default = ["nightly"]
9nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-time/nightly", "embassy-time/unstable-traits", "static_cell/nightly", 9nightly = [
10 "embassy-usb", "embedded-io/async", "embassy-net", "embassy-lora", "lora-phy", "lorawan-device", "lorawan"] 10 "embedded-hal-async",
11 "embassy-executor/nightly",
12 "embassy-nrf/nightly",
13 "embassy-net/nightly",
14 "embassy-net-esp-hosted",
15 "embassy-nrf/unstable-traits",
16 "embassy-time/nightly",
17 "embassy-time/unstable-traits",
18 "static_cell/nightly",
19 "embassy-usb",
20 "embedded-io/async",
21 "embassy-net",
22 "embassy-lora",
23 "lora-phy",
24 "lorawan-device",
25 "lorawan",
26]
11 27
12[dependencies] 28[dependencies]
13embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 29embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -22,6 +38,7 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti
22lora-phy = { version = "1", optional = true } 38lora-phy = { version = "1", optional = true }
23lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true } 39lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"], optional = true }
24lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true } 40lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"], optional = true }
41embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"], optional = true }
25 42
26defmt = "0.3" 43defmt = "0.3"
27defmt-rtt = "0.4" 44defmt-rtt = "0.4"
@@ -35,3 +52,4 @@ rand = { version = "0.8.4", default-features = false }
35embedded-storage = "0.3.0" 52embedded-storage = "0.3.0"
36usbd-hid = "0.6.0" 53usbd-hid = "0.6.0"
37serde = { version = "1.0.136", default-features = false } 54serde = { version = "1.0.136", default-features = false }
55embedded-hal-async = { version = "0.2.0-alpha.1", optional = true }
diff --git a/examples/nrf52840/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs
index 33a44516d..31c6fe4b6 100644
--- a/examples/nrf52840/src/bin/nvmc.rs
+++ b/examples/nrf52840/src/bin/nvmc.rs
@@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) {
14 let p = embassy_nrf::init(Default::default()); 14 let p = embassy_nrf::init(Default::default());
15 info!("Hello NVMC!"); 15 info!("Hello NVMC!");
16 16
17 // probe-rs-cli run breaks without this, I'm not sure why. 17 // probe-rs run breaks without this, I'm not sure why.
18 Timer::after(Duration::from_secs(1)).await; 18 Timer::after(Duration::from_secs(1)).await;
19 19
20 let mut f = Nvmc::new(p.NVMC); 20 let mut f = Nvmc::new(p.NVMC);
diff --git a/examples/nrf52840/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs
index 196255a52..31ea6c81e 100644
--- a/examples/nrf52840/src/bin/self_spawn.rs
+++ b/examples/nrf52840/src/bin/self_spawn.rs
@@ -7,7 +7,11 @@ use embassy_executor::Spawner;
7use embassy_time::{Duration, Timer}; 7use embassy_time::{Duration, Timer};
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10#[embassy_executor::task(pool_size = 2)] 10mod config {
11 pub const MY_TASK_POOL_SIZE: usize = 2;
12}
13
14#[embassy_executor::task(pool_size = config::MY_TASK_POOL_SIZE)]
11async fn my_task(spawner: Spawner, n: u32) { 15async fn my_task(spawner: Spawner, n: u32) {
12 Timer::after(Duration::from_secs(1)).await; 16 Timer::after(Duration::from_secs(1)).await;
13 info!("Spawning self! {}", n); 17 info!("Spawning self! {}", n);
diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs
index ccfd0e439..058746518 100644
--- a/examples/nrf52840/src/bin/wdt.rs
+++ b/examples/nrf52840/src/bin/wdt.rs
@@ -16,7 +16,7 @@ async fn main(_spawner: Spawner) {
16 let mut config = Config::default(); 16 let mut config = Config::default();
17 config.timeout_ticks = 32768 * 3; // 3 seconds 17 config.timeout_ticks = 32768 * 3; // 3 seconds
18 18
19 // This is needed for `probe-rs-cli run` to be able to catch the panic message 19 // This is needed for `probe-rs run` to be able to catch the panic message
20 // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. 20 // in the WDT interrupt. The core resets 2 ticks after firing the interrupt.
21 config.run_during_debug_halt = false; 21 config.run_during_debug_halt = false;
22 22
diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs
new file mode 100644
index 000000000..4eb31b105
--- /dev/null
+++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs
@@ -0,0 +1,139 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{info, unwrap, warn};
6use embassy_executor::Spawner;
7use embassy_net::tcp::TcpSocket;
8use embassy_net::{Stack, StackResources};
9use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull};
10use embassy_nrf::rng::Rng;
11use embassy_nrf::spim::{self, Spim};
12use embassy_nrf::{bind_interrupts, peripherals};
13use embedded_hal_async::spi::ExclusiveDevice;
14use embedded_io::asynch::Write;
15use static_cell::make_static;
16use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _};
17
18bind_interrupts!(struct Irqs {
19 SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
20 RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
21});
22
23#[embassy_executor::task]
24async fn wifi_task(
25 runner: hosted::Runner<
26 'static,
27 ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_31>>,
28 Input<'static, AnyPin>,
29 Output<'static, peripherals::P1_05>,
30 >,
31) -> ! {
32 runner.run().await
33}
34
35#[embassy_executor::task]
36async fn net_task(stack: &'static Stack<hosted::NetDriver<'static>>) -> ! {
37 stack.run().await
38}
39
40#[embassy_executor::main]
41async fn main(spawner: Spawner) {
42 info!("Hello World!");
43
44 let p = embassy_nrf::init(Default::default());
45
46 let miso = p.P0_28;
47 let sck = p.P0_29;
48 let mosi = p.P0_30;
49 let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive);
50 let handshake = Input::new(p.P1_01.degrade(), Pull::Up);
51 let ready = Input::new(p.P1_04.degrade(), Pull::None);
52 let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);
53
54 let mut config = spim::Config::default();
55 config.frequency = spim::Frequency::M32;
56 config.mode = spim::MODE_2; // !!!
57 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
58 let spi = ExclusiveDevice::new(spi, cs);
59
60 let (device, mut control, runner) = embassy_net_esp_hosted::new(
61 make_static!(embassy_net_esp_hosted::State::new()),
62 spi,
63 handshake,
64 ready,
65 reset,
66 )
67 .await;
68
69 unwrap!(spawner.spawn(wifi_task(runner)));
70
71 control.init().await;
72 control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await;
73
74 let config = embassy_net::Config::dhcpv4(Default::default());
75 // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 {
76 // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
77 // dns_servers: Vec::new(),
78 // gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
79 // });
80
81 // Generate random seed
82 let mut rng = Rng::new(p.RNG, Irqs);
83 let mut seed = [0; 8];
84 rng.blocking_fill_bytes(&mut seed);
85 let seed = u64::from_le_bytes(seed);
86
87 // Init network stack
88 let stack = &*make_static!(Stack::new(
89 device,
90 config,
91 make_static!(StackResources::<2>::new()),
92 seed
93 ));
94
95 unwrap!(spawner.spawn(net_task(stack)));
96
97 // And now we can use it!
98
99 let mut rx_buffer = [0; 4096];
100 let mut tx_buffer = [0; 4096];
101 let mut buf = [0; 4096];
102
103 loop {
104 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
105 socket.set_timeout(Some(embassy_time::Duration::from_secs(10)));
106
107 info!("Listening on TCP:1234...");
108 if let Err(e) = socket.accept(1234).await {
109 warn!("accept error: {:?}", e);
110 continue;
111 }
112
113 info!("Received connection from {:?}", socket.remote_endpoint());
114
115 loop {
116 let n = match socket.read(&mut buf).await {
117 Ok(0) => {
118 warn!("read EOF");
119 break;
120 }
121 Ok(n) => n,
122 Err(e) => {
123 warn!("read error: {:?}", e);
124 break;
125 }
126 };
127
128 info!("rxd {:02x}", &buf[..n]);
129
130 match socket.write_all(&buf[..n]).await {
131 Ok(()) => {}
132 Err(e) => {
133 warn!("write error: {:?}", e);
134 break;
135 }
136 };
137 }
138 }
139}
diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml
index d25355894..4c3cf3d32 100644
--- a/examples/nrf5340/.cargo/config.toml
+++ b/examples/nrf5340/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace nRF5340_xxAA with your chip as listed in `probe-rs-cli chip list` 2# replace nRF5340_xxAA with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip nRF5340_xxAA" 3runner = "probe-rs run --chip nRF5340_xxAA"
4 4
5[build] 5[build]
6target = "thumbv8m.main-none-eabihf" 6target = "thumbv8m.main-none-eabihf"
diff --git a/examples/rp/.cargo/config.toml b/examples/rp/.cargo/config.toml
index 2ee6fcb00..3d7d61740 100644
--- a/examples/rp/.cargo/config.toml
+++ b/examples/rp/.cargo/config.toml
@@ -1,5 +1,5 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2runner = "probe-rs-cli run --chip RP2040" 2runner = "probe-rs run --chip RP2040"
3 3
4[build] 4[build]
5target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ 5target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs
index e8197390c..310e84d92 100644
--- a/examples/rp/src/bin/wifi_ap_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs
@@ -42,8 +42,8 @@ async fn main(spawner: Spawner) {
42 42
43 // To make flashing faster for development, you may want to flash the firmwares independently 43 // To make flashing faster for development, you may want to flash the firmwares independently
44 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 44 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
45 // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 45 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
46 // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 46 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
47 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; 47 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
48 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 48 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
49 49
diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs
index be965807b..bbcb1b5ec 100644
--- a/examples/rp/src/bin/wifi_blinky.rs
+++ b/examples/rp/src/bin/wifi_blinky.rs
@@ -27,8 +27,8 @@ async fn main(spawner: Spawner) {
27 27
28 // To make flashing faster for development, you may want to flash the firmwares independently 28 // To make flashing faster for development, you may want to flash the firmwares independently
29 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 29 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
30 // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 30 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
31 // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 31 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
32 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; 32 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
33 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 33 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
34 34
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs
index 79534f229..391e12282 100644
--- a/examples/rp/src/bin/wifi_scan.rs
+++ b/examples/rp/src/bin/wifi_scan.rs
@@ -39,8 +39,8 @@ async fn main(spawner: Spawner) {
39 39
40 // To make flashing faster for development, you may want to flash the firmwares independently 40 // To make flashing faster for development, you may want to flash the firmwares independently
41 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 41 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
42 // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 42 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
43 // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 43 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
44 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; 44 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
45 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 45 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
46 46
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs
index 026e056fa..e9d1079a6 100644
--- a/examples/rp/src/bin/wifi_tcp_server.rs
+++ b/examples/rp/src/bin/wifi_tcp_server.rs
@@ -42,8 +42,8 @@ async fn main(spawner: Spawner) {
42 42
43 // To make flashing faster for development, you may want to flash the firmwares independently 43 // To make flashing faster for development, you may want to flash the firmwares independently
44 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: 44 // at hardcoded addresses, instead of baking them into the program with `include_bytes!`:
45 // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 45 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
46 // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 46 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
47 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; 47 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) };
48 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; 48 //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) };
49 49
diff --git a/examples/std/README.md b/examples/std/README.md
new file mode 100644
index 000000000..adc795928
--- /dev/null
+++ b/examples/std/README.md
@@ -0,0 +1,23 @@
1
2## Running the `embassy-net` examples
3
4First, create the tap0 interface. You only need to do this once.
5
6```sh
7sudo ip tuntap add name tap0 mode tap user $USER
8sudo ip link set tap0 up
9sudo ip addr add 192.168.69.100/24 dev tap0
10sudo ip -6 addr add fe80::100/64 dev tap0
11sudo ip -6 addr add fdaa::100/64 dev tap0
12sudo ip -6 route add fe80::/64 dev tap0
13sudo ip -6 route add fdaa::/64 dev tap0
14```
15
16Second, have something listening there. For example `nc -l 8000`
17
18Then run the example located in the `examples` folder:
19
20```sh
21cd $EMBASSY_ROOT/examples/std/
22cargo run --bin net -- --static-ip
23``` \ No newline at end of file
diff --git a/examples/stm32c0/.cargo/config.toml b/examples/stm32c0/.cargo/config.toml
index 517101fae..29a8be7e1 100644
--- a/examples/stm32c0/.cargo/config.toml
+++ b/examples/stm32c0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --speed 100 --chip STM32c031c6tx" 3runner = "probe-rs run --speed 100 --chip STM32c031c6tx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32f0/.cargo/config.toml b/examples/stm32f0/.cargo/config.toml
index bd0c0cd97..def4c8c92 100644
--- a/examples/stm32f0/.cargo/config.toml
+++ b/examples/stm32f0/.cargo/config.toml
@@ -1,5 +1,5 @@
1[target.thumbv6m-none-eabi] 1[target.thumbv6m-none-eabi]
2runner = 'probe-rs-cli run --chip STM32F091RCTX' 2runner = 'probe-rs run --chip STM32F091RCTX'
3 3
4[build] 4[build]
5target = "thumbv6m-none-eabi" 5target = "thumbv6m-none-eabi"
diff --git a/examples/stm32f1/.cargo/config.toml b/examples/stm32f1/.cargo/config.toml
index 81199c5aa..ce6fef11b 100644
--- a/examples/stm32f1/.cargo/config.toml
+++ b/examples/stm32f1/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F103C8 with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F103C8 with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F103C8" 3runner = "probe-rs run --chip STM32F103C8"
4 4
5[build] 5[build]
6target = "thumbv7m-none-eabi" 6target = "thumbv7m-none-eabi"
diff --git a/examples/stm32f2/.cargo/config.toml b/examples/stm32f2/.cargo/config.toml
index 5532779c8..1198fcab8 100644
--- a/examples/stm32f2/.cargo/config.toml
+++ b/examples/stm32f2/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F207ZGTx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F207ZGTx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F207ZGTx" 3runner = "probe-rs run --chip STM32F207ZGTx"
4 4
5[build] 5[build]
6target = "thumbv7m-none-eabi" 6target = "thumbv7m-none-eabi"
diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml
index 7f3fda529..cb8a7c5af 100644
--- a/examples/stm32f3/.cargo/config.toml
+++ b/examples/stm32f3/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F303ZETx" 3runner = "probe-rs run --chip STM32F303ZETx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32f4/.cargo/config.toml b/examples/stm32f4/.cargo/config.toml
index bed04b68f..16efa8e6f 100644
--- a/examples/stm32f4/.cargo/config.toml
+++ b/examples/stm32f4/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F429ZITx" 3runner = "probe-rs run --chip STM32F429ZITx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
diff --git a/examples/stm32f4/src/bin/dac.rs b/examples/stm32f4/src/bin/dac.rs
index d97ae7082..3a6216712 100644
--- a/examples/stm32f4/src/bin/dac.rs
+++ b/examples/stm32f4/src/bin/dac.rs
@@ -4,7 +4,8 @@
4 4
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::dac::{Channel, Dac, Value}; 7use embassy_stm32::dac::{DacCh1, DacChannel, Value};
8use embassy_stm32::dma::NoDma;
8use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
9 10
10#[embassy_executor::main] 11#[embassy_executor::main]
@@ -12,12 +13,12 @@ async fn main(_spawner: Spawner) -> ! {
12 let p = embassy_stm32::init(Default::default()); 13 let p = embassy_stm32::init(Default::default());
13 info!("Hello World, dude!"); 14 info!("Hello World, dude!");
14 15
15 let mut dac = Dac::new_1ch(p.DAC, p.PA4); 16 let mut dac = DacCh1::new(p.DAC, NoDma, p.PA4);
16 17
17 loop { 18 loop {
18 for v in 0..=255 { 19 for v in 0..=255 {
19 unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); 20 unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
20 unwrap!(dac.trigger(Channel::Ch1)); 21 dac.trigger();
21 } 22 }
22 } 23 }
23} 24}
diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs
index 953d99a45..b1f01417c 100644
--- a/examples/stm32f4/src/bin/usb_ethernet.rs
+++ b/examples/stm32f4/src/bin/usb_ethernet.rs
@@ -52,7 +52,9 @@ async fn main(spawner: Spawner) {
52 52
53 // Create the driver, from the HAL. 53 // Create the driver, from the HAL.
54 let ep_out_buffer = &mut make_static!([0; 256])[..]; 54 let ep_out_buffer = &mut make_static!([0; 256])[..];
55 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer); 55 let mut config = embassy_stm32::usb_otg::Config::default();
56 config.vbus_detection = true;
57 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, config);
56 58
57 // Create embassy-usb Config 59 // Create embassy-usb Config
58 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 60 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs
index f8f5940a7..4ff6452ef 100644
--- a/examples/stm32f4/src/bin/usb_serial.rs
+++ b/examples/stm32f4/src/bin/usb_serial.rs
@@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) {
29 29
30 // Create the driver, from the HAL. 30 // Create the driver, from the HAL.
31 let mut ep_out_buffer = [0u8; 256]; 31 let mut ep_out_buffer = [0u8; 256];
32 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); 32 let mut config = embassy_stm32::usb_otg::Config::default();
33 config.vbus_detection = true;
34 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
33 35
34 // Create embassy-usb Config 36 // Create embassy-usb Config
35 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 37 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml
index 7d6c88a99..9088eea6e 100644
--- a/examples/stm32f7/.cargo/config.toml
+++ b/examples/stm32f7/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32F767ZITx" 3runner = "probe-rs run --chip STM32F767ZITx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs
index 763309ce2..a2c76178b 100644
--- a/examples/stm32f7/src/bin/usb_serial.rs
+++ b/examples/stm32f7/src/bin/usb_serial.rs
@@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) {
30 30
31 // Create the driver, from the HAL. 31 // Create the driver, from the HAL.
32 let mut ep_out_buffer = [0u8; 256]; 32 let mut ep_out_buffer = [0u8; 256];
33 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); 33 let mut config = embassy_stm32::usb_otg::Config::default();
34 config.vbus_detection = true;
35 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
34 36
35 // Create embassy-usb Config 37 // Create embassy-usb Config
36 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 38 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32g0/.cargo/config.toml b/examples/stm32g0/.cargo/config.toml
index a7a5fbd84..35cca5412 100644
--- a/examples/stm32g0/.cargo/config.toml
+++ b/examples/stm32g0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32G071RBTx" 3runner = "probe-rs run --chip STM32G071RBTx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32g4/.cargo/config.toml b/examples/stm32g4/.cargo/config.toml
index 606d7d5a3..d28ad069e 100644
--- a/examples/stm32g4/.cargo/config.toml
+++ b/examples/stm32g4/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32G071C8Rx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32G484VETx" 3runner = "probe-rs run --chip STM32G484VETx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs
index c111a9787..77cfa67d3 100644
--- a/examples/stm32g4/src/bin/usb_serial.rs
+++ b/examples/stm32g4/src/bin/usb_serial.rs
@@ -4,10 +4,10 @@
4 4
5use defmt::{panic, *}; 5use defmt::{panic, *};
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc}; 7use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc};
8use embassy_stm32::time::Hertz; 8use embassy_stm32::time::Hertz;
9use embassy_stm32::usb::{self, Driver, Instance}; 9use embassy_stm32::usb::{self, Driver, Instance};
10use embassy_stm32::{bind_interrupts, pac, peripherals, Config}; 10use embassy_stm32::{bind_interrupts, peripherals, Config};
11use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; 11use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
12use embassy_usb::driver::EndpointError; 12use embassy_usb::driver::EndpointError;
13use embassy_usb::Builder; 13use embassy_usb::Builder;
@@ -22,23 +22,35 @@ bind_interrupts!(struct Irqs {
22async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
23 let mut config = Config::default(); 23 let mut config = Config::default();
24 24
25 // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
26 const USE_HSI48: bool = true;
27
28 let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) };
29
25 config.rcc.pll = Some(Pll { 30 config.rcc.pll = Some(Pll {
26 source: PllSrc::HSE(Hertz(8000000)), 31 source: PllSrc::HSE(Hertz(8_000_000)),
27 prediv_m: PllM::Div2, 32 prediv_m: PllM::Div2,
28 mul_n: PllN::Mul72, 33 mul_n: PllN::Mul72,
29 div_p: None, 34 div_p: None,
30 // USB and CAN at 48 MHz 35 div_q: pllq_div,
31 div_q: Some(PllQ::Div6),
32 // Main system clock at 144 MHz 36 // Main system clock at 144 MHz
33 div_r: Some(PllR::Div2), 37 div_r: Some(PllR::Div2),
34 }); 38 });
35 39
36 config.rcc.mux = ClockSrc::PLL; 40 config.rcc.mux = ClockSrc::PLL;
37 41
42 if USE_HSI48 {
43 // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
44 config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig {
45 sync_src: CrsSyncSource::Usb,
46 })));
47 } else {
48 config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
49 }
50
38 let p = embassy_stm32::init(config); 51 let p = embassy_stm32::init(config);
39 info!("Hello World!");
40 52
41 pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10)); 53 info!("Hello World!");
42 54
43 let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); 55 let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
44 56
diff --git a/examples/stm32h5/.cargo/config.toml b/examples/stm32h5/.cargo/config.toml
index c8b864b6c..478146142 100644
--- a/examples/stm32h5/.cargo/config.toml
+++ b/examples/stm32h5/.cargo/config.toml
@@ -1,5 +1,5 @@
1[target.thumbv8m.main-none-eabihf] 1[target.thumbv8m.main-none-eabihf]
2runner = 'probe-rs-cli run --chip STM32H563ZITx' 2runner = 'probe-rs run --chip STM32H563ZITx'
3 3
4[build] 4[build]
5target = "thumbv8m.main-none-eabihf" 5target = "thumbv8m.main-none-eabihf"
diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml
index f08f57a54..5f680dbce 100644
--- a/examples/stm32h7/.cargo/config.toml
+++ b/examples/stm32h7/.cargo/config.toml
@@ -1,5 +1,5 @@
1[target.thumbv7em-none-eabihf] 1[target.thumbv7em-none-eabihf]
2runner = 'probe-rs-cli run --chip STM32H743ZITx' 2runner = 'probe-rs run --chip STM32H743ZITx'
3 3
4[build] 4[build]
5target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) 5target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs
index f12716370..586b4154b 100644
--- a/examples/stm32h7/src/bin/dac.rs
+++ b/examples/stm32h7/src/bin/dac.rs
@@ -4,7 +4,8 @@
4 4
5use cortex_m_rt::entry; 5use cortex_m_rt::entry;
6use defmt::*; 6use defmt::*;
7use embassy_stm32::dac::{Channel, Dac, Value}; 7use embassy_stm32::dac::{DacCh1, DacChannel, Value};
8use embassy_stm32::dma::NoDma;
8use embassy_stm32::time::mhz; 9use embassy_stm32::time::mhz;
9use embassy_stm32::Config; 10use embassy_stm32::Config;
10use {defmt_rtt as _, panic_probe as _}; 11use {defmt_rtt as _, panic_probe as _};
@@ -19,12 +20,12 @@ fn main() -> ! {
19 config.rcc.pll1.q_ck = Some(mhz(100)); 20 config.rcc.pll1.q_ck = Some(mhz(100));
20 let p = embassy_stm32::init(config); 21 let p = embassy_stm32::init(config);
21 22
22 let mut dac = Dac::new_1ch(p.DAC1, p.PA4); 23 let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
23 24
24 loop { 25 loop {
25 for v in 0..=255 { 26 for v in 0..=255 {
26 unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); 27 unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
27 unwrap!(dac.trigger(Channel::Ch1)); 28 dac.trigger();
28 } 29 }
29 } 30 }
30} 31}
diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs
index c622f19f7..97291f60c 100644
--- a/examples/stm32h7/src/bin/usb_serial.rs
+++ b/examples/stm32h7/src/bin/usb_serial.rs
@@ -29,7 +29,9 @@ async fn main(_spawner: Spawner) {
29 29
30 // Create the driver, from the HAL. 30 // Create the driver, from the HAL.
31 let mut ep_out_buffer = [0u8; 256]; 31 let mut ep_out_buffer = [0u8; 256];
32 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); 32 let mut config = embassy_stm32::usb_otg::Config::default();
33 config.vbus_detection = true;
34 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
33 35
34 // Create embassy-usb Config 36 // Create embassy-usb Config
35 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 37 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml
index 526f5a1f7..b050334b2 100644
--- a/examples/stm32l0/.cargo/config.toml
+++ b/examples/stm32l0/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32L053R8Tx" 3runner = "probe-rs run --chip STM32L053R8Tx"
4 4
5[build] 5[build]
6target = "thumbv6m-none-eabi" 6target = "thumbv6m-none-eabi"
diff --git a/examples/stm32l1/.cargo/config.toml b/examples/stm32l1/.cargo/config.toml
index 1401500a0..9cabd14ba 100644
--- a/examples/stm32l1/.cargo/config.toml
+++ b/examples/stm32l1/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32L151CBxxA" 3runner = "probe-rs run --chip STM32L151CBxxA"
4 4
5[build] 5[build]
6target = "thumbv7m-none-eabi" 6target = "thumbv7m-none-eabi"
diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml
index abf55eb2e..36e74e5a5 100644
--- a/examples/stm32l4/.cargo/config.toml
+++ b/examples/stm32l4/.cargo/config.toml
@@ -1,8 +1,8 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
3#runner = "probe-rs-cli run --chip STM32L475VGT6" 3#runner = "probe-rs run --chip STM32L475VGT6"
4#runner = "probe-rs-cli run --chip STM32L475VG" 4#runner = "probe-rs run --chip STM32L475VG"
5runner = "probe-rs-cli run --chip STM32L4S5VI" 5runner = "probe-rs run --chip STM32L4S5VI"
6 6
7[build] 7[build]
8target = "thumbv7em-none-eabi" 8target = "thumbv7em-none-eabi"
diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs
index a36ed5d90..ade43eb35 100644
--- a/examples/stm32l4/src/bin/dac.rs
+++ b/examples/stm32l4/src/bin/dac.rs
@@ -3,26 +3,21 @@
3#![feature(type_alias_impl_trait)] 3#![feature(type_alias_impl_trait)]
4 4
5use defmt::*; 5use defmt::*;
6use embassy_stm32::dac::{Channel, Dac, Value}; 6use embassy_stm32::dac::{DacCh1, DacChannel, Value};
7use embassy_stm32::pac; 7use embassy_stm32::dma::NoDma;
8use {defmt_rtt as _, panic_probe as _}; 8use {defmt_rtt as _, panic_probe as _};
9 9
10#[cortex_m_rt::entry] 10#[cortex_m_rt::entry]
11fn main() -> ! { 11fn main() -> ! {
12 info!("Hello World!");
13
14 pac::RCC.apb1enr1().modify(|w| {
15 w.set_dac1en(true);
16 });
17
18 let p = embassy_stm32::init(Default::default()); 12 let p = embassy_stm32::init(Default::default());
13 info!("Hello World!");
19 14
20 let mut dac = Dac::new_1ch(p.DAC1, p.PA4); 15 let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
21 16
22 loop { 17 loop {
23 for v in 0..=255 { 18 for v in 0..=255 {
24 unwrap!(dac.set(Channel::Ch1, Value::Bit8(to_sine_wave(v)))); 19 unwrap!(dac.set(Value::Bit8(to_sine_wave(v))));
25 unwrap!(dac.trigger(Channel::Ch1)); 20 dac.trigger();
26 } 21 }
27 } 22 }
28} 23}
diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs
new file mode 100644
index 000000000..c27cc03e1
--- /dev/null
+++ b/examples/stm32l4/src/bin/dac_dma.rs
@@ -0,0 +1,137 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::dac::{DacChannel, ValueArray};
8use embassy_stm32::pac::timer::vals::{Mms, Opm};
9use embassy_stm32::peripherals::{TIM6, TIM7};
10use embassy_stm32::rcc::low_level::RccPeripheral;
11use embassy_stm32::time::Hertz;
12use embassy_stm32::timer::low_level::Basic16bitInstance;
13use micromath::F32Ext;
14use {defmt_rtt as _, panic_probe as _};
15
16pub type Dac1Type =
17 embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>;
18
19pub type Dac2Type =
20 embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>;
21
22#[embassy_executor::main]
23async fn main(spawner: Spawner) {
24 let config = embassy_stm32::Config::default();
25
26 // Initialize the board and obtain a Peripherals instance
27 let p: embassy_stm32::Peripherals = embassy_stm32::init(config);
28
29 // Obtain two independent channels (p.DAC1 can only be consumed once, though!)
30 let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
31
32 spawner.spawn(dac_task1(dac_ch1)).ok();
33 spawner.spawn(dac_task2(dac_ch2)).ok();
34}
35
36#[embassy_executor::task]
37async fn dac_task1(mut dac: Dac1Type) {
38 let data: &[u8; 256] = &calculate_array::<256>();
39
40 info!("TIM6 frequency is {}", TIM6::frequency());
41 const FREQUENCY: Hertz = Hertz::hz(200);
42
43 // Compute the reload value such that we obtain the FREQUENCY for the sine
44 let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32;
45
46 // Depends on your clock and on the specific chip used, you may need higher or lower values here
47 if reload < 10 {
48 error!("Reload value {} below threshold!", reload);
49 }
50
51 dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap();
52 dac.enable_channel().unwrap();
53
54 TIM6::enable();
55 TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
56 TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
57 TIM6::regs().cr1().modify(|w| {
58 w.set_opm(Opm::DISABLED);
59 w.set_cen(true);
60 });
61
62 debug!(
63 "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
64 TIM6::frequency(),
65 FREQUENCY,
66 reload,
67 reload as u16,
68 data.len()
69 );
70
71 // Loop technically not necessary if DMA circular mode is enabled
72 loop {
73 info!("Loop DAC1");
74 if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
75 error!("Could not write to dac: {}", e);
76 }
77 }
78}
79
80#[embassy_executor::task]
81async fn dac_task2(mut dac: Dac2Type) {
82 let data: &[u8; 256] = &calculate_array::<256>();
83
84 info!("TIM7 frequency is {}", TIM7::frequency());
85
86 const FREQUENCY: Hertz = Hertz::hz(600);
87 let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32;
88
89 if reload < 10 {
90 error!("Reload value {} below threshold!", reload);
91 }
92
93 TIM7::enable();
94 TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1));
95 TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE));
96 TIM7::regs().cr1().modify(|w| {
97 w.set_opm(Opm::DISABLED);
98 w.set_cen(true);
99 });
100
101 dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap();
102
103 debug!(
104 "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}",
105 TIM7::frequency(),
106 FREQUENCY,
107 reload,
108 reload as u16,
109 data.len()
110 );
111
112 if let Err(e) = dac.write(ValueArray::Bit8(data), true).await {
113 error!("Could not write to dac: {}", e);
114 }
115}
116
117fn to_sine_wave(v: u8) -> u8 {
118 if v >= 128 {
119 // top half
120 let r = 3.14 * ((v - 128) as f32 / 128.0);
121 (r.sin() * 128.0 + 127.0) as u8
122 } else {
123 // bottom half
124 let r = 3.14 + 3.14 * (v as f32 / 128.0);
125 (r.sin() * 128.0 + 127.0) as u8
126 }
127}
128
129fn calculate_array<const N: usize>() -> [u8; N] {
130 let mut res = [0; N];
131 let mut i = 0;
132 while i < N {
133 res[i] = to_sine_wave(i as u8);
134 i += 1;
135 }
136 res
137}
diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs
index 80811a43e..410d6891b 100644
--- a/examples/stm32l4/src/bin/usb_serial.rs
+++ b/examples/stm32l4/src/bin/usb_serial.rs
@@ -30,7 +30,9 @@ async fn main(_spawner: Spawner) {
30 30
31 // Create the driver, from the HAL. 31 // Create the driver, from the HAL.
32 let mut ep_out_buffer = [0u8; 256]; 32 let mut ep_out_buffer = [0u8; 256];
33 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); 33 let mut config = embassy_stm32::usb_otg::Config::default();
34 config.vbus_detection = true;
35 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
34 36
35 // Create embassy-usb Config 37 // Create embassy-usb Config
36 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 38 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml
index 1dc3a6fb7..86a145a27 100644
--- a/examples/stm32l5/.cargo/config.toml
+++ b/examples/stm32l5/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32L552ZETxQ with your chip as listed in `probe-rs-cli chip list` 2# replace STM32L552ZETxQ with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32L552ZETxQ" 3runner = "probe-rs run --chip STM32L552ZETxQ"
4 4
5[build] 5[build]
6target = "thumbv8m.main-none-eabihf" 6target = "thumbv8m.main-none-eabihf"
diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml
index cecd01938..36c5b63a6 100644
--- a/examples/stm32u5/.cargo/config.toml
+++ b/examples/stm32u5/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32U585AIIx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32U585AIIx with your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32U585AIIx" 3runner = "probe-rs run --chip STM32U585AIIx"
4 4
5[build] 5[build]
6target = "thumbv8m.main-none-eabihf" 6target = "thumbv8m.main-none-eabihf"
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs
index f36daf91b..9e47fb18a 100644
--- a/examples/stm32u5/src/bin/usb_serial.rs
+++ b/examples/stm32u5/src/bin/usb_serial.rs
@@ -31,7 +31,9 @@ async fn main(_spawner: Spawner) {
31 31
32 // Create the driver, from the HAL. 32 // Create the driver, from the HAL.
33 let mut ep_out_buffer = [0u8; 256]; 33 let mut ep_out_buffer = [0u8; 256];
34 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer); 34 let mut config = embassy_stm32::usb_otg::Config::default();
35 config.vbus_detection = true;
36 let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
35 37
36 // Create embassy-usb Config 38 // Create embassy-usb Config
37 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); 39 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml
index d23fdc513..8b6d6d754 100644
--- a/examples/stm32wb/.cargo/config.toml
+++ b/examples/stm32wb/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace STM32WB55CCUx with your chip as listed in `probe-rs-cli chip list` 2# replace STM32WB55CCUx with your chip as listed in `probe-rs chip list`
3# runner = "probe-rs-cli run --chip STM32WB55CCUx --speed 1000 --connect-under-reset" 3# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset"
4runner = "teleprobe local run --chip STM32WB55RG --elf" 4runner = "teleprobe local run --chip STM32WB55RG --elf"
5 5
6[build] 6[build]
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index e41424aad..fbb2d918b 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -33,4 +33,11 @@ required-features = ["ble"]
33 33
34[[bin]] 34[[bin]]
35name = "tl_mbox_mac" 35name = "tl_mbox_mac"
36required-features = ["mac"] \ No newline at end of file 36required-features = ["mac"]
37
38[[bin]]
39name = "eddystone_beacon"
40required-features = ["ble"]
41
42[patch.crates-io]
43stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} \ No newline at end of file
diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs
new file mode 100644
index 000000000..b99f8cb2e
--- /dev/null
+++ b/examples/stm32wb/src/bin/eddystone_beacon.rs
@@ -0,0 +1,249 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use core::time::Duration;
6
7use defmt::*;
8use embassy_executor::Spawner;
9use embassy_stm32::bind_interrupts;
10use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
11use embassy_stm32_wpan::hci::host::uart::UartHci;
12use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType};
13use embassy_stm32_wpan::hci::types::AdvertisingType;
14use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{
15 AdvertisingDataType, DiscoverableParameters, GapCommands, Role,
16};
17use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::GattCommands;
18use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel};
19use embassy_stm32_wpan::hci::BdAddr;
20use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp;
21use embassy_stm32_wpan::TlMbox;
22use {defmt_rtt as _, panic_probe as _};
23
24bind_interrupts!(struct Irqs{
25 IPCC_C1_RX => ReceiveInterruptHandler;
26 IPCC_C1_TX => TransmitInterruptHandler;
27});
28
29const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;
30
31#[embassy_executor::main]
32async fn main(_spawner: Spawner) {
33 /*
34 How to make this work:
35
36 - Obtain a NUCLEO-STM32WB55 from your preferred supplier.
37 - Download and Install STM32CubeProgrammer.
38 - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from
39 gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
40 - Open STM32CubeProgrammer
41 - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware.
42 - Once complete, click connect to connect to the device.
43 - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services".
44 - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file
45 - Select that file, the memory address, "verify download", and then "Firmware Upgrade".
46 - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the
47 stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address.
48 - Select that file, the memory address, "verify download", and then "Firmware Upgrade".
49 - Select "Start Wireless Stack".
50 - Disconnect from the device.
51 - In the examples folder for stm32wb, modify the memory.x file to match your target device.
52 - Run this example.
53
54 Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name.
55 */
56
57 let p = embassy_stm32::init(Default::default());
58 info!("Hello World!");
59
60 let config = Config::default();
61 let mut mbox = TlMbox::init(p.IPCC, Irqs, config);
62
63 let sys_event = mbox.sys_subsystem.read().await;
64 info!("sys event: {}", sys_event.payload());
65
66 mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
67
68 info!("resetting BLE...");
69 mbox.ble_subsystem.reset().await;
70 let response = mbox.ble_subsystem.read().await.unwrap();
71 defmt::info!("{}", response);
72
73 info!("config public address...");
74 mbox.ble_subsystem
75 .write_config_data(&ConfigData::public_address(get_bd_addr()).build())
76 .await;
77 let response = mbox.ble_subsystem.read().await.unwrap();
78 defmt::info!("{}", response);
79
80 info!("config random address...");
81 mbox.ble_subsystem
82 .write_config_data(&ConfigData::random_address(get_random_addr()).build())
83 .await;
84 let response = mbox.ble_subsystem.read().await.unwrap();
85 defmt::info!("{}", response);
86
87 info!("config identity root...");
88 mbox.ble_subsystem
89 .write_config_data(&ConfigData::identity_root(&get_irk()).build())
90 .await;
91 let response = mbox.ble_subsystem.read().await.unwrap();
92 defmt::info!("{}", response);
93
94 info!("config encryption root...");
95 mbox.ble_subsystem
96 .write_config_data(&ConfigData::encryption_root(&get_erk()).build())
97 .await;
98 let response = mbox.ble_subsystem.read().await.unwrap();
99 defmt::info!("{}", response);
100
101 info!("config tx power level...");
102 mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await;
103 let response = mbox.ble_subsystem.read().await.unwrap();
104 defmt::info!("{}", response);
105
106 info!("GATT init...");
107 mbox.ble_subsystem.init_gatt().await;
108 let response = mbox.ble_subsystem.read().await.unwrap();
109 defmt::info!("{}", response);
110
111 info!("GAP init...");
112 mbox.ble_subsystem
113 .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH)
114 .await;
115 let response = mbox.ble_subsystem.read().await.unwrap();
116 defmt::info!("{}", response);
117
118 // info!("set scan response...");
119 // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap();
120 // let response = mbox.ble_subsystem.read().await.unwrap();
121 // defmt::info!("{}", response);
122
123 info!("set discoverable...");
124 mbox.ble_subsystem
125 .set_discoverable(&DiscoverableParameters {
126 advertising_type: AdvertisingType::NonConnectableUndirected,
127 advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))),
128 address_type: OwnAddressType::Public,
129 filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan,
130 local_name: None,
131 advertising_data: &[],
132 conn_interval: (None, None),
133 })
134 .await
135 .unwrap();
136
137 let response = mbox.ble_subsystem.read().await;
138 defmt::info!("{}", response);
139
140 // remove some advertisement to decrease the packet size
141 info!("delete tx power ad type...");
142 mbox.ble_subsystem
143 .delete_ad_type(AdvertisingDataType::TxPowerLevel)
144 .await;
145 let response = mbox.ble_subsystem.read().await.unwrap();
146 defmt::info!("{}", response);
147
148 info!("delete conn interval ad type...");
149 mbox.ble_subsystem
150 .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval)
151 .await;
152 let response = mbox.ble_subsystem.read().await.unwrap();
153 defmt::info!("{}", response);
154
155 info!("update advertising data...");
156 mbox.ble_subsystem
157 .update_advertising_data(&eddystone_advertising_data())
158 .await
159 .unwrap();
160 let response = mbox.ble_subsystem.read().await.unwrap();
161 defmt::info!("{}", response);
162
163 info!("update advertising data type...");
164 mbox.ble_subsystem
165 .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe])
166 .await
167 .unwrap();
168 let response = mbox.ble_subsystem.read().await.unwrap();
169 defmt::info!("{}", response);
170
171 info!("update advertising data flags...");
172 mbox.ble_subsystem
173 .update_advertising_data(&[
174 2,
175 AdvertisingDataType::Flags as u8,
176 (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support
177 ])
178 .await
179 .unwrap();
180 let response = mbox.ble_subsystem.read().await.unwrap();
181 defmt::info!("{}", response);
182
183 cortex_m::asm::wfi();
184}
185
186fn get_bd_addr() -> BdAddr {
187 let mut bytes = [0u8; 6];
188
189 let lhci_info = LhciC1DeviceInformationCcrp::new();
190 bytes[0] = (lhci_info.uid64 & 0xff) as u8;
191 bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;
192 bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;
193 bytes[3] = lhci_info.device_type_id;
194 bytes[4] = (lhci_info.st_company_id & 0xff) as u8;
195 bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8;
196
197 BdAddr(bytes)
198}
199
200fn get_random_addr() -> BdAddr {
201 let mut bytes = [0u8; 6];
202
203 let lhci_info = LhciC1DeviceInformationCcrp::new();
204 bytes[0] = (lhci_info.uid64 & 0xff) as u8;
205 bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;
206 bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;
207 bytes[3] = 0;
208 bytes[4] = 0x6E;
209 bytes[5] = 0xED;
210
211 BdAddr(bytes)
212}
213
214const BLE_CFG_IRK: [u8; 16] = [
215 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
216];
217const BLE_CFG_ERK: [u8; 16] = [
218 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21,
219];
220
221fn get_irk() -> EncryptionKey {
222 EncryptionKey(BLE_CFG_IRK)
223}
224
225fn get_erk() -> EncryptionKey {
226 EncryptionKey(BLE_CFG_ERK)
227}
228
229fn eddystone_advertising_data() -> [u8; 24] {
230 const EDDYSTONE_URL: &[u8] = b"www.rust-lang.com";
231
232 let mut service_data = [0u8; 24];
233 let url_len = EDDYSTONE_URL.len();
234
235 service_data[0] = 6 + url_len as u8;
236 service_data[1] = AdvertisingDataType::ServiceData as u8;
237
238 // 16-bit eddystone uuid
239 service_data[2] = 0xaa;
240 service_data[3] = 0xFE;
241
242 service_data[4] = 0x10; // URL frame type
243 service_data[5] = 22_i8 as u8; // calibrated TX power at 0m
244 service_data[6] = 0x03; // eddystone url prefix = https
245
246 service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL);
247
248 service_data
249}
diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs
index 439bd01ac..a511e89aa 100644
--- a/examples/stm32wb/src/bin/tl_mbox_ble.rs
+++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs
@@ -52,10 +52,10 @@ async fn main(_spawner: Spawner) {
52 mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; 52 mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
53 53
54 info!("starting ble..."); 54 info!("starting ble...");
55 mbox.ble_subsystem.write(0x0c, &[]).await; 55 mbox.ble_subsystem.tl_write(0x0c, &[]).await;
56 56
57 info!("waiting for ble..."); 57 info!("waiting for ble...");
58 let ble_event = mbox.ble_subsystem.read().await; 58 let ble_event = mbox.ble_subsystem.tl_read().await;
59 59
60 info!("ble event: {}", ble_event.payload()); 60 info!("ble event: {}", ble_event.payload());
61 61
diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs
index a42939bbd..f67be4682 100644
--- a/examples/stm32wb/src/bin/tl_mbox_mac.rs
+++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs
@@ -49,13 +49,15 @@ async fn main(_spawner: Spawner) {
49 let sys_event = mbox.sys_subsystem.read().await; 49 let sys_event = mbox.sys_subsystem.read().await;
50 info!("sys event: {}", sys_event.payload()); 50 info!("sys event: {}", sys_event.payload());
51 51
52 mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; 52 let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await;
53 info!("initialized mac: {}", result);
54
53 // 55 //
54 // info!("starting ble..."); 56 // info!("starting ble...");
55 // mbox.ble_subsystem.write(0x0c, &[]).await; 57 // mbox.ble_subsystem.t_write(0x0c, &[]).await;
56 // 58 //
57 // info!("waiting for ble..."); 59 // info!("waiting for ble...");
58 // let ble_event = mbox.ble_subsystem.read().await; 60 // let ble_event = mbox.ble_subsystem.tl_read().await;
59 // 61 //
60 // info!("ble event: {}", ble_event.payload()); 62 // info!("ble event: {}", ble_event.payload());
61 63
diff --git a/examples/stm32wl/.cargo/config.toml b/examples/stm32wl/.cargo/config.toml
index b49b582e0..4f8094ff2 100644
--- a/examples/stm32wl/.cargo/config.toml
+++ b/examples/stm32wl/.cargo/config.toml
@@ -1,6 +1,6 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2# replace your chip as listed in `probe-rs-cli chip list` 2# replace your chip as listed in `probe-rs chip list`
3runner = "probe-rs-cli run --chip STM32WLE5JCIx" 3runner = "probe-rs run --chip STM32WLE5JCIx"
4 4
5[build] 5[build]
6target = "thumbv7em-none-eabihf" 6target = "thumbv7em-none-eabihf"
diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml
index 9735c87d9..4f9ecc47a 100644
--- a/tests/nrf/Cargo.toml
+++ b/tests/nrf/Cargo.toml
@@ -13,6 +13,10 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature
13embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } 13embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] }
14embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 14embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
15embedded-io = { version = "0.4.0", features = ["async"] } 15embedded-io = { version = "0.4.0", features = ["async"] }
16embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
17embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] }
18embedded-hal-async = { version = "0.2.0-alpha.1" }
19static_cell = { version = "1.1", features = [ "nightly" ] }
16 20
17defmt = "0.3" 21defmt = "0.3"
18defmt-rtt = "0.4" 22defmt-rtt = "0.4"
diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
new file mode 100644
index 000000000..277b985c5
--- /dev/null
+++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs
@@ -0,0 +1,270 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../common.rs"]
6mod common;
7
8use defmt::{error, info, unwrap};
9use embassy_executor::Spawner;
10use embassy_futures::join::join;
11use embassy_net::tcp::TcpSocket;
12use embassy_net::{Config, Ipv4Address, Stack, StackResources};
13use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull};
14use embassy_nrf::rng::Rng;
15use embassy_nrf::spim::{self, Spim};
16use embassy_nrf::{bind_interrupts, peripherals};
17use embassy_time::{with_timeout, Duration, Timer};
18use embedded_hal_async::spi::ExclusiveDevice;
19use static_cell::make_static;
20use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _};
21
22teleprobe_meta::timeout!(120);
23
24bind_interrupts!(struct Irqs {
25 SPIM3 => spim::InterruptHandler<peripherals::SPI3>;
26 RNG => embassy_nrf::rng::InterruptHandler<peripherals::RNG>;
27});
28
29#[embassy_executor::task]
30async fn wifi_task(
31 runner: hosted::Runner<
32 'static,
33 ExclusiveDevice<Spim<'static, peripherals::SPI3>, Output<'static, peripherals::P0_31>>,
34 Input<'static, AnyPin>,
35 Output<'static, peripherals::P1_05>,
36 >,
37) -> ! {
38 runner.run().await
39}
40
41type MyDriver = hosted::NetDriver<'static>;
42
43#[embassy_executor::task]
44async fn net_task(stack: &'static Stack<MyDriver>) -> ! {
45 stack.run().await
46}
47
48#[embassy_executor::main]
49async fn main(spawner: Spawner) {
50 info!("Hello World!");
51
52 let p = embassy_nrf::init(Default::default());
53
54 let miso = p.P0_28;
55 let sck = p.P0_29;
56 let mosi = p.P0_30;
57 let cs = Output::new(p.P0_31, Level::High, OutputDrive::HighDrive);
58 let handshake = Input::new(p.P1_01.degrade(), Pull::Up);
59 let ready = Input::new(p.P1_04.degrade(), Pull::None);
60 let reset = Output::new(p.P1_05, Level::Low, OutputDrive::Standard);
61
62 let mut config = spim::Config::default();
63 config.frequency = spim::Frequency::M32;
64 config.mode = spim::MODE_2; // !!!
65 let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config);
66 let spi = ExclusiveDevice::new(spi, cs);
67
68 let (device, mut control, runner) = embassy_net_esp_hosted::new(
69 make_static!(embassy_net_esp_hosted::State::new()),
70 spi,
71 handshake,
72 ready,
73 reset,
74 )
75 .await;
76
77 unwrap!(spawner.spawn(wifi_task(runner)));
78
79 control.init().await;
80 control.join(WIFI_NETWORK, WIFI_PASSWORD).await;
81
82 // Generate random seed
83 let mut rng = Rng::new(p.RNG, Irqs);
84 let mut seed = [0; 8];
85 rng.blocking_fill_bytes(&mut seed);
86 let seed = u64::from_le_bytes(seed);
87
88 // Init network stack
89 let stack = &*make_static!(Stack::new(
90 device,
91 Config::dhcpv4(Default::default()),
92 make_static!(StackResources::<2>::new()),
93 seed
94 ));
95
96 unwrap!(spawner.spawn(net_task(stack)));
97
98 info!("Waiting for DHCP up...");
99 while stack.config_v4().is_none() {
100 Timer::after(Duration::from_millis(100)).await;
101 }
102 info!("IP addressing up!");
103
104 let down = test_download(stack).await;
105 let up = test_upload(stack).await;
106 let updown = test_upload_download(stack).await;
107
108 assert!(down > TEST_EXPECTED_DOWNLOAD_KBPS);
109 assert!(up > TEST_EXPECTED_UPLOAD_KBPS);
110 assert!(updown > TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS);
111
112 info!("Test OK");
113 cortex_m::asm::bkpt();
114}
115
116// Test-only wifi network, no internet access!
117const WIFI_NETWORK: &str = "EmbassyTest";
118const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud";
119
120const TEST_DURATION: usize = 10;
121const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 150;
122const TEST_EXPECTED_UPLOAD_KBPS: usize = 150;
123const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150;
124const RX_BUFFER_SIZE: usize = 4096;
125const TX_BUFFER_SIZE: usize = 4096;
126const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2);
127const DOWNLOAD_PORT: u16 = 4321;
128const UPLOAD_PORT: u16 = 4322;
129const UPLOAD_DOWNLOAD_PORT: u16 = 4323;
130
131async fn test_download(stack: &'static Stack<MyDriver>) -> usize {
132 info!("Testing download...");
133
134 let mut rx_buffer = [0; RX_BUFFER_SIZE];
135 let mut tx_buffer = [0; TX_BUFFER_SIZE];
136 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
137 socket.set_timeout(Some(Duration::from_secs(10)));
138
139 info!("connecting to {:?}:{}...", SERVER_ADDRESS, DOWNLOAD_PORT);
140 if let Err(e) = socket.connect((SERVER_ADDRESS, DOWNLOAD_PORT)).await {
141 error!("connect error: {:?}", e);
142 return 0;
143 }
144 info!("connected, testing...");
145
146 let mut rx_buf = [0; 4096];
147 let mut total: usize = 0;
148 with_timeout(Duration::from_secs(TEST_DURATION as _), async {
149 loop {
150 match socket.read(&mut rx_buf).await {
151 Ok(0) => {
152 error!("read EOF");
153 return 0;
154 }
155 Ok(n) => total += n,
156 Err(e) => {
157 error!("read error: {:?}", e);
158 return 0;
159 }
160 }
161 }
162 })
163 .await
164 .ok();
165
166 let kbps = (total + 512) / 1024 / TEST_DURATION;
167 info!("download: {} kB/s", kbps);
168 kbps
169}
170
171async fn test_upload(stack: &'static Stack<MyDriver>) -> usize {
172 info!("Testing upload...");
173
174 let mut rx_buffer = [0; RX_BUFFER_SIZE];
175 let mut tx_buffer = [0; TX_BUFFER_SIZE];
176 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
177 socket.set_timeout(Some(Duration::from_secs(10)));
178
179 info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_PORT);
180 if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_PORT)).await {
181 error!("connect error: {:?}", e);
182 return 0;
183 }
184 info!("connected, testing...");
185
186 let buf = [0; 4096];
187 let mut total: usize = 0;
188 with_timeout(Duration::from_secs(TEST_DURATION as _), async {
189 loop {
190 match socket.write(&buf).await {
191 Ok(0) => {
192 error!("write zero?!??!?!");
193 return 0;
194 }
195 Ok(n) => total += n,
196 Err(e) => {
197 error!("write error: {:?}", e);
198 return 0;
199 }
200 }
201 }
202 })
203 .await
204 .ok();
205
206 let kbps = (total + 512) / 1024 / TEST_DURATION;
207 info!("upload: {} kB/s", kbps);
208 kbps
209}
210
211async fn test_upload_download(stack: &'static Stack<MyDriver>) -> usize {
212 info!("Testing upload+download...");
213
214 let mut rx_buffer = [0; RX_BUFFER_SIZE];
215 let mut tx_buffer = [0; TX_BUFFER_SIZE];
216 let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
217 socket.set_timeout(Some(Duration::from_secs(10)));
218
219 info!("connecting to {:?}:{}...", SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT);
220 if let Err(e) = socket.connect((SERVER_ADDRESS, UPLOAD_DOWNLOAD_PORT)).await {
221 error!("connect error: {:?}", e);
222 return 0;
223 }
224 info!("connected, testing...");
225
226 let (mut reader, mut writer) = socket.split();
227
228 let tx_buf = [0; 4096];
229 let mut rx_buf = [0; 4096];
230 let mut total: usize = 0;
231 let tx_fut = async {
232 loop {
233 match writer.write(&tx_buf).await {
234 Ok(0) => {
235 error!("write zero?!??!?!");
236 return 0;
237 }
238 Ok(_) => {}
239 Err(e) => {
240 error!("write error: {:?}", e);
241 return 0;
242 }
243 }
244 }
245 };
246
247 let rx_fut = async {
248 loop {
249 match reader.read(&mut rx_buf).await {
250 Ok(0) => {
251 error!("read EOF");
252 return 0;
253 }
254 Ok(n) => total += n,
255 Err(e) => {
256 error!("read error: {:?}", e);
257 return 0;
258 }
259 }
260 }
261 };
262
263 with_timeout(Duration::from_secs(TEST_DURATION as _), join(tx_fut, rx_fut))
264 .await
265 .ok();
266
267 let kbps = (total + 512) / 1024 / TEST_DURATION;
268 info!("upload+download: {} kB/s", kbps);
269 kbps
270}
diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs
index 7a94ea191..1ecaab266 100644
--- a/tests/rp/src/bin/cyw43-perf.rs
+++ b/tests/rp/src/bin/cyw43-perf.rs
@@ -44,8 +44,8 @@ async fn main(spawner: Spawner) {
44 } 44 }
45 45
46 // cyw43 firmware needs to be flashed manually: 46 // cyw43 firmware needs to be flashed manually:
47 // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 47 // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000
48 // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 48 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000
49 let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) }; 49 let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) };
50 let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) }; 50 let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) };
51 51
@@ -63,20 +63,13 @@ async fn main(spawner: Spawner) {
63 .set_power_management(cyw43::PowerManagementMode::PowerSave) 63 .set_power_management(cyw43::PowerManagementMode::PowerSave)
64 .await; 64 .await;
65 65
66 let config = Config::dhcpv4(Default::default());
67 //let config = embassy_net::Config::Static(embassy_net::Config {
68 // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),
69 // dns_servers: Vec::new(),
70 // gateway: Some(Ipv4Address::new(192, 168, 69, 1)),
71 //});
72
73 // Generate random seed 66 // Generate random seed
74 let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. 67 let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random.
75 68
76 // Init network stack 69 // Init network stack
77 let stack = &*make_static!(Stack::new( 70 let stack = &*make_static!(Stack::new(
78 net_device, 71 net_device,
79 config, 72 Config::dhcpv4(Default::default()),
80 make_static!(StackResources::<2>::new()), 73 make_static!(StackResources::<2>::new()),
81 seed 74 seed
82 )); 75 ));
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index fe646927a..c2422f7bc 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -46,6 +46,9 @@ rand_chacha = { version = "0.3", default-features = false }
46 46
47chrono = { version = "^0.4", default-features = false, optional = true} 47chrono = { version = "^0.4", default-features = false, optional = true}
48 48
49[patch.crates-io]
50stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"}
51
49# BEGIN TESTS 52# BEGIN TESTS
50# Generated by gen_test.py. DO NOT EDIT. 53# Generated by gen_test.py. DO NOT EDIT.
51[[bin]] 54[[bin]]
diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs
index f47a89b6f..af3832709 100644
--- a/tests/stm32/src/bin/tl_mbox.rs
+++ b/tests/stm32/src/bin/tl_mbox.rs
@@ -6,21 +6,33 @@
6#[path = "../common.rs"] 6#[path = "../common.rs"]
7mod common; 7mod common;
8 8
9use core::mem; 9use core::time::Duration;
10 10
11use common::*; 11use common::*;
12use embassy_executor::Spawner; 12use embassy_executor::Spawner;
13use embassy_futures::poll_once;
14use embassy_stm32::bind_interrupts; 13use embassy_stm32::bind_interrupts;
15use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; 14use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
16use embassy_stm32_wpan::{mm, TlMbox}; 15use embassy_stm32_wpan::hci::host::uart::UartHci;
17use embassy_time::{Duration, Timer}; 16use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType};
17use embassy_stm32_wpan::hci::types::AdvertisingType;
18use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{
19 AdvertisingDataType, DiscoverableParameters, GapCommands, Role,
20};
21use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::GattCommands;
22use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel};
23use embassy_stm32_wpan::hci::BdAddr;
24use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp;
25use embassy_stm32_wpan::sub::mm;
26use embassy_stm32_wpan::TlMbox;
27use {defmt_rtt as _, panic_probe as _};
18 28
19bind_interrupts!(struct Irqs{ 29bind_interrupts!(struct Irqs{
20 IPCC_C1_RX => ReceiveInterruptHandler; 30 IPCC_C1_RX => ReceiveInterruptHandler;
21 IPCC_C1_TX => TransmitInterruptHandler; 31 IPCC_C1_TX => TransmitInterruptHandler;
22}); 32});
23 33
34const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7;
35
24#[embassy_executor::task] 36#[embassy_executor::task]
25async fn run_mm_queue(memory_manager: mm::MemoryManager) { 37async fn run_mm_queue(memory_manager: mm::MemoryManager) {
26 memory_manager.run_queue().await; 38 memory_manager.run_queue().await;
@@ -32,17 +44,12 @@ async fn main(spawner: Spawner) {
32 info!("Hello World!"); 44 info!("Hello World!");
33 45
34 let config = Config::default(); 46 let config = Config::default();
35 let mbox = TlMbox::init(p.IPCC, Irqs, config); 47 let mut mbox = TlMbox::init(p.IPCC, Irqs, config);
36 48
37 spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); 49 spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap();
38 50
39 let ready_event = mbox.sys_subsystem.read().await; 51 let sys_event = mbox.sys_subsystem.read().await;
40 let _ = poll_once(mbox.sys_subsystem.read()); // clear rx not 52 info!("sys event: {}", sys_event.payload());
41
42 info!("sys event {:x} : {:x}", ready_event.stub().kind, ready_event.payload());
43
44 // test memory manager
45 mem::drop(ready_event);
46 53
47 let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap(); 54 let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap();
48 let version_major = fw_info.version_major(); 55 let version_major = fw_info.version_major();
@@ -57,19 +64,188 @@ async fn main(spawner: Spawner) {
57 version_major, version_minor, subversion, sram2a_size, sram2b_size 64 version_major, version_minor, subversion, sram2a_size, sram2b_size
58 ); 65 );
59 66
60 Timer::after(Duration::from_millis(50)).await; 67 mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
68
69 info!("resetting BLE...");
70 mbox.ble_subsystem.reset().await;
71 let response = mbox.ble_subsystem.read().await.unwrap();
72 info!("{}", response);
73
74 info!("config public address...");
75 mbox.ble_subsystem
76 .write_config_data(&ConfigData::public_address(get_bd_addr()).build())
77 .await;
78 let response = mbox.ble_subsystem.read().await.unwrap();
79 info!("{}", response);
80
81 info!("config random address...");
82 mbox.ble_subsystem
83 .write_config_data(&ConfigData::random_address(get_random_addr()).build())
84 .await;
85 let response = mbox.ble_subsystem.read().await.unwrap();
86 info!("{}", response);
87
88 info!("config identity root...");
89 mbox.ble_subsystem
90 .write_config_data(&ConfigData::identity_root(&get_irk()).build())
91 .await;
92 let response = mbox.ble_subsystem.read().await.unwrap();
93 info!("{}", response);
94
95 info!("config encryption root...");
96 mbox.ble_subsystem
97 .write_config_data(&ConfigData::encryption_root(&get_erk()).build())
98 .await;
99 let response = mbox.ble_subsystem.read().await.unwrap();
100 info!("{}", response);
101
102 info!("config tx power level...");
103 mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await;
104 let response = mbox.ble_subsystem.read().await.unwrap();
105 info!("{}", response);
106
107 info!("GATT init...");
108 mbox.ble_subsystem.init_gatt().await;
109 let response = mbox.ble_subsystem.read().await.unwrap();
110 info!("{}", response);
111
112 info!("GAP init...");
113 mbox.ble_subsystem
114 .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH)
115 .await;
116 let response = mbox.ble_subsystem.read().await.unwrap();
117 info!("{}", response);
118
119 // info!("set scan response...");
120 // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap();
121 // let response = mbox.ble_subsystem.read().await.unwrap();
122 // info!("{}", response);
123
124 info!("set discoverable...");
125 mbox.ble_subsystem
126 .set_discoverable(&DiscoverableParameters {
127 advertising_type: AdvertisingType::NonConnectableUndirected,
128 advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))),
129 address_type: OwnAddressType::Public,
130 filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan,
131 local_name: None,
132 advertising_data: &[],
133 conn_interval: (None, None),
134 })
135 .await
136 .unwrap();
137
138 let response = mbox.ble_subsystem.read().await;
139 info!("{}", response);
140
141 // remove some advertisement to decrease the packet size
142 info!("delete tx power ad type...");
143 mbox.ble_subsystem
144 .delete_ad_type(AdvertisingDataType::TxPowerLevel)
145 .await;
146 let response = mbox.ble_subsystem.read().await.unwrap();
147 info!("{}", response);
148
149 info!("delete conn interval ad type...");
150 mbox.ble_subsystem
151 .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval)
152 .await;
153 let response = mbox.ble_subsystem.read().await.unwrap();
154 info!("{}", response);
155
156 info!("update advertising data...");
157 mbox.ble_subsystem
158 .update_advertising_data(&eddystone_advertising_data())
159 .await
160 .unwrap();
161 let response = mbox.ble_subsystem.read().await.unwrap();
162 info!("{}", response);
163
164 info!("update advertising data type...");
165 mbox.ble_subsystem
166 .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe])
167 .await
168 .unwrap();
169 let response = mbox.ble_subsystem.read().await.unwrap();
170 info!("{}", response);
171
172 info!("update advertising data flags...");
173 mbox.ble_subsystem
174 .update_advertising_data(&[
175 2,
176 AdvertisingDataType::Flags as u8,
177 (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support
178 ])
179 .await
180 .unwrap();
181 let response = mbox.ble_subsystem.read().await.unwrap();
182 info!("{}", response);
183
184 info!("Test OK");
185 cortex_m::asm::bkpt();
186}
187
188fn get_bd_addr() -> BdAddr {
189 let mut bytes = [0u8; 6];
61 190
62 let result = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; 191 let lhci_info = LhciC1DeviceInformationCcrp::new();
63 info!("subsystem initialization: {}", result); 192 bytes[0] = (lhci_info.uid64 & 0xff) as u8;
193 bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;
194 bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;
195 bytes[3] = lhci_info.device_type_id;
196 bytes[4] = (lhci_info.st_company_id & 0xff) as u8;
197 bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8;
64 198
65 info!("starting ble..."); 199 BdAddr(bytes)
66 mbox.ble_subsystem.write(0x0c, &[]).await; 200}
67 201
68 info!("waiting for ble..."); 202fn get_random_addr() -> BdAddr {
69 let ble_event = mbox.ble_subsystem.read().await; 203 let mut bytes = [0u8; 6];
70 204
71 info!("ble event {:x} : {:x}", ble_event.stub().kind, ble_event.payload()); 205 let lhci_info = LhciC1DeviceInformationCcrp::new();
206 bytes[0] = (lhci_info.uid64 & 0xff) as u8;
207 bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8;
208 bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8;
209 bytes[3] = 0;
210 bytes[4] = 0x6E;
211 bytes[5] = 0xED;
72 212
73 info!("Test OK"); 213 BdAddr(bytes)
74 cortex_m::asm::bkpt(); 214}
215
216const BLE_CFG_IRK: [u8; 16] = [
217 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0,
218];
219const BLE_CFG_ERK: [u8; 16] = [
220 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21,
221];
222
223fn get_irk() -> EncryptionKey {
224 EncryptionKey(BLE_CFG_IRK)
225}
226
227fn get_erk() -> EncryptionKey {
228 EncryptionKey(BLE_CFG_ERK)
229}
230
231fn eddystone_advertising_data() -> [u8; 24] {
232 const EDDYSTONE_URL: &[u8] = b"www.rust-lang.com";
233
234 let mut service_data = [0u8; 24];
235 let url_len = EDDYSTONE_URL.len();
236
237 service_data[0] = 6 + url_len as u8;
238 service_data[1] = AdvertisingDataType::ServiceData as u8;
239
240 // 16-bit eddystone uuid
241 service_data[2] = 0xaa;
242 service_data[3] = 0xFE;
243
244 service_data[4] = 0x10; // URL frame type
245 service_data[5] = 22_i8 as u8; // calibrated TX power at 0m
246 service_data[6] = 0x03; // eddystone url prefix = https
247
248 service_data[7..(7 + url_len)].copy_from_slice(EDDYSTONE_URL);
249
250 service_data
75} 251}