aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-05-29 15:07:21 -0500
committerxoviat <[email protected]>2023-05-29 15:07:21 -0500
commit68441a74c2da3d3186ca351f0b3d263940564a16 (patch)
treee5d40a357c49accd1b774a2b401d79b03ecf6075
parentaba0f8fd6cd51cad65480689bc9254df4f071175 (diff)
parent3b38079490b0c283899cab42308c4feab4c47fdc (diff)
Merge branch 'main' of https://github.com/embassy-rs/embassy into uart
-rwxr-xr-x.github/ci/build-stable.sh16
-rwxr-xr-x.github/ci/build.sh18
-rwxr-xr-x.github/ci/test.sh28
-rw-r--r--.github/workflows/doc.yml2
-rw-r--r--.github/workflows/rust.yml80
-rwxr-xr-xci.sh1
-rwxr-xr-xci_stable.sh3
-rw-r--r--embassy-boot/boot/src/firmware_updater.rs543
-rw-r--r--embassy-boot/boot/src/firmware_updater/asynch.rs251
-rw-r--r--embassy-boot/boot/src/firmware_updater/blocking.rs221
-rw-r--r--embassy-boot/boot/src/firmware_updater/mod.rs71
-rw-r--r--embassy-embedded-hal/src/shared_bus/blocking/i2c.rs5
-rw-r--r--embassy-embedded-hal/src/shared_bus/blocking/spi.rs5
-rw-r--r--embassy-hal-common/src/atomic_ring_buffer.rs2
-rw-r--r--embassy-nrf/src/time_driver.rs2
-rw-r--r--embassy-stm32/build.rs10
-rw-r--r--embassy-stm32/src/flash/common.rs2
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/pwm/complementary_pwm.rs14
-rw-r--r--embassy-stm32/src/tl_mbox/ble.rs20
-rw-r--r--embassy-stm32/src/tl_mbox/channels.rs4
-rw-r--r--embassy-stm32/src/tl_mbox/evt.rs7
-rw-r--r--embassy-stm32/src/tl_mbox/ipcc.rs (renamed from embassy-stm32/src/ipcc.rs)70
-rw-r--r--embassy-stm32/src/tl_mbox/mm.rs24
-rw-r--r--embassy-stm32/src/tl_mbox/mod.rs134
-rw-r--r--embassy-stm32/src/tl_mbox/shci.rs8
-rw-r--r--embassy-stm32/src/tl_mbox/sys.rs28
-rw-r--r--embassy-stm32/tl_mbox.x.in (renamed from tests/stm32/memory_ble.x)14
-rw-r--r--embassy-time/Cargo.toml2
-rw-r--r--embassy-time/src/driver.rs2
-rw-r--r--embassy-time/src/queue_generic.rs15
-rw-r--r--embassy-time/src/timer.rs1
-rw-r--r--examples/stm32wb/Cargo.toml2
-rw-r--r--examples/stm32wb/build.rs36
-rw-r--r--examples/stm32wb/memory.x35
-rw-r--r--examples/stm32wb/src/bin/tl_mbox.rs14
-rw-r--r--examples/stm32wb/src/bin/tl_mbox_tx_rx.rs23
-rw-r--r--tests/stm32/.cargo/config.toml2
-rw-r--r--tests/stm32/Cargo.toml8
-rw-r--r--tests/stm32/build.rs13
-rw-r--r--tests/stm32/src/bin/tl_mbox.rs (renamed from tests/stm32/src/bin/ble.rs)17
41 files changed, 825 insertions, 930 deletions
diff --git a/.github/ci/build-stable.sh b/.github/ci/build-stable.sh
new file mode 100755
index 000000000..0dadd6102
--- /dev/null
+++ b/.github/ci/build-stable.sh
@@ -0,0 +1,16 @@
1#!/bin/bash
2## on push branch~=gh-readonly-queue/main/.*
3## on pull_request
4
5set -euo pipefail
6
7export RUSTUP_HOME=/ci/cache/rustup
8export CARGO_HOME=/ci/cache/cargo
9export CARGO_TARGET_DIR=/ci/cache/target
10
11hashtime restore /ci/cache/filetime.json || true
12hashtime save /ci/cache/filetime.json
13
14sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
15
16./ci_stable.sh
diff --git a/.github/ci/build.sh b/.github/ci/build.sh
new file mode 100755
index 000000000..1c3a7f3b0
--- /dev/null
+++ b/.github/ci/build.sh
@@ -0,0 +1,18 @@
1#!/bin/bash
2## on push branch~=gh-readonly-queue/main/.*
3## on pull_request
4
5set -euo pipefail
6
7export RUSTUP_HOME=/ci/cache/rustup
8export CARGO_HOME=/ci/cache/cargo
9export CARGO_TARGET_DIR=/ci/cache/target
10if [ -f /ci/secrets/teleprobe-token.txt ]; then
11 echo Got teleprobe token!
12 export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt)
13fi
14
15hashtime restore /ci/cache/filetime.json || true
16hashtime save /ci/cache/filetime.json
17
18./ci.sh
diff --git a/.github/ci/test.sh b/.github/ci/test.sh
new file mode 100755
index 000000000..a7140cfd9
--- /dev/null
+++ b/.github/ci/test.sh
@@ -0,0 +1,28 @@
1#!/bin/bash
2## on push branch~=gh-readonly-queue/main/.*
3## on pull_request
4
5set -euo pipefail
6
7export RUSTUP_HOME=/ci/cache/rustup
8export CARGO_HOME=/ci/cache/cargo
9export CARGO_TARGET_DIR=/ci/cache/target
10
11hashtime restore /ci/cache/filetime.json || true
12hashtime save /ci/cache/filetime.json
13
14cargo test --manifest-path ./embassy-sync/Cargo.toml
15cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
16cargo test --manifest-path ./embassy-hal-common/Cargo.toml
17cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
18
19cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
20cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly
21cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-dalek
22cargo test --manifest-path ./embassy-boot/boot/Cargo.toml --features nightly,ed25519-salty
23
24#cargo test --manifest-path ./embassy-nrf/Cargo.toml --no-default-features --features nightly,nrf52840,time-driver-rtc1 ## broken doctests
25
26cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
27cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti
28cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index b4e225e64..a69a49718 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -2,7 +2,7 @@ name: Docs
2 2
3on: 3on:
4 push: 4 push:
5 branches: [master] 5 branches: [main]
6 6
7env: 7env:
8 BUILDER_THREADS: '1' 8 BUILDER_THREADS: '1'
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
deleted file mode 100644
index 0cbca31b8..000000000
--- a/.github/workflows/rust.yml
+++ /dev/null
@@ -1,80 +0,0 @@
1name: Rust
2
3on:
4 push:
5 branches: [staging, trying, master]
6 pull_request:
7 branches: [master]
8
9env:
10 CARGO_TERM_COLOR: always
11
12jobs:
13 all:
14 runs-on: ubuntu-latest
15 needs: [build-nightly, build-stable, test]
16 steps:
17 - name: Done
18 run: exit 0
19 build-nightly:
20 runs-on: ubuntu-latest
21 permissions:
22 id-token: write
23 contents: read
24 steps:
25 - uses: actions/checkout@v3
26 with:
27 submodules: true
28 - name: Cache multiple paths
29 uses: actions/cache@v3
30 with:
31 path: |
32 ~/.cargo/bin/
33 ~/.cargo/registry/index/
34 ~/.cargo/registry/cache/
35 ~/.cargo/git/db/
36 target_ci
37 key: rust3-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }}
38 - name: build
39 env:
40 TELEPROBE_TOKEN: ${{ secrets.TELEPROBE_TOKEN }}
41 run: |
42 curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch
43 chmod +x /usr/local/bin/cargo-batch
44 ./ci.sh
45 rm -rf target_ci/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}*
46 build-stable:
47 runs-on: ubuntu-latest
48 steps:
49 - uses: actions/checkout@v3
50 with:
51 submodules: true
52 - name: Cache multiple paths
53 uses: actions/cache@v3
54 with:
55 path: |
56 ~/.cargo/bin/
57 ~/.cargo/registry/index/
58 ~/.cargo/registry/cache/
59 ~/.cargo/git/db/
60 target_ci_stable
61 key: rust-stable-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }}
62 - name: build
63 run: |
64 curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch
65 chmod +x /usr/local/bin/cargo-batch
66 ./ci_stable.sh
67 rm -rf target_ci_stable/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}*
68
69 test:
70 runs-on: ubuntu-latest
71 steps:
72 - uses: actions/checkout@v3
73
74 - name: Test boot
75 working-directory: ./embassy-boot/boot
76 run: cargo test && cargo test --features nightly && cargo test --features "ed25519-dalek,nightly" && cargo test --features "ed25519-salty,nightly"
77
78 - name: Test sync
79 working-directory: ./embassy-sync
80 run: cargo test
diff --git a/ci.sh b/ci.sh
index 6d906f5f9..11c569328 100755
--- a/ci.sh
+++ b/ci.sh
@@ -2,7 +2,6 @@
2 2
3set -euo pipefail 3set -euo pipefail
4 4
5export CARGO_TARGET_DIR=$PWD/target_ci
6export RUSTFLAGS=-Dwarnings 5export RUSTFLAGS=-Dwarnings
7export DEFMT_LOG=trace 6export DEFMT_LOG=trace
8 7
diff --git a/ci_stable.sh b/ci_stable.sh
index 55a2f89a0..a67087ffb 100755
--- a/ci_stable.sh
+++ b/ci_stable.sh
@@ -2,12 +2,9 @@
2 2
3set -euo pipefail 3set -euo pipefail
4 4
5export CARGO_TARGET_DIR=$PWD/target_ci_stable
6export RUSTFLAGS=-Dwarnings 5export RUSTFLAGS=-Dwarnings
7export DEFMT_LOG=trace 6export DEFMT_LOG=trace
8 7
9sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
10
11cargo batch \ 8cargo batch \
12 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ 9 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
13 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ 10 --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
diff --git a/embassy-boot/boot/src/firmware_updater.rs b/embassy-boot/boot/src/firmware_updater.rs
deleted file mode 100644
index aeea206f9..000000000
--- a/embassy-boot/boot/src/firmware_updater.rs
+++ /dev/null
@@ -1,543 +0,0 @@
1use digest::Digest;
2use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
3#[cfg(feature = "nightly")]
4use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
5
6use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
7
8/// Errors returned by FirmwareUpdater
9#[derive(Debug)]
10pub enum FirmwareUpdaterError {
11 /// Error from flash.
12 Flash(NorFlashErrorKind),
13 /// Signature errors.
14 Signature(signature::Error),
15}
16
17#[cfg(feature = "defmt")]
18impl defmt::Format for FirmwareUpdaterError {
19 fn format(&self, fmt: defmt::Formatter) {
20 match self {
21 FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"),
22 FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"),
23 }
24 }
25}
26
27impl<E> From<E> for FirmwareUpdaterError
28where
29 E: NorFlashError,
30{
31 fn from(error: E) -> Self {
32 FirmwareUpdaterError::Flash(error.kind())
33 }
34}
35
36/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
37/// 'mess up' the internal bootloader state
38pub struct FirmwareUpdater {
39 state: Partition,
40 dfu: Partition,
41}
42
43#[cfg(target_os = "none")]
44impl Default for FirmwareUpdater {
45 fn default() -> Self {
46 extern "C" {
47 static __bootloader_state_start: u32;
48 static __bootloader_state_end: u32;
49 static __bootloader_dfu_start: u32;
50 static __bootloader_dfu_end: u32;
51 }
52
53 let dfu = unsafe {
54 Partition::new(
55 &__bootloader_dfu_start as *const u32 as u32,
56 &__bootloader_dfu_end as *const u32 as u32,
57 )
58 };
59 let state = unsafe {
60 Partition::new(
61 &__bootloader_state_start as *const u32 as u32,
62 &__bootloader_state_end as *const u32 as u32,
63 )
64 };
65
66 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
67 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
68 FirmwareUpdater::new(dfu, state)
69 }
70}
71
72impl FirmwareUpdater {
73 /// Create a firmware updater instance with partition ranges for the update and state partitions.
74 pub const fn new(dfu: Partition, state: Partition) -> Self {
75 Self { dfu, state }
76 }
77
78 /// Obtain the current state.
79 ///
80 /// This is useful to check if the bootloader has just done a swap, in order
81 /// to do verifications and self-tests of the new image before calling
82 /// `mark_booted`.
83 #[cfg(feature = "nightly")]
84 pub async fn get_state<F: AsyncNorFlash>(
85 &mut self,
86 state_flash: &mut F,
87 aligned: &mut [u8],
88 ) -> Result<State, FirmwareUpdaterError> {
89 self.state.read(state_flash, 0, aligned).await?;
90
91 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
92 Ok(State::Swap)
93 } else {
94 Ok(State::Boot)
95 }
96 }
97
98 /// Verify the DFU given a public key. If there is an error then DO NOT
99 /// proceed with updating the firmware as it must be signed with a
100 /// corresponding private key (otherwise it could be malicious firmware).
101 ///
102 /// Mark to trigger firmware swap on next boot if verify suceeds.
103 ///
104 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
105 /// been generated from a SHA-512 digest of the firmware bytes.
106 ///
107 /// If no signature feature is set then this method will always return a
108 /// signature error.
109 ///
110 /// # Safety
111 ///
112 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
113 /// and written to.
114 #[cfg(all(feature = "_verify", feature = "nightly"))]
115 pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
116 &mut self,
117 _state_and_dfu_flash: &mut F,
118 _public_key: &[u8],
119 _signature: &[u8],
120 _update_len: u32,
121 _aligned: &mut [u8],
122 ) -> Result<(), FirmwareUpdaterError> {
123 assert_eq!(_aligned.len(), F::WRITE_SIZE);
124 assert!(_update_len <= self.dfu.size());
125
126 #[cfg(feature = "ed25519-dalek")]
127 {
128 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
129
130 use crate::digest_adapters::ed25519_dalek::Sha512;
131
132 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
133
134 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
135 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
136
137 let mut message = [0; 64];
138 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
139 .await?;
140
141 public_key.verify(&message, &signature).map_err(into_signature_error)?
142 }
143 #[cfg(feature = "ed25519-salty")]
144 {
145 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
146 use salty::{PublicKey, Signature};
147
148 use crate::digest_adapters::salty::Sha512;
149
150 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
151 FirmwareUpdaterError::Signature(signature::Error::default())
152 }
153
154 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
155 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
156 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
157 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
158
159 let mut message = [0; 64];
160 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
161 .await?;
162
163 let r = public_key.verify(&message, &signature);
164 trace!(
165 "Verifying with public key {}, signature {} and message {} yields ok: {}",
166 public_key.to_bytes(),
167 signature.to_bytes(),
168 message,
169 r.is_ok()
170 );
171 r.map_err(into_signature_error)?
172 }
173
174 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await
175 }
176
177 /// Verify the update in DFU with any digest.
178 #[cfg(feature = "nightly")]
179 pub async fn hash<F: AsyncNorFlash, D: Digest>(
180 &mut self,
181 dfu_flash: &mut F,
182 update_len: u32,
183 chunk_buf: &mut [u8],
184 output: &mut [u8],
185 ) -> Result<(), FirmwareUpdaterError> {
186 let mut digest = D::new();
187 for offset in (0..update_len).step_by(chunk_buf.len()) {
188 self.dfu.read(dfu_flash, offset, chunk_buf).await?;
189 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
190 digest.update(&chunk_buf[..len]);
191 }
192 output.copy_from_slice(digest.finalize().as_slice());
193 Ok(())
194 }
195
196 /// Mark to trigger firmware swap on next boot.
197 ///
198 /// # Safety
199 ///
200 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
201 #[cfg(all(feature = "nightly", not(feature = "_verify")))]
202 pub async fn mark_updated<F: AsyncNorFlash>(
203 &mut self,
204 state_flash: &mut F,
205 aligned: &mut [u8],
206 ) -> Result<(), FirmwareUpdaterError> {
207 assert_eq!(aligned.len(), F::WRITE_SIZE);
208 self.set_magic(aligned, SWAP_MAGIC, state_flash).await
209 }
210
211 /// Mark firmware boot successful and stop rollback on reset.
212 ///
213 /// # Safety
214 ///
215 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
216 #[cfg(feature = "nightly")]
217 pub async fn mark_booted<F: AsyncNorFlash>(
218 &mut self,
219 state_flash: &mut F,
220 aligned: &mut [u8],
221 ) -> Result<(), FirmwareUpdaterError> {
222 assert_eq!(aligned.len(), F::WRITE_SIZE);
223 self.set_magic(aligned, BOOT_MAGIC, state_flash).await
224 }
225
226 #[cfg(feature = "nightly")]
227 async fn set_magic<F: AsyncNorFlash>(
228 &mut self,
229 aligned: &mut [u8],
230 magic: u8,
231 state_flash: &mut F,
232 ) -> Result<(), FirmwareUpdaterError> {
233 self.state.read(state_flash, 0, aligned).await?;
234
235 if aligned.iter().any(|&b| b != magic) {
236 // Read progress validity
237 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
238
239 // FIXME: Do not make this assumption.
240 const STATE_ERASE_VALUE: u8 = 0xFF;
241
242 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
243 // The current progress validity marker is invalid
244 } else {
245 // Invalidate progress
246 aligned.fill(!STATE_ERASE_VALUE);
247 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
248 }
249
250 // Clear magic and progress
251 self.state.wipe(state_flash).await?;
252
253 // Set magic
254 aligned.fill(magic);
255 self.state.write(state_flash, 0, aligned).await?;
256 }
257 Ok(())
258 }
259
260 /// Write data to a flash page.
261 ///
262 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
263 ///
264 /// # Safety
265 ///
266 /// Failing to meet alignment and size requirements may result in a panic.
267 #[cfg(feature = "nightly")]
268 pub async fn write_firmware<F: AsyncNorFlash>(
269 &mut self,
270 offset: usize,
271 data: &[u8],
272 dfu_flash: &mut F,
273 ) -> Result<(), FirmwareUpdaterError> {
274 assert!(data.len() >= F::ERASE_SIZE);
275
276 self.dfu
277 .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
278 .await?;
279
280 self.dfu.write(dfu_flash, offset as u32, data).await?;
281
282 Ok(())
283 }
284
285 /// Prepare for an incoming DFU update by erasing the entire DFU area and
286 /// returning its `Partition`.
287 ///
288 /// Using this instead of `write_firmware` allows for an optimized API in
289 /// exchange for added complexity.
290 #[cfg(feature = "nightly")]
291 pub async fn prepare_update<F: AsyncNorFlash>(
292 &mut self,
293 dfu_flash: &mut F,
294 ) -> Result<Partition, FirmwareUpdaterError> {
295 self.dfu.wipe(dfu_flash).await?;
296
297 Ok(self.dfu)
298 }
299
300 //
301 // Blocking API
302 //
303
304 /// Obtain the current state.
305 ///
306 /// This is useful to check if the bootloader has just done a swap, in order
307 /// to do verifications and self-tests of the new image before calling
308 /// `mark_booted`.
309 pub fn get_state_blocking<F: NorFlash>(
310 &mut self,
311 state_flash: &mut F,
312 aligned: &mut [u8],
313 ) -> Result<State, FirmwareUpdaterError> {
314 self.state.read_blocking(state_flash, 0, aligned)?;
315
316 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
317 Ok(State::Swap)
318 } else {
319 Ok(State::Boot)
320 }
321 }
322
323 /// Verify the DFU given a public key. If there is an error then DO NOT
324 /// proceed with updating the firmware as it must be signed with a
325 /// corresponding private key (otherwise it could be malicious firmware).
326 ///
327 /// Mark to trigger firmware swap on next boot if verify suceeds.
328 ///
329 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
330 /// been generated from a SHA-512 digest of the firmware bytes.
331 ///
332 /// If no signature feature is set then this method will always return a
333 /// signature error.
334 ///
335 /// # Safety
336 ///
337 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
338 /// and written to.
339 #[cfg(feature = "_verify")]
340 pub fn verify_and_mark_updated_blocking<F: NorFlash>(
341 &mut self,
342 _state_and_dfu_flash: &mut F,
343 _public_key: &[u8],
344 _signature: &[u8],
345 _update_len: u32,
346 _aligned: &mut [u8],
347 ) -> Result<(), FirmwareUpdaterError> {
348 assert_eq!(_aligned.len(), F::WRITE_SIZE);
349 assert!(_update_len <= self.dfu.size());
350
351 #[cfg(feature = "ed25519-dalek")]
352 {
353 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
354
355 use crate::digest_adapters::ed25519_dalek::Sha512;
356
357 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
358
359 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
360 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
361
362 let mut message = [0; 64];
363 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
364
365 public_key.verify(&message, &signature).map_err(into_signature_error)?
366 }
367 #[cfg(feature = "ed25519-salty")]
368 {
369 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
370 use salty::{PublicKey, Signature};
371
372 use crate::digest_adapters::salty::Sha512;
373
374 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
375 FirmwareUpdaterError::Signature(signature::Error::default())
376 }
377
378 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
379 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
380 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
381 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
382
383 let mut message = [0; 64];
384 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
385
386 let r = public_key.verify(&message, &signature);
387 trace!(
388 "Verifying with public key {}, signature {} and message {} yields ok: {}",
389 public_key.to_bytes(),
390 signature.to_bytes(),
391 message,
392 r.is_ok()
393 );
394 r.map_err(into_signature_error)?
395 }
396
397 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash)
398 }
399
400 /// Verify the update in DFU with any digest.
401 pub fn hash_blocking<F: NorFlash, D: Digest>(
402 &mut self,
403 dfu_flash: &mut F,
404 update_len: u32,
405 chunk_buf: &mut [u8],
406 output: &mut [u8],
407 ) -> Result<(), FirmwareUpdaterError> {
408 let mut digest = D::new();
409 for offset in (0..update_len).step_by(chunk_buf.len()) {
410 self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?;
411 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
412 digest.update(&chunk_buf[..len]);
413 }
414 output.copy_from_slice(digest.finalize().as_slice());
415 Ok(())
416 }
417
418 /// Mark to trigger firmware swap on next boot.
419 ///
420 /// # Safety
421 ///
422 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
423 #[cfg(not(feature = "_verify"))]
424 pub fn mark_updated_blocking<F: NorFlash>(
425 &mut self,
426 state_flash: &mut F,
427 aligned: &mut [u8],
428 ) -> Result<(), FirmwareUpdaterError> {
429 assert_eq!(aligned.len(), F::WRITE_SIZE);
430 self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
431 }
432
433 /// Mark firmware boot successful and stop rollback on reset.
434 ///
435 /// # Safety
436 ///
437 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
438 pub fn mark_booted_blocking<F: NorFlash>(
439 &mut self,
440 state_flash: &mut F,
441 aligned: &mut [u8],
442 ) -> Result<(), FirmwareUpdaterError> {
443 assert_eq!(aligned.len(), F::WRITE_SIZE);
444 self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
445 }
446
447 fn set_magic_blocking<F: NorFlash>(
448 &mut self,
449 aligned: &mut [u8],
450 magic: u8,
451 state_flash: &mut F,
452 ) -> Result<(), FirmwareUpdaterError> {
453 self.state.read_blocking(state_flash, 0, aligned)?;
454
455 if aligned.iter().any(|&b| b != magic) {
456 // Read progress validity
457 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
458
459 // FIXME: Do not make this assumption.
460 const STATE_ERASE_VALUE: u8 = 0xFF;
461
462 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
463 // The current progress validity marker is invalid
464 } else {
465 // Invalidate progress
466 aligned.fill(!STATE_ERASE_VALUE);
467 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
468 }
469
470 // Clear magic and progress
471 self.state.wipe_blocking(state_flash)?;
472
473 // Set magic
474 aligned.fill(magic);
475 self.state.write_blocking(state_flash, 0, aligned)?;
476 }
477 Ok(())
478 }
479
480 /// Write data to a flash page.
481 ///
482 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
483 ///
484 /// # Safety
485 ///
486 /// Failing to meet alignment and size requirements may result in a panic.
487 pub fn write_firmware_blocking<F: NorFlash>(
488 &mut self,
489 offset: usize,
490 data: &[u8],
491 dfu_flash: &mut F,
492 ) -> Result<(), FirmwareUpdaterError> {
493 assert!(data.len() >= F::ERASE_SIZE);
494
495 self.dfu
496 .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
497
498 self.dfu.write_blocking(dfu_flash, offset as u32, data)?;
499
500 Ok(())
501 }
502
503 /// Prepare for an incoming DFU update by erasing the entire DFU area and
504 /// returning its `Partition`.
505 ///
506 /// Using this instead of `write_firmware_blocking` allows for an optimized
507 /// API in exchange for added complexity.
508 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> {
509 self.dfu.wipe_blocking(flash)?;
510
511 Ok(self.dfu)
512 }
513}
514
515#[cfg(test)]
516mod tests {
517 use futures::executor::block_on;
518 use sha1::{Digest, Sha1};
519
520 use super::*;
521 use crate::mem_flash::MemFlash;
522
523 #[test]
524 #[cfg(feature = "nightly")]
525 fn can_verify_sha1() {
526 const STATE: Partition = Partition::new(0, 4096);
527 const DFU: Partition = Partition::new(65536, 131072);
528
529 let mut flash = MemFlash::<131072, 4096, 8>::default();
530
531 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
532 let mut to_write = [0; 4096];
533 to_write[..7].copy_from_slice(update.as_slice());
534
535 let mut updater = FirmwareUpdater::new(DFU, STATE);
536 block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap();
537 let mut chunk_buf = [0; 2];
538 let mut hash = [0; 20];
539 block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
540
541 assert_eq!(Sha1::digest(update).as_slice(), hash);
542 }
543}
diff --git a/embassy-boot/boot/src/firmware_updater/asynch.rs b/embassy-boot/boot/src/firmware_updater/asynch.rs
new file mode 100644
index 000000000..bdd03bff4
--- /dev/null
+++ b/embassy-boot/boot/src/firmware_updater/asynch.rs
@@ -0,0 +1,251 @@
1use digest::Digest;
2use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
3
4use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC};
5
6impl FirmwareUpdater {
7 /// Obtain the current state.
8 ///
9 /// This is useful to check if the bootloader has just done a swap, in order
10 /// to do verifications and self-tests of the new image before calling
11 /// `mark_booted`.
12 pub async fn get_state<F: AsyncNorFlash>(
13 &mut self,
14 state_flash: &mut F,
15 aligned: &mut [u8],
16 ) -> Result<State, FirmwareUpdaterError> {
17 self.state.read(state_flash, 0, aligned).await?;
18
19 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
20 Ok(State::Swap)
21 } else {
22 Ok(State::Boot)
23 }
24 }
25
26 /// Verify the DFU given a public key. If there is an error then DO NOT
27 /// proceed with updating the firmware as it must be signed with a
28 /// corresponding private key (otherwise it could be malicious firmware).
29 ///
30 /// Mark to trigger firmware swap on next boot if verify suceeds.
31 ///
32 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
33 /// been generated from a SHA-512 digest of the firmware bytes.
34 ///
35 /// If no signature feature is set then this method will always return a
36 /// signature error.
37 ///
38 /// # Safety
39 ///
40 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
41 /// and written to.
42 #[cfg(all(feature = "_verify", feature = "nightly"))]
43 pub async fn verify_and_mark_updated<F: AsyncNorFlash>(
44 &mut self,
45 _state_and_dfu_flash: &mut F,
46 _public_key: &[u8],
47 _signature: &[u8],
48 _update_len: u32,
49 _aligned: &mut [u8],
50 ) -> Result<(), FirmwareUpdaterError> {
51 assert_eq!(_aligned.len(), F::WRITE_SIZE);
52 assert!(_update_len <= self.dfu.size());
53
54 #[cfg(feature = "ed25519-dalek")]
55 {
56 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
57
58 use crate::digest_adapters::ed25519_dalek::Sha512;
59
60 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
61
62 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
63 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
64
65 let mut message = [0; 64];
66 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
67 .await?;
68
69 public_key.verify(&message, &signature).map_err(into_signature_error)?
70 }
71 #[cfg(feature = "ed25519-salty")]
72 {
73 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
74 use salty::{PublicKey, Signature};
75
76 use crate::digest_adapters::salty::Sha512;
77
78 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
79 FirmwareUpdaterError::Signature(signature::Error::default())
80 }
81
82 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
83 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
84 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
85 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
86
87 let mut message = [0; 64];
88 self.hash::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)
89 .await?;
90
91 let r = public_key.verify(&message, &signature);
92 trace!(
93 "Verifying with public key {}, signature {} and message {} yields ok: {}",
94 public_key.to_bytes(),
95 signature.to_bytes(),
96 message,
97 r.is_ok()
98 );
99 r.map_err(into_signature_error)?
100 }
101
102 self.set_magic(_aligned, SWAP_MAGIC, _state_and_dfu_flash).await
103 }
104
105 /// Verify the update in DFU with any digest.
106 pub async fn hash<F: AsyncNorFlash, D: Digest>(
107 &mut self,
108 dfu_flash: &mut F,
109 update_len: u32,
110 chunk_buf: &mut [u8],
111 output: &mut [u8],
112 ) -> Result<(), FirmwareUpdaterError> {
113 let mut digest = D::new();
114 for offset in (0..update_len).step_by(chunk_buf.len()) {
115 self.dfu.read(dfu_flash, offset, chunk_buf).await?;
116 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
117 digest.update(&chunk_buf[..len]);
118 }
119 output.copy_from_slice(digest.finalize().as_slice());
120 Ok(())
121 }
122
123 /// Mark to trigger firmware swap on next boot.
124 ///
125 /// # Safety
126 ///
127 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
128 #[cfg(all(feature = "nightly", not(feature = "_verify")))]
129 pub async fn mark_updated<F: AsyncNorFlash>(
130 &mut self,
131 state_flash: &mut F,
132 aligned: &mut [u8],
133 ) -> Result<(), FirmwareUpdaterError> {
134 assert_eq!(aligned.len(), F::WRITE_SIZE);
135 self.set_magic(aligned, SWAP_MAGIC, state_flash).await
136 }
137
138 /// Mark firmware boot successful and stop rollback on reset.
139 ///
140 /// # Safety
141 ///
142 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
143 pub async fn mark_booted<F: AsyncNorFlash>(
144 &mut self,
145 state_flash: &mut F,
146 aligned: &mut [u8],
147 ) -> Result<(), FirmwareUpdaterError> {
148 assert_eq!(aligned.len(), F::WRITE_SIZE);
149 self.set_magic(aligned, BOOT_MAGIC, state_flash).await
150 }
151
152 async fn set_magic<F: AsyncNorFlash>(
153 &mut self,
154 aligned: &mut [u8],
155 magic: u8,
156 state_flash: &mut F,
157 ) -> Result<(), FirmwareUpdaterError> {
158 self.state.read(state_flash, 0, aligned).await?;
159
160 if aligned.iter().any(|&b| b != magic) {
161 // Read progress validity
162 self.state.read(state_flash, F::WRITE_SIZE as u32, aligned).await?;
163
164 // FIXME: Do not make this assumption.
165 const STATE_ERASE_VALUE: u8 = 0xFF;
166
167 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
168 // The current progress validity marker is invalid
169 } else {
170 // Invalidate progress
171 aligned.fill(!STATE_ERASE_VALUE);
172 self.state.write(state_flash, F::WRITE_SIZE as u32, aligned).await?;
173 }
174
175 // Clear magic and progress
176 self.state.wipe(state_flash).await?;
177
178 // Set magic
179 aligned.fill(magic);
180 self.state.write(state_flash, 0, aligned).await?;
181 }
182 Ok(())
183 }
184
185 /// Write data to a flash page.
186 ///
187 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
188 ///
189 /// # Safety
190 ///
191 /// Failing to meet alignment and size requirements may result in a panic.
192 pub async fn write_firmware<F: AsyncNorFlash>(
193 &mut self,
194 offset: usize,
195 data: &[u8],
196 dfu_flash: &mut F,
197 ) -> Result<(), FirmwareUpdaterError> {
198 assert!(data.len() >= F::ERASE_SIZE);
199
200 self.dfu
201 .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
202 .await?;
203
204 self.dfu.write(dfu_flash, offset as u32, data).await?;
205
206 Ok(())
207 }
208
209 /// Prepare for an incoming DFU update by erasing the entire DFU area and
210 /// returning its `Partition`.
211 ///
212 /// Using this instead of `write_firmware` allows for an optimized API in
213 /// exchange for added complexity.
214 pub async fn prepare_update<F: AsyncNorFlash>(
215 &mut self,
216 dfu_flash: &mut F,
217 ) -> Result<Partition, FirmwareUpdaterError> {
218 self.dfu.wipe(dfu_flash).await?;
219
220 Ok(self.dfu)
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use futures::executor::block_on;
227 use sha1::{Digest, Sha1};
228
229 use super::*;
230 use crate::mem_flash::MemFlash;
231
232 #[test]
233 fn can_verify_sha1() {
234 const STATE: Partition = Partition::new(0, 4096);
235 const DFU: Partition = Partition::new(65536, 131072);
236
237 let mut flash = MemFlash::<131072, 4096, 8>::default();
238
239 let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
240 let mut to_write = [0; 4096];
241 to_write[..7].copy_from_slice(update.as_slice());
242
243 let mut updater = FirmwareUpdater::new(DFU, STATE);
244 block_on(updater.write_firmware(0, to_write.as_slice(), &mut flash)).unwrap();
245 let mut chunk_buf = [0; 2];
246 let mut hash = [0; 20];
247 block_on(updater.hash::<_, Sha1>(&mut flash, update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
248
249 assert_eq!(Sha1::digest(update).as_slice(), hash);
250 }
251}
diff --git a/embassy-boot/boot/src/firmware_updater/blocking.rs b/embassy-boot/boot/src/firmware_updater/blocking.rs
new file mode 100644
index 000000000..50caaf08c
--- /dev/null
+++ b/embassy-boot/boot/src/firmware_updater/blocking.rs
@@ -0,0 +1,221 @@
1use digest::Digest;
2use embedded_storage::nor_flash::NorFlash;
3
4use crate::{FirmwareUpdater, FirmwareUpdaterError, Partition, State, BOOT_MAGIC, SWAP_MAGIC};
5
6impl FirmwareUpdater {
7 /// Create a firmware updater instance with partition ranges for the update and state partitions.
8 pub const fn new(dfu: Partition, state: Partition) -> Self {
9 Self { dfu, state }
10 }
11
12 /// Obtain the current state.
13 ///
14 /// This is useful to check if the bootloader has just done a swap, in order
15 /// to do verifications and self-tests of the new image before calling
16 /// `mark_booted`.
17 pub fn get_state_blocking<F: NorFlash>(
18 &mut self,
19 state_flash: &mut F,
20 aligned: &mut [u8],
21 ) -> Result<State, FirmwareUpdaterError> {
22 self.state.read_blocking(state_flash, 0, aligned)?;
23
24 if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
25 Ok(State::Swap)
26 } else {
27 Ok(State::Boot)
28 }
29 }
30
31 /// Verify the DFU given a public key. If there is an error then DO NOT
32 /// proceed with updating the firmware as it must be signed with a
33 /// corresponding private key (otherwise it could be malicious firmware).
34 ///
35 /// Mark to trigger firmware swap on next boot if verify suceeds.
36 ///
37 /// If the "ed25519-salty" feature is set (or another similar feature) then the signature is expected to have
38 /// been generated from a SHA-512 digest of the firmware bytes.
39 ///
40 /// If no signature feature is set then this method will always return a
41 /// signature error.
42 ///
43 /// # Safety
44 ///
45 /// The `_aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being read from
46 /// and written to.
47 #[cfg(feature = "_verify")]
48 pub fn verify_and_mark_updated_blocking<F: NorFlash>(
49 &mut self,
50 _state_and_dfu_flash: &mut F,
51 _public_key: &[u8],
52 _signature: &[u8],
53 _update_len: u32,
54 _aligned: &mut [u8],
55 ) -> Result<(), FirmwareUpdaterError> {
56 assert_eq!(_aligned.len(), F::WRITE_SIZE);
57 assert!(_update_len <= self.dfu.size());
58
59 #[cfg(feature = "ed25519-dalek")]
60 {
61 use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
62
63 use crate::digest_adapters::ed25519_dalek::Sha512;
64
65 let into_signature_error = |e: SignatureError| FirmwareUpdaterError::Signature(e.into());
66
67 let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
68 let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
69
70 let mut message = [0; 64];
71 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
72
73 public_key.verify(&message, &signature).map_err(into_signature_error)?
74 }
75 #[cfg(feature = "ed25519-salty")]
76 {
77 use salty::constants::{PUBLICKEY_SERIALIZED_LENGTH, SIGNATURE_SERIALIZED_LENGTH};
78 use salty::{PublicKey, Signature};
79
80 use crate::digest_adapters::salty::Sha512;
81
82 fn into_signature_error<E>(_: E) -> FirmwareUpdaterError {
83 FirmwareUpdaterError::Signature(signature::Error::default())
84 }
85
86 let public_key: [u8; PUBLICKEY_SERIALIZED_LENGTH] = _public_key.try_into().map_err(into_signature_error)?;
87 let public_key = PublicKey::try_from(&public_key).map_err(into_signature_error)?;
88 let signature: [u8; SIGNATURE_SERIALIZED_LENGTH] = _signature.try_into().map_err(into_signature_error)?;
89 let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
90
91 let mut message = [0; 64];
92 self.hash_blocking::<_, Sha512>(_state_and_dfu_flash, _update_len, _aligned, &mut message)?;
93
94 let r = public_key.verify(&message, &signature);
95 trace!(
96 "Verifying with public key {}, signature {} and message {} yields ok: {}",
97 public_key.to_bytes(),
98 signature.to_bytes(),
99 message,
100 r.is_ok()
101 );
102 r.map_err(into_signature_error)?
103 }
104
105 self.set_magic_blocking(_aligned, SWAP_MAGIC, _state_and_dfu_flash)
106 }
107
108 /// Verify the update in DFU with any digest.
109 pub fn hash_blocking<F: NorFlash, D: Digest>(
110 &mut self,
111 dfu_flash: &mut F,
112 update_len: u32,
113 chunk_buf: &mut [u8],
114 output: &mut [u8],
115 ) -> Result<(), FirmwareUpdaterError> {
116 let mut digest = D::new();
117 for offset in (0..update_len).step_by(chunk_buf.len()) {
118 self.dfu.read_blocking(dfu_flash, offset, chunk_buf)?;
119 let len = core::cmp::min((update_len - offset) as usize, chunk_buf.len());
120 digest.update(&chunk_buf[..len]);
121 }
122 output.copy_from_slice(digest.finalize().as_slice());
123 Ok(())
124 }
125
126 /// Mark to trigger firmware swap on next boot.
127 ///
128 /// # Safety
129 ///
130 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
131 #[cfg(not(feature = "_verify"))]
132 pub fn mark_updated_blocking<F: NorFlash>(
133 &mut self,
134 state_flash: &mut F,
135 aligned: &mut [u8],
136 ) -> Result<(), FirmwareUpdaterError> {
137 assert_eq!(aligned.len(), F::WRITE_SIZE);
138 self.set_magic_blocking(aligned, SWAP_MAGIC, state_flash)
139 }
140
141 /// Mark firmware boot successful and stop rollback on reset.
142 ///
143 /// # Safety
144 ///
145 /// The `aligned` buffer must have a size of F::WRITE_SIZE, and follow the alignment rules for the flash being written to.
146 pub fn mark_booted_blocking<F: NorFlash>(
147 &mut self,
148 state_flash: &mut F,
149 aligned: &mut [u8],
150 ) -> Result<(), FirmwareUpdaterError> {
151 assert_eq!(aligned.len(), F::WRITE_SIZE);
152 self.set_magic_blocking(aligned, BOOT_MAGIC, state_flash)
153 }
154
155 fn set_magic_blocking<F: NorFlash>(
156 &mut self,
157 aligned: &mut [u8],
158 magic: u8,
159 state_flash: &mut F,
160 ) -> Result<(), FirmwareUpdaterError> {
161 self.state.read_blocking(state_flash, 0, aligned)?;
162
163 if aligned.iter().any(|&b| b != magic) {
164 // Read progress validity
165 self.state.read_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
166
167 // FIXME: Do not make this assumption.
168 const STATE_ERASE_VALUE: u8 = 0xFF;
169
170 if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
171 // The current progress validity marker is invalid
172 } else {
173 // Invalidate progress
174 aligned.fill(!STATE_ERASE_VALUE);
175 self.state.write_blocking(state_flash, F::WRITE_SIZE as u32, aligned)?;
176 }
177
178 // Clear magic and progress
179 self.state.wipe_blocking(state_flash)?;
180
181 // Set magic
182 aligned.fill(magic);
183 self.state.write_blocking(state_flash, 0, aligned)?;
184 }
185 Ok(())
186 }
187
188 /// Write data to a flash page.
189 ///
190 /// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
191 ///
192 /// # Safety
193 ///
194 /// Failing to meet alignment and size requirements may result in a panic.
195 pub fn write_firmware_blocking<F: NorFlash>(
196 &mut self,
197 offset: usize,
198 data: &[u8],
199 dfu_flash: &mut F,
200 ) -> Result<(), FirmwareUpdaterError> {
201 assert!(data.len() >= F::ERASE_SIZE);
202
203 self.dfu
204 .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
205
206 self.dfu.write_blocking(dfu_flash, offset as u32, data)?;
207
208 Ok(())
209 }
210
211 /// Prepare for an incoming DFU update by erasing the entire DFU area and
212 /// returning its `Partition`.
213 ///
214 /// Using this instead of `write_firmware_blocking` allows for an optimized
215 /// API in exchange for added complexity.
216 pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> {
217 self.dfu.wipe_blocking(flash)?;
218
219 Ok(self.dfu)
220 }
221}
diff --git a/embassy-boot/boot/src/firmware_updater/mod.rs b/embassy-boot/boot/src/firmware_updater/mod.rs
new file mode 100644
index 000000000..e09f5eb6c
--- /dev/null
+++ b/embassy-boot/boot/src/firmware_updater/mod.rs
@@ -0,0 +1,71 @@
1#[cfg(feature = "nightly")]
2mod asynch;
3mod blocking;
4
5use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
6
7use crate::Partition;
8
9/// Errors returned by FirmwareUpdater
10#[derive(Debug)]
11pub enum FirmwareUpdaterError {
12 /// Error from flash.
13 Flash(NorFlashErrorKind),
14 /// Signature errors.
15 Signature(signature::Error),
16}
17
18#[cfg(feature = "defmt")]
19impl defmt::Format for FirmwareUpdaterError {
20 fn format(&self, fmt: defmt::Formatter) {
21 match self {
22 FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"),
23 FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"),
24 }
25 }
26}
27
28impl<E> From<E> for FirmwareUpdaterError
29where
30 E: NorFlashError,
31{
32 fn from(error: E) -> Self {
33 FirmwareUpdaterError::Flash(error.kind())
34 }
35}
36
37/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
38/// 'mess up' the internal bootloader state
39pub struct FirmwareUpdater {
40 state: Partition,
41 dfu: Partition,
42}
43
44#[cfg(target_os = "none")]
45impl Default for FirmwareUpdater {
46 fn default() -> Self {
47 extern "C" {
48 static __bootloader_state_start: u32;
49 static __bootloader_state_end: u32;
50 static __bootloader_dfu_start: u32;
51 static __bootloader_dfu_end: u32;
52 }
53
54 let dfu = unsafe {
55 Partition::new(
56 &__bootloader_dfu_start as *const u32 as u32,
57 &__bootloader_dfu_end as *const u32 as u32,
58 )
59 };
60 let state = unsafe {
61 Partition::new(
62 &__bootloader_state_start as *const u32 as u32,
63 &__bootloader_state_end as *const u32 as u32,
64 )
65 };
66
67 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
68 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
69 FirmwareUpdater::new(dfu, state)
70 }
71}
diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs
index 1fe520e6c..af73df059 100644
--- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs
+++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs
@@ -2,13 +2,12 @@
2//! 2//!
3//! # Example (nrf52) 3//! # Example (nrf52)
4//! 4//!
5//! ```rust 5//! ```rust,ignore
6//! use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; 6//! use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
7//! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; 7//! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex};
8//! 8//!
9//! static I2C_BUS: StaticCell<NoopMutex<RefCell<Twim<TWISPI0>>>> = StaticCell::new(); 9//! static I2C_BUS: StaticCell<NoopMutex<RefCell<Twim<TWISPI0>>>> = StaticCell::new();
10//! let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); 10//! let i2c = Twim::new(p.TWISPI0, Irqs, p.P0_03, p.P0_04, Config::default());
11//! let i2c = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, Config::default());
12//! let i2c_bus = NoopMutex::new(RefCell::new(i2c)); 11//! let i2c_bus = NoopMutex::new(RefCell::new(i2c));
13//! let i2c_bus = I2C_BUS.init(i2c_bus); 12//! let i2c_bus = I2C_BUS.init(i2c_bus);
14//! 13//!
diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs
index 7982ffb6e..22e013be9 100644
--- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs
+++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs
@@ -2,13 +2,12 @@
2//! 2//!
3//! # Example (nrf52) 3//! # Example (nrf52)
4//! 4//!
5//! ```rust 5//! ```rust,ignore
6//! use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; 6//! use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice;
7//! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex}; 7//! use embassy_sync::blocking_mutex::{NoopMutex, raw::NoopRawMutex};
8//! 8//!
9//! static SPI_BUS: StaticCell<NoopMutex<RefCell<Spim<SPI3>>>> = StaticCell::new(); 9//! static SPI_BUS: StaticCell<NoopMutex<RefCell<Spim<SPI3>>>> = StaticCell::new();
10//! let irq = interrupt::take!(SPIM3); 10//! let spi = Spim::new_txonly(p.SPI3, Irqs, p.P0_15, p.P0_18, Config::default());
11//! let spi = Spim::new_txonly(p.SPI3, irq, p.P0_15, p.P0_18, Config::default());
12//! let spi_bus = NoopMutex::new(RefCell::new(spi)); 11//! let spi_bus = NoopMutex::new(RefCell::new(spi));
13//! let spi_bus = SPI_BUS.init(spi_bus); 12//! let spi_bus = SPI_BUS.init(spi_bus);
14//! 13//!
diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-common/src/atomic_ring_buffer.rs
index 0eb39cb33..ea84925c4 100644
--- a/embassy-hal-common/src/atomic_ring_buffer.rs
+++ b/embassy-hal-common/src/atomic_ring_buffer.rs
@@ -458,8 +458,6 @@ mod tests {
458 458
459 #[test] 459 #[test]
460 fn push_slices() { 460 fn push_slices() {
461 init();
462
463 let mut b = [0; 4]; 461 let mut b = [0; 4];
464 let rb = RingBuffer::new(); 462 let rb = RingBuffer::new();
465 unsafe { 463 unsafe {
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs
index bc2c8a3c1..c82c238cc 100644
--- a/embassy-nrf/src/time_driver.rs
+++ b/embassy-nrf/src/time_driver.rs
@@ -67,7 +67,7 @@ fn compare_n(n: usize) -> u32 {
67 1 << (n + 16) 67 1 << (n + 16)
68} 68}
69 69
70#[cfg(tests)] 70#[cfg(test)]
71mod test { 71mod test {
72 use super::*; 72 use super::*;
73 73
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index b766f0739..730c78f5e 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -912,6 +912,16 @@ fn main() {
912 println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); 912 println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
913 } 913 }
914 914
915 // ========
916 // stm32wb tl_mbox link sections
917
918 if chip_name.starts_with("stm32wb") {
919 let out_file = out_dir.join("tl_mbox.x").to_string_lossy().to_string();
920 fs::write(out_file, fs::read_to_string("tl_mbox.x.in").unwrap()).unwrap();
921 println!("cargo:rustc-link-search={}", out_dir.display());
922 println!("cargo:rerun-if-changed=tl_mbox.x.in");
923 }
924
915 // ======= 925 // =======
916 // Features for targeting groups of chips 926 // Features for targeting groups of chips
917 927
diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs
index 6c1912900..c6cdc574b 100644
--- a/embassy-stm32/src/flash/common.rs
+++ b/embassy-stm32/src/flash/common.rs
@@ -163,7 +163,7 @@ pub(super) fn get_sector(address: u32, regions: &[&FlashRegion]) -> FlashSector
163 bank_offset = 0; 163 bank_offset = 0;
164 } 164 }
165 165
166 if address < region.end() { 166 if address >= region.base && address < region.end() {
167 let index_in_region = (address - region.base) / region.erase_size; 167 let index_in_region = (address - region.base) / region.erase_size;
168 return FlashSector { 168 return FlashSector {
169 bank: region.bank, 169 bank: region.bank,
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 1258a65f1..6533509eb 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -41,8 +41,6 @@ pub mod crc;
41pub mod flash; 41pub mod flash;
42#[cfg(all(spi_v1, rcc_f4))] 42#[cfg(all(spi_v1, rcc_f4))]
43pub mod i2s; 43pub mod i2s;
44#[cfg(stm32wb)]
45pub mod ipcc;
46pub mod pwm; 44pub mod pwm;
47#[cfg(quadspi)] 45#[cfg(quadspi)]
48pub mod qspi; 46pub mod qspi;
diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs
index 3f8b43cfa..cfb79947c 100644
--- a/embassy-stm32/src/pwm/complementary_pwm.rs
+++ b/embassy-stm32/src/pwm/complementary_pwm.rs
@@ -209,39 +209,39 @@ mod tests {
209 209
210 #[test] 210 #[test]
211 fn test_compute_dead_time_value() { 211 fn test_compute_dead_time_value() {
212 struct test_run { 212 struct TestRun {
213 value: u16, 213 value: u16,
214 ckd: Ckd, 214 ckd: Ckd,
215 bits: u8, 215 bits: u8,
216 } 216 }
217 217
218 let fn_results = [ 218 let fn_results = [
219 test_run { 219 TestRun {
220 value: 1, 220 value: 1,
221 ckd: Ckd::DIV1, 221 ckd: Ckd::DIV1,
222 bits: 1, 222 bits: 1,
223 }, 223 },
224 test_run { 224 TestRun {
225 value: 125, 225 value: 125,
226 ckd: Ckd::DIV1, 226 ckd: Ckd::DIV1,
227 bits: 125, 227 bits: 125,
228 }, 228 },
229 test_run { 229 TestRun {
230 value: 245, 230 value: 245,
231 ckd: Ckd::DIV1, 231 ckd: Ckd::DIV1,
232 bits: 64 + 245 / 2, 232 bits: 64 + 245 / 2,
233 }, 233 },
234 test_run { 234 TestRun {
235 value: 255, 235 value: 255,
236 ckd: Ckd::DIV2, 236 ckd: Ckd::DIV2,
237 bits: 127, 237 bits: 127,
238 }, 238 },
239 test_run { 239 TestRun {
240 value: 400, 240 value: 400,
241 ckd: Ckd::DIV1, 241 ckd: Ckd::DIV1,
242 bits: 32 + (400u16 / 8) as u8, 242 bits: 32 + (400u16 / 8) as u8,
243 }, 243 },
244 test_run { 244 TestRun {
245 value: 600, 245 value: 600,
246 ckd: Ckd::DIV4, 246 ckd: Ckd::DIV4,
247 bits: 64 + (600u16 / 8) as u8, 247 bits: 64 + (600u16 / 8) as u8,
diff --git a/embassy-stm32/src/tl_mbox/ble.rs b/embassy-stm32/src/tl_mbox/ble.rs
index d8bf14d4f..062377999 100644
--- a/embassy-stm32/src/tl_mbox/ble.rs
+++ b/embassy-stm32/src/tl_mbox/ble.rs
@@ -1,5 +1,3 @@
1use core::mem::MaybeUninit;
2
3use embassy_futures::block_on; 1use embassy_futures::block_on;
4 2
5use super::cmd::CmdSerial; 3use super::cmd::CmdSerial;
@@ -10,17 +8,17 @@ use super::{
10 channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_CHANNEL, 8 channels, BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_CHANNEL,
11 TL_REF_TABLE, 9 TL_REF_TABLE,
12}; 10};
13use crate::ipcc::Ipcc;
14use crate::tl_mbox::cmd::CmdPacket; 11use crate::tl_mbox::cmd::CmdPacket;
12use crate::tl_mbox::ipcc::Ipcc;
15 13
16pub struct Ble; 14pub struct Ble;
17 15
18impl Ble { 16impl Ble {
19 pub(crate) fn new(ipcc: &mut Ipcc) -> Self { 17 pub fn enable() {
20 unsafe { 18 unsafe {
21 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); 19 LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
22 20
23 TL_BLE_TABLE = MaybeUninit::new(BleTable { 21 TL_BLE_TABLE.as_mut_ptr().write_volatile(BleTable {
24 pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(), 22 pcmd_buffer: BLE_CMD_BUFFER.as_mut_ptr().cast(),
25 pcs_buffer: CS_BUFFER.as_mut_ptr().cast(), 23 pcs_buffer: CS_BUFFER.as_mut_ptr().cast(),
26 pevt_queue: EVT_QUEUE.as_ptr().cast(), 24 pevt_queue: EVT_QUEUE.as_ptr().cast(),
@@ -28,12 +26,10 @@ impl Ble {
28 }); 26 });
29 } 27 }
30 28
31 ipcc.c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true); 29 Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true);
32
33 Ble
34 } 30 }
35 31
36 pub(crate) fn evt_handler(ipcc: &mut Ipcc) { 32 pub fn evt_handler() {
37 unsafe { 33 unsafe {
38 let mut node_ptr = core::ptr::null_mut(); 34 let mut node_ptr = core::ptr::null_mut();
39 let node_ptr_ptr: *mut _ = &mut node_ptr; 35 let node_ptr_ptr: *mut _ = &mut node_ptr;
@@ -48,10 +44,10 @@ impl Ble {
48 } 44 }
49 } 45 }
50 46
51 ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL); 47 Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL);
52 } 48 }
53 49
54 pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { 50 pub fn send_cmd(buf: &[u8]) {
55 unsafe { 51 unsafe {
56 let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer; 52 let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer;
57 let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial; 53 let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmd_serial;
@@ -63,6 +59,6 @@ impl Ble {
63 cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8; 59 cmd_packet.cmd_serial.ty = TlPacketType::BleCmd as u8;
64 } 60 }
65 61
66 ipcc.c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL); 62 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL);
67 } 63 }
68} 64}
diff --git a/embassy-stm32/src/tl_mbox/channels.rs b/embassy-stm32/src/tl_mbox/channels.rs
index aaa6ce177..25a065ba4 100644
--- a/embassy-stm32/src/tl_mbox/channels.rs
+++ b/embassy-stm32/src/tl_mbox/channels.rs
@@ -50,7 +50,7 @@
50//! 50//!
51 51
52pub mod cpu1 { 52pub mod cpu1 {
53 use crate::ipcc::IpccChannel; 53 use crate::tl_mbox::ipcc::IpccChannel;
54 54
55 // Not used currently but reserved 55 // Not used currently but reserved
56 pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; 56 pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1;
@@ -75,7 +75,7 @@ pub mod cpu1 {
75} 75}
76 76
77pub mod cpu2 { 77pub mod cpu2 {
78 use crate::ipcc::IpccChannel; 78 use crate::tl_mbox::ipcc::IpccChannel;
79 79
80 pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; 80 pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1;
81 pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; 81 pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2;
diff --git a/embassy-stm32/src/tl_mbox/evt.rs b/embassy-stm32/src/tl_mbox/evt.rs
index 770133f29..47a8b72fd 100644
--- a/embassy-stm32/src/tl_mbox/evt.rs
+++ b/embassy-stm32/src/tl_mbox/evt.rs
@@ -3,7 +3,7 @@ use core::mem::MaybeUninit;
3use super::cmd::{AclDataPacket, AclDataSerial}; 3use super::cmd::{AclDataPacket, AclDataSerial};
4use super::consts::TlPacketType; 4use super::consts::TlPacketType;
5use super::{PacketHeader, TL_EVT_HEADER_SIZE}; 5use super::{PacketHeader, TL_EVT_HEADER_SIZE};
6use crate::tl_mbox::mm; 6use crate::tl_mbox::mm::MemoryManager;
7 7
8/// the payload of [`Evt`] for a command status event 8/// the payload of [`Evt`] for a command status event
9#[derive(Copy, Clone)] 9#[derive(Copy, Clone)]
@@ -131,9 +131,6 @@ impl EvtBox {
131 131
132impl Drop for EvtBox { 132impl Drop for EvtBox {
133 fn drop(&mut self) { 133 fn drop(&mut self) {
134 use crate::ipcc::Ipcc; 134 MemoryManager::evt_drop(self.ptr);
135
136 let mut ipcc = Ipcc::new_inner(unsafe { crate::Peripherals::steal() }.IPCC);
137 mm::MemoryManager::evt_drop(self.ptr, &mut ipcc);
138 } 135 }
139} 136}
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/tl_mbox/ipcc.rs
index 2b9caf8e5..d1ac731ed 100644
--- a/embassy-stm32/src/ipcc.rs
+++ b/embassy-stm32/src/tl_mbox/ipcc.rs
@@ -1,6 +1,4 @@
1use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 1use self::sealed::Instance;
2
3use crate::ipcc::sealed::Instance;
4use crate::peripherals::IPCC; 2use crate::peripherals::IPCC;
5use crate::rcc::sealed::RccPeripheral; 3use crate::rcc::sealed::RccPeripheral;
6 4
@@ -22,29 +20,17 @@ pub enum IpccChannel {
22 Channel6 = 5, 20 Channel6 = 5,
23} 21}
24 22
25pub(crate) mod sealed { 23pub mod sealed {
26 pub trait Instance: crate::rcc::RccPeripheral { 24 pub trait Instance: crate::rcc::RccPeripheral {
27 fn regs() -> crate::pac::ipcc::Ipcc; 25 fn regs() -> crate::pac::ipcc::Ipcc;
28 fn set_cpu2(enabled: bool); 26 fn set_cpu2(enabled: bool);
29 } 27 }
30} 28}
31 29
32pub struct Ipcc<'d> { 30pub struct Ipcc;
33 _peri: PeripheralRef<'d, IPCC>,
34}
35
36impl<'d> Ipcc<'d> {
37 pub fn new(peri: impl Peripheral<P = IPCC> + 'd, _config: Config) -> Self {
38 Self::new_inner(peri)
39 }
40
41 pub(crate) fn new_inner(peri: impl Peripheral<P = IPCC> + 'd) -> Self {
42 into_ref!(peri);
43 31
44 Self { _peri: peri } 32impl Ipcc {
45 } 33 pub fn enable(_config: Config) {
46
47 pub fn init(&mut self) {
48 IPCC::enable(); 34 IPCC::enable();
49 IPCC::reset(); 35 IPCC::reset();
50 IPCC::set_cpu2(true); 36 IPCC::set_cpu2(true);
@@ -61,56 +47,60 @@ impl<'d> Ipcc<'d> {
61 } 47 }
62 } 48 }
63 49
64 pub fn c1_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { 50 pub fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) {
65 let regs = IPCC::regs(); 51 let regs = IPCC::regs();
66 52
67 // If bit is set to 1 then interrupt is disabled 53 // If bit is set to 1 then interrupt is disabled
68 unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } 54 unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
69 } 55 }
70 56
71 pub fn c1_get_rx_channel(&self, channel: IpccChannel) -> bool { 57 pub fn c1_get_rx_channel(channel: IpccChannel) -> bool {
72 let regs = IPCC::regs(); 58 let regs = IPCC::regs();
73 59
74 // If bit is set to 1 then interrupt is disabled 60 // If bit is set to 1 then interrupt is disabled
75 unsafe { !regs.cpu(0).mr().read().chom(channel as usize) } 61 unsafe { !regs.cpu(0).mr().read().chom(channel as usize) }
76 } 62 }
77 63
78 pub fn c2_set_rx_channel(&mut self, channel: IpccChannel, enabled: bool) { 64 #[allow(dead_code)]
65 pub fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) {
79 let regs = IPCC::regs(); 66 let regs = IPCC::regs();
80 67
81 // If bit is set to 1 then interrupt is disabled 68 // If bit is set to 1 then interrupt is disabled
82 unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) } 69 unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
83 } 70 }
84 71
85 pub fn c2_get_rx_channel(&self, channel: IpccChannel) -> bool { 72 #[allow(dead_code)]
73 pub fn c2_get_rx_channel(channel: IpccChannel) -> bool {
86 let regs = IPCC::regs(); 74 let regs = IPCC::regs();
87 75
88 // If bit is set to 1 then interrupt is disabled 76 // If bit is set to 1 then interrupt is disabled
89 unsafe { !regs.cpu(1).mr().read().chom(channel as usize) } 77 unsafe { !regs.cpu(1).mr().read().chom(channel as usize) }
90 } 78 }
91 79
92 pub fn c1_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { 80 pub fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) {
93 let regs = IPCC::regs(); 81 let regs = IPCC::regs();
94 82
95 // If bit is set to 1 then interrupt is disabled 83 // If bit is set to 1 then interrupt is disabled
96 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } 84 unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) }
97 } 85 }
98 86
99 pub fn c1_get_tx_channel(&self, channel: IpccChannel) -> bool { 87 pub fn c1_get_tx_channel(channel: IpccChannel) -> bool {
100 let regs = IPCC::regs(); 88 let regs = IPCC::regs();
101 89
102 // If bit is set to 1 then interrupt is disabled 90 // If bit is set to 1 then interrupt is disabled
103 unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) } 91 unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) }
104 } 92 }
105 93
106 pub fn c2_set_tx_channel(&mut self, channel: IpccChannel, enabled: bool) { 94 #[allow(dead_code)]
95 pub fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) {
107 let regs = IPCC::regs(); 96 let regs = IPCC::regs();
108 97
109 // If bit is set to 1 then interrupt is disabled 98 // If bit is set to 1 then interrupt is disabled
110 unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) } 99 unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) }
111 } 100 }
112 101
113 pub fn c2_get_tx_channel(&self, channel: IpccChannel) -> bool { 102 #[allow(dead_code)]
103 pub fn c2_get_tx_channel(channel: IpccChannel) -> bool {
114 let regs = IPCC::regs(); 104 let regs = IPCC::regs();
115 105
116 // If bit is set to 1 then interrupt is disabled 106 // If bit is set to 1 then interrupt is disabled
@@ -118,53 +108,51 @@ impl<'d> Ipcc<'d> {
118 } 108 }
119 109
120 /// clears IPCC receive channel status for CPU1 110 /// clears IPCC receive channel status for CPU1
121 pub fn c1_clear_flag_channel(&mut self, channel: IpccChannel) { 111 pub fn c1_clear_flag_channel(channel: IpccChannel) {
122 let regs = IPCC::regs(); 112 let regs = IPCC::regs();
123 113
124 unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) } 114 unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) }
125 } 115 }
126 116
117 #[allow(dead_code)]
127 /// clears IPCC receive channel status for CPU2 118 /// clears IPCC receive channel status for CPU2
128 pub fn c2_clear_flag_channel(&mut self, channel: IpccChannel) { 119 pub fn c2_clear_flag_channel(channel: IpccChannel) {
129 let regs = IPCC::regs(); 120 let regs = IPCC::regs();
130 121
131 unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) } 122 unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) }
132 } 123 }
133 124
134 pub fn c1_set_flag_channel(&mut self, channel: IpccChannel) { 125 pub fn c1_set_flag_channel(channel: IpccChannel) {
135 let regs = IPCC::regs(); 126 let regs = IPCC::regs();
136 127
137 unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) } 128 unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) }
138 } 129 }
139 130
140 pub fn c2_set_flag_channel(&mut self, channel: IpccChannel) { 131 #[allow(dead_code)]
132 pub fn c2_set_flag_channel(channel: IpccChannel) {
141 let regs = IPCC::regs(); 133 let regs = IPCC::regs();
142 134
143 unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) } 135 unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) }
144 } 136 }
145 137
146 pub fn c1_is_active_flag(&self, channel: IpccChannel) -> bool { 138 pub fn c1_is_active_flag(channel: IpccChannel) -> bool {
147 let regs = IPCC::regs(); 139 let regs = IPCC::regs();
148 140
149 unsafe { regs.cpu(0).sr().read().chf(channel as usize) } 141 unsafe { regs.cpu(0).sr().read().chf(channel as usize) }
150 } 142 }
151 143
152 pub fn c2_is_active_flag(&self, channel: IpccChannel) -> bool { 144 pub fn c2_is_active_flag(channel: IpccChannel) -> bool {
153 let regs = IPCC::regs(); 145 let regs = IPCC::regs();
154 146
155 unsafe { regs.cpu(1).sr().read().chf(channel as usize) } 147 unsafe { regs.cpu(1).sr().read().chf(channel as usize) }
156 } 148 }
157 149
158 pub fn is_tx_pending(&self, channel: IpccChannel) -> bool { 150 pub fn is_tx_pending(channel: IpccChannel) -> bool {
159 !self.c1_is_active_flag(channel) && self.c1_get_tx_channel(channel) 151 !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel)
160 }
161
162 pub fn is_rx_pending(&self, channel: IpccChannel) -> bool {
163 self.c2_is_active_flag(channel) && self.c1_get_rx_channel(channel)
164 } 152 }
165 153
166 pub fn as_mut_ptr(&self) -> *mut Self { 154 pub fn is_rx_pending(channel: IpccChannel) -> bool {
167 unsafe { &mut core::ptr::read(self) as *mut _ } 155 Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel)
168 } 156 }
169} 157}
170 158
diff --git a/embassy-stm32/src/tl_mbox/mm.rs b/embassy-stm32/src/tl_mbox/mm.rs
index f99ffa399..e28a6aa0c 100644
--- a/embassy-stm32/src/tl_mbox/mm.rs
+++ b/embassy-stm32/src/tl_mbox/mm.rs
@@ -1,22 +1,20 @@
1use core::mem::MaybeUninit;
2
3use super::evt::EvtPacket; 1use super::evt::EvtPacket;
4use super::unsafe_linked_list::LinkedListNode; 2use super::unsafe_linked_list::LinkedListNode;
5use super::{ 3use super::{
6 channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, 4 channels, MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUFF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE,
7 SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE, 5 SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, TL_REF_TABLE,
8}; 6};
9use crate::ipcc::Ipcc; 7use crate::tl_mbox::ipcc::Ipcc;
10 8
11pub struct MemoryManager; 9pub struct MemoryManager;
12 10
13impl MemoryManager { 11impl MemoryManager {
14 pub fn new() -> Self { 12 pub fn enable() {
15 unsafe { 13 unsafe {
16 LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr()); 14 LinkedListNode::init_head(FREE_BUFF_QUEUE.as_mut_ptr());
17 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); 15 LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
18 16
19 TL_MEM_MANAGER_TABLE = MaybeUninit::new(MemManagerTable { 17 TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
20 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), 18 spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
21 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), 19 spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
22 ble_pool: EVT_POOL.as_ptr().cast(), 20 ble_pool: EVT_POOL.as_ptr().cast(),
@@ -26,31 +24,29 @@ impl MemoryManager {
26 traces_pool_size: 0, 24 traces_pool_size: 0,
27 }); 25 });
28 } 26 }
29
30 MemoryManager
31 } 27 }
32 28
33 pub fn evt_handler(ipcc: &mut Ipcc) { 29 pub fn evt_handler() {
34 ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false); 30 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false);
35 Self::send_free_buf(); 31 Self::send_free_buf();
36 ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); 32 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
37 } 33 }
38 34
39 pub fn evt_drop(evt: *mut EvtPacket, ipcc: &mut Ipcc) { 35 pub fn evt_drop(evt: *mut EvtPacket) {
40 unsafe { 36 unsafe {
41 let list_node = evt.cast(); 37 let list_node = evt.cast();
42 38
43 LinkedListNode::remove_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node); 39 LinkedListNode::remove_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node);
44 } 40 }
45 41
46 let channel_is_busy = ipcc.c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); 42 let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
47 43
48 // postpone event buffer freeing to IPCC interrupt handler 44 // postpone event buffer freeing to IPCC interrupt handler
49 if channel_is_busy { 45 if channel_is_busy {
50 ipcc.c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true); 46 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true);
51 } else { 47 } else {
52 Self::send_free_buf(); 48 Self::send_free_buf();
53 ipcc.c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL); 49 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
54 } 50 }
55 } 51 }
56 52
diff --git a/embassy-stm32/src/tl_mbox/mod.rs b/embassy-stm32/src/tl_mbox/mod.rs
index dc6104cc3..616f7dc56 100644
--- a/embassy-stm32/src/tl_mbox/mod.rs
+++ b/embassy-stm32/src/tl_mbox/mod.rs
@@ -1,6 +1,9 @@
1use core::mem::MaybeUninit; 1use core::mem::MaybeUninit;
2 2
3use atomic_polyfill::{compiler_fence, Ordering};
3use bit_field::BitField; 4use bit_field::BitField;
5use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
6use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
4use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; 7use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
5use embassy_sync::channel::Channel; 8use embassy_sync::channel::Channel;
6 9
@@ -12,13 +15,16 @@ use self::shci::{shci_ble_init, ShciBleInitCmdParam};
12use self::sys::Sys; 15use self::sys::Sys;
13use self::unsafe_linked_list::LinkedListNode; 16use self::unsafe_linked_list::LinkedListNode;
14use crate::interrupt; 17use crate::interrupt;
15use crate::ipcc::Ipcc; 18use crate::peripherals::IPCC;
19pub use crate::tl_mbox::ipcc::Config;
20use crate::tl_mbox::ipcc::Ipcc;
16 21
17mod ble; 22mod ble;
18mod channels; 23mod channels;
19mod cmd; 24mod cmd;
20mod consts; 25mod consts;
21mod evt; 26mod evt;
27mod ipcc;
22mod mm; 28mod mm;
23mod shci; 29mod shci;
24mod sys; 30mod sys;
@@ -58,13 +64,34 @@ pub struct FusInfoTable {
58pub struct ReceiveInterruptHandler {} 64pub struct ReceiveInterruptHandler {}
59 65
60impl interrupt::Handler<interrupt::IPCC_C1_RX> for ReceiveInterruptHandler { 66impl interrupt::Handler<interrupt::IPCC_C1_RX> for ReceiveInterruptHandler {
61 unsafe fn on_interrupt() {} 67 unsafe fn on_interrupt() {
68 // info!("ipcc rx interrupt");
69
70 if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) {
71 sys::Sys::evt_handler();
72 } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) {
73 ble::Ble::evt_handler();
74 } else {
75 todo!()
76 }
77 }
62} 78}
63 79
64pub struct TransmitInterruptHandler {} 80pub struct TransmitInterruptHandler {}
65 81
66impl interrupt::Handler<interrupt::IPCC_C1_TX> for TransmitInterruptHandler { 82impl interrupt::Handler<interrupt::IPCC_C1_TX> for TransmitInterruptHandler {
67 unsafe fn on_interrupt() {} 83 unsafe fn on_interrupt() {
84 // info!("ipcc tx interrupt");
85
86 if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) {
87 // TODO: handle this case
88 let _ = sys::Sys::cmd_evt_handler();
89 } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) {
90 mm::MemoryManager::evt_handler();
91 } else {
92 todo!()
93 }
94 }
68} 95}
69 96
70/// # Version 97/// # Version
@@ -289,21 +316,24 @@ static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251
289// TODO: get a better size, this is a placeholder 316// TODO: get a better size, this is a placeholder
290pub(crate) static TL_CHANNEL: Channel<CriticalSectionRawMutex, EvtBox, 5> = Channel::new(); 317pub(crate) static TL_CHANNEL: Channel<CriticalSectionRawMutex, EvtBox, 5> = Channel::new();
291 318
292pub struct TlMbox { 319pub struct TlMbox<'d> {
293 _sys: Sys, 320 _ipcc: PeripheralRef<'d, IPCC>,
294 _ble: Ble,
295 _mm: MemoryManager,
296} 321}
297 322
298impl TlMbox { 323impl<'d> TlMbox<'d> {
299 /// initializes low-level transport between CPU1 and BLE stack on CPU2 324 /// initializes low-level transport between CPU1 and BLE stack on CPU2
300 pub fn init( 325 pub fn new(
301 ipcc: &mut Ipcc, 326 ipcc: impl Peripheral<P = IPCC> + 'd,
302 _irqs: impl interrupt::Binding<interrupt::IPCC_C1_RX, ReceiveInterruptHandler> 327 _irqs: impl interrupt::Binding<interrupt::IPCC_C1_RX, ReceiveInterruptHandler>
303 + interrupt::Binding<interrupt::IPCC_C1_TX, TransmitInterruptHandler>, 328 + interrupt::Binding<interrupt::IPCC_C1_TX, TransmitInterruptHandler>,
304 ) -> TlMbox { 329 config: Config,
330 ) -> Self {
331 into_ref!(ipcc);
332
305 unsafe { 333 unsafe {
306 TL_REF_TABLE = MaybeUninit::new(RefTable { 334 compiler_fence(Ordering::AcqRel);
335
336 TL_REF_TABLE.as_mut_ptr().write_volatile(RefTable {
307 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(), 337 device_info_table: TL_DEVICE_INFO_TABLE.as_ptr(),
308 ble_table: TL_BLE_TABLE.as_ptr(), 338 ble_table: TL_BLE_TABLE.as_ptr(),
309 thread_table: TL_THREAD_TABLE.as_ptr(), 339 thread_table: TL_THREAD_TABLE.as_ptr(),
@@ -316,6 +346,10 @@ impl TlMbox {
316 ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), 346 ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
317 }); 347 });
318 348
349 // info!("TL_REF_TABLE addr: {:x}", TL_REF_TABLE.as_ptr() as usize);
350
351 compiler_fence(Ordering::AcqRel);
352
319 TL_SYS_TABLE = MaybeUninit::zeroed(); 353 TL_SYS_TABLE = MaybeUninit::zeroed();
320 TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed(); 354 TL_DEVICE_INFO_TABLE = MaybeUninit::zeroed();
321 TL_BLE_TABLE = MaybeUninit::zeroed(); 355 TL_BLE_TABLE = MaybeUninit::zeroed();
@@ -334,33 +368,24 @@ impl TlMbox {
334 CS_BUFFER = MaybeUninit::zeroed(); 368 CS_BUFFER = MaybeUninit::zeroed();
335 BLE_CMD_BUFFER = MaybeUninit::zeroed(); 369 BLE_CMD_BUFFER = MaybeUninit::zeroed();
336 HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed(); 370 HCI_ACL_DATA_BUFFER = MaybeUninit::zeroed();
371
372 compiler_fence(Ordering::AcqRel);
337 } 373 }
338 374
339 ipcc.init(); 375 Ipcc::enable(config);
340 376
341 let _sys = Sys::new(ipcc); 377 Sys::enable();
342 let _ble = Ble::new(ipcc); 378 Ble::enable();
343 let _mm = MemoryManager::new(); 379 MemoryManager::enable();
344 380
345 // rx_irq.disable(); 381 // enable interrupts
346 // tx_irq.disable(); 382 unsafe { crate::interrupt::IPCC_C1_RX::steal() }.unpend();
347 // 383 unsafe { crate::interrupt::IPCC_C1_TX::steal() }.unpend();
348 // rx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); 384
349 // tx_irq.set_handler_context(ipcc.as_mut_ptr() as *mut ()); 385 unsafe { crate::interrupt::IPCC_C1_RX::steal() }.enable();
350 // 386 unsafe { crate::interrupt::IPCC_C1_TX::steal() }.enable();
351 // rx_irq.set_handler(|ipcc| { 387
352 // let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() }; 388 Self { _ipcc: ipcc }
353 // Self::interrupt_ipcc_rx_handler(ipcc);
354 // });
355 // tx_irq.set_handler(|ipcc| {
356 // let ipcc: &mut Ipcc = unsafe { &mut *ipcc.cast() };
357 // Self::interrupt_ipcc_tx_handler(ipcc);
358 // });
359 //
360 // rx_irq.enable();
361 // tx_irq.enable();
362
363 TlMbox { _sys, _ble, _mm }
364 } 389 }
365 390
366 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> { 391 pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
@@ -374,42 +399,19 @@ impl TlMbox {
374 } 399 }
375 } 400 }
376 401
377 pub fn shci_ble_init(&self, ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { 402 pub fn shci_ble_init(&self, param: ShciBleInitCmdParam) {
378 shci_ble_init(ipcc, param); 403 shci_ble_init(param);
379 } 404 }
380 405
381 pub fn send_ble_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { 406 pub fn send_ble_cmd(&self, buf: &[u8]) {
382 ble::Ble::send_cmd(ipcc, buf); 407 ble::Ble::send_cmd(buf);
383 } 408 }
384 409
385 // pub fn send_sys_cmd(&self, ipcc: &mut Ipcc, buf: &[u8]) { 410 // pub fn send_sys_cmd(&self, buf: &[u8]) {
386 // sys::Sys::send_cmd(ipcc, buf); 411 // sys::Sys::send_cmd(buf);
387 // } 412 // }
388 413
389 pub async fn read(&self) -> EvtBox { 414 pub async fn read(&self) -> EvtBox {
390 TL_CHANNEL.recv().await 415 TL_CHANNEL.recv().await
391 } 416 }
392
393 #[allow(dead_code)]
394 fn interrupt_ipcc_rx_handler(ipcc: &mut Ipcc) {
395 if ipcc.is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) {
396 sys::Sys::evt_handler(ipcc);
397 } else if ipcc.is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) {
398 ble::Ble::evt_handler(ipcc);
399 } else {
400 todo!()
401 }
402 }
403
404 #[allow(dead_code)]
405 fn interrupt_ipcc_tx_handler(ipcc: &mut Ipcc) {
406 if ipcc.is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) {
407 // TODO: handle this case
408 let _ = sys::Sys::cmd_evt_handler(ipcc);
409 } else if ipcc.is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) {
410 mm::MemoryManager::evt_handler(ipcc);
411 } else {
412 todo!()
413 }
414 }
415} 417}
diff --git a/embassy-stm32/src/tl_mbox/shci.rs b/embassy-stm32/src/tl_mbox/shci.rs
index 61fd9e4a3..6b5b2dd19 100644
--- a/embassy-stm32/src/tl_mbox/shci.rs
+++ b/embassy-stm32/src/tl_mbox/shci.rs
@@ -3,7 +3,7 @@
3use super::cmd::CmdPacket; 3use super::cmd::CmdPacket;
4use super::consts::TlPacketType; 4use super::consts::TlPacketType;
5use super::{channels, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE}; 5use super::{channels, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE};
6use crate::ipcc::Ipcc; 6use crate::tl_mbox::ipcc::Ipcc;
7 7
8const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66; 8const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66;
9pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE; 9pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
@@ -76,7 +76,7 @@ pub struct ShciBleInitCmdPacket {
76 param: ShciBleInitCmdParam, 76 param: ShciBleInitCmdParam,
77} 77}
78 78
79pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) { 79pub fn shci_ble_init(param: ShciBleInitCmdParam) {
80 let mut packet = ShciBleInitCmdPacket { 80 let mut packet = ShciBleInitCmdPacket {
81 header: ShciHeader::default(), 81 header: ShciHeader::default(),
82 param, 82 param,
@@ -95,7 +95,7 @@ pub fn shci_ble_init(ipcc: &mut Ipcc, param: ShciBleInitCmdParam) {
95 95
96 cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8; 96 cmd_buf.cmd_serial.ty = TlPacketType::SysCmd as u8;
97 97
98 ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); 98 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL);
99 ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); 99 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true);
100 } 100 }
101} 101}
diff --git a/embassy-stm32/src/tl_mbox/sys.rs b/embassy-stm32/src/tl_mbox/sys.rs
index 31ebde721..9685fb920 100644
--- a/embassy-stm32/src/tl_mbox/sys.rs
+++ b/embassy-stm32/src/tl_mbox/sys.rs
@@ -1,5 +1,3 @@
1use core::mem::MaybeUninit;
2
3use embassy_futures::block_on; 1use embassy_futures::block_on;
4 2
5use super::cmd::{CmdPacket, CmdSerial}; 3use super::cmd::{CmdPacket, CmdSerial};
@@ -7,27 +5,25 @@ use super::consts::TlPacketType;
7use super::evt::{CcEvt, EvtBox, EvtSerial}; 5use super::evt::{CcEvt, EvtBox, EvtSerial};
8use super::unsafe_linked_list::LinkedListNode; 6use super::unsafe_linked_list::LinkedListNode;
9use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_CHANNEL, TL_REF_TABLE, TL_SYS_TABLE}; 7use super::{channels, SysTable, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_CHANNEL, TL_REF_TABLE, TL_SYS_TABLE};
10use crate::ipcc::Ipcc; 8use crate::tl_mbox::ipcc::Ipcc;
11 9
12pub struct Sys; 10pub struct Sys;
13 11
14impl Sys { 12impl Sys {
15 pub(crate) fn new(ipcc: &mut Ipcc) -> Self { 13 pub fn enable() {
16 unsafe { 14 unsafe {
17 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); 15 LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
18 16
19 TL_SYS_TABLE = MaybeUninit::new(SysTable { 17 TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {
20 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(), 18 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
21 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(), 19 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
22 }); 20 });
23 } 21 }
24 22
25 ipcc.c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true); 23 Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true);
26
27 Sys
28 } 24 }
29 25
30 pub(crate) fn evt_handler(ipcc: &mut Ipcc) { 26 pub fn evt_handler() {
31 unsafe { 27 unsafe {
32 let mut node_ptr = core::ptr::null_mut(); 28 let mut node_ptr = core::ptr::null_mut();
33 let node_ptr_ptr: *mut _ = &mut node_ptr; 29 let node_ptr_ptr: *mut _ = &mut node_ptr;
@@ -43,11 +39,11 @@ impl Sys {
43 } 39 }
44 } 40 }
45 41
46 ipcc.c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL); 42 Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL);
47 } 43 }
48 44
49 pub(crate) fn cmd_evt_handler(ipcc: &mut Ipcc) -> CcEvt { 45 pub fn cmd_evt_handler() -> CcEvt {
50 ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false); 46 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false);
51 47
52 // ST's command response data structure is really convoluted. 48 // ST's command response data structure is really convoluted.
53 // 49 //
@@ -68,11 +64,11 @@ impl Sys {
68 } 64 }
69 65
70 #[allow(dead_code)] 66 #[allow(dead_code)]
71 pub(crate) fn send_cmd(ipcc: &mut Ipcc, buf: &[u8]) { 67 pub fn send_cmd(buf: &[u8]) {
72 unsafe { 68 unsafe {
73 // TODO: check this 69 // TODO: check this
74 let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; 70 let cmd_buffer = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer;
75 let cmd_serial: *mut CmdSerial = &mut (*cmd_buffer).cmd_serial; 71 let cmd_serial: *mut CmdSerial = &mut cmd_buffer.cmd_serial;
76 let cmd_serial_buf = cmd_serial.cast(); 72 let cmd_serial_buf = cmd_serial.cast();
77 73
78 core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len()); 74 core::ptr::copy(buf.as_ptr(), cmd_serial_buf, buf.len());
@@ -80,8 +76,8 @@ impl Sys {
80 let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer; 76 let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().sys_table).pcmd_buffer;
81 cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8; 77 cmd_packet.cmd_serial.ty = TlPacketType::SysCmd as u8;
82 78
83 ipcc.c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL); 79 Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL);
84 ipcc.c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true); 80 Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true);
85 } 81 }
86 } 82 }
87} 83}
diff --git a/tests/stm32/memory_ble.x b/embassy-stm32/tl_mbox.x.in
index 4332e2c72..b6eecb429 100644
--- a/tests/stm32/memory_ble.x
+++ b/embassy-stm32/tl_mbox.x.in
@@ -1,21 +1,13 @@
1/* 1MEMORY
2 Memory size for STM32WB55xG with 512K FLASH
3*/
4
5MEMORY
6{ 2{
7 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
8 RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
9 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K 3 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
10} 4}
11 5
12/* Place stack at the end of SRAM1 */
13_stack_start = ORIGIN(RAM) + LENGTH(RAM);
14
15/* 6/*
16 * Scatter the mailbox interface memory sections in shared memory 7 * Scatter the mailbox interface memory sections in shared memory
17 */ 8 */
18SECTIONS { 9SECTIONS
10{
19 TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED 11 TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED
20 12
21 MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED 13 MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
index c4edbd38a..857da5467 100644
--- a/embassy-time/Cargo.toml
+++ b/embassy-time/Cargo.toml
@@ -169,4 +169,4 @@ wasm-timer = { version = "0.2.5", optional = true }
169[dev-dependencies] 169[dev-dependencies]
170serial_test = "0.9" 170serial_test = "0.9"
171critical-section = { version = "1.1", features = ["std"] } 171critical-section = { version = "1.1", features = ["std"] }
172 172embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly"] }
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
index 5c2ad3b23..d6436369b 100644
--- a/embassy-time/src/driver.rs
+++ b/embassy-time/src/driver.rs
@@ -49,7 +49,7 @@
49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
50//! todo!() 50//! todo!()
51//! } 51//! }
52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
53//! todo!() 53//! todo!()
54//! } 54//! }
55//! } 55//! }
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
index 64a8af4bc..4795eb2f3 100644
--- a/embassy-time/src/queue_generic.rs
+++ b/embassy-time/src/queue_generic.rs
@@ -183,7 +183,6 @@ mod tests {
183 183
184 use serial_test::serial; 184 use serial_test::serial;
185 185
186 use super::InnerQueue;
187 use crate::driver::{AlarmHandle, Driver}; 186 use crate::driver::{AlarmHandle, Driver};
188 use crate::queue_generic::QUEUE; 187 use crate::queue_generic::QUEUE;
189 use crate::Instant; 188 use crate::Instant;
@@ -317,14 +316,18 @@ mod tests {
317 316
318 fn setup() { 317 fn setup() {
319 DRIVER.reset(); 318 DRIVER.reset();
320 319 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
321 QUEUE.inner.lock(|inner| {
322 *inner.borrow_mut() = InnerQueue::new();
323 });
324 } 320 }
325 321
326 fn queue_len() -> usize { 322 fn queue_len() -> usize {
327 QUEUE.inner.lock(|inner| inner.borrow().queue.iter().count()) 323 critical_section::with(|cs| {
324 QUEUE
325 .inner
326 .borrow_ref(cs)
327 .as_ref()
328 .map(|inner| inner.queue.iter().count())
329 .unwrap_or(0)
330 })
328 } 331 }
329 332
330 #[test] 333 #[test]
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index 52620d233..d3d1f9f5f 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -109,7 +109,6 @@ impl Future for Timer {
109/// # #![feature(type_alias_impl_trait)] 109/// # #![feature(type_alias_impl_trait)]
110/// # 110/// #
111/// use embassy_time::{Duration, Ticker}; 111/// use embassy_time::{Duration, Ticker};
112/// use futures::StreamExt;
113/// # fn foo(){} 112/// # fn foo(){}
114/// 113///
115/// #[embassy_executor::task] 114/// #[embassy_executor::task]
diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml
index 3c7e3e874..8cfac772a 100644
--- a/examples/stm32wb/Cargo.toml
+++ b/examples/stm32wb/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } 9embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] }
12 12
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.4" 14defmt-rtt = "0.4"
diff --git a/examples/stm32wb/build.rs b/examples/stm32wb/build.rs
index 30691aa97..29b3a9b2a 100644
--- a/examples/stm32wb/build.rs
+++ b/examples/stm32wb/build.rs
@@ -1,35 +1,11 @@
1//! This build script copies the `memory.x` file from the crate root into 1use std::error::Error;
2//! a directory where the linker can always find it at build time.
3//! For many projects this is optional, as the linker always searches the
4//! project root directory -- wherever `Cargo.toml` is. However, if you
5//! are using a workspace or have a more complicated build setup, this
6//! build script becomes required. Additionally, by requesting that
7//! Cargo re-run the build script whenever `memory.x` is changed,
8//! updating `memory.x` ensures a rebuild of the application with the
9//! new memory settings.
10
11use std::env;
12use std::fs::File;
13use std::io::Write;
14use std::path::PathBuf;
15
16fn main() {
17 // Put `memory.x` in our output directory and ensure it's
18 // on the linker search path.
19 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
20 File::create(out.join("memory.x"))
21 .unwrap()
22 .write_all(include_bytes!("memory.x"))
23 .unwrap();
24 println!("cargo:rustc-link-search={}", out.display());
25
26 // By default, Cargo will re-run a build script whenever
27 // any file in the project changes. By specifying `memory.x`
28 // here, we ensure the build script is only re-run when
29 // `memory.x` is changed.
30 println!("cargo:rerun-if-changed=memory.x");
31 2
3fn main() -> Result<(), Box<dyn Error>> {
32 println!("cargo:rustc-link-arg-bins=--nmagic"); 4 println!("cargo:rustc-link-arg-bins=--nmagic");
33 println!("cargo:rustc-link-arg-bins=-Tlink.x"); 5 println!("cargo:rustc-link-arg-bins=-Tlink.x");
6 println!("cargo:rerun-if-changed=link.x");
7 println!("cargo:rustc-link-arg-bins=-Ttl_mbox.x");
34 println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); 8 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
9
10 Ok(())
35} 11}
diff --git a/examples/stm32wb/memory.x b/examples/stm32wb/memory.x
deleted file mode 100644
index e1f0530bd..000000000
--- a/examples/stm32wb/memory.x
+++ /dev/null
@@ -1,35 +0,0 @@
1/*
2 The size of this file must be exactly the same as in other memory_xx.x files.
3 Memory size for STM32WB55xC with 256K FLASH
4*/
5
6MEMORY
7{
8 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K
9 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
10 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
11}
12
13/*
14 Memory size for STM32WB55xG with 512K FLASH
15
16 MEMORY
17 {
18 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
19 RAM (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8
20 RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K
21 }
22*/
23
24/* Place stack at the end of SRAM1 */
25_stack_start = ORIGIN(RAM) + LENGTH(RAM);
26
27/*
28 * Scatter the mailbox interface memory sections in shared memory
29 */
30SECTIONS {
31 TL_REF_TABLE (NOLOAD) : { *(TL_REF_TABLE) } >RAM_SHARED
32
33 MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED
34 MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED
35}
diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs
index 326e4be85..8f4e70af0 100644
--- a/examples/stm32wb/src/bin/tl_mbox.rs
+++ b/examples/stm32wb/src/bin/tl_mbox.rs
@@ -4,8 +4,7 @@
4 4
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::ipcc::{Config, Ipcc}; 7use embassy_stm32::tl_mbox::{Config, TlMbox};
8use embassy_stm32::tl_mbox::TlMbox;
9use embassy_stm32::{bind_interrupts, tl_mbox}; 8use embassy_stm32::{bind_interrupts, tl_mbox};
10use embassy_time::{Duration, Timer}; 9use embassy_time::{Duration, Timer};
11use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
@@ -45,14 +44,12 @@ async fn main(_spawner: Spawner) {
45 info!("Hello World!"); 44 info!("Hello World!");
46 45
47 let config = Config::default(); 46 let config = Config::default();
48 let mut ipcc = Ipcc::new(p.IPCC, config); 47 let mbox = TlMbox::new(p.IPCC, Irqs, config);
49
50 let mbox = TlMbox::init(&mut ipcc, Irqs);
51 48
52 loop { 49 loop {
53 let wireless_fw_info = mbox.wireless_fw_info(); 50 let wireless_fw_info = mbox.wireless_fw_info();
54 match wireless_fw_info { 51 match wireless_fw_info {
55 None => error!("not yet initialized"), 52 None => info!("not yet initialized"),
56 Some(fw_info) => { 53 Some(fw_info) => {
57 let version_major = fw_info.version_major(); 54 let version_major = fw_info.version_major();
58 let version_minor = fw_info.version_minor(); 55 let version_minor = fw_info.version_minor();
@@ -70,6 +67,9 @@ async fn main(_spawner: Spawner) {
70 } 67 }
71 } 68 }
72 69
73 Timer::after(Duration::from_millis(500)).await; 70 Timer::after(Duration::from_millis(50)).await;
74 } 71 }
72
73 info!("Test OK");
74 cortex_m::asm::bkpt();
75} 75}
diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
index 7a69f26b7..1724d946f 100644
--- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
+++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
@@ -4,8 +4,7 @@
4 4
5use defmt::*; 5use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_stm32::ipcc::{Config, Ipcc}; 7use embassy_stm32::tl_mbox::{Config, TlMbox};
8use embassy_stm32::tl_mbox::TlMbox;
9use embassy_stm32::{bind_interrupts, tl_mbox}; 8use embassy_stm32::{bind_interrupts, tl_mbox};
10use {defmt_rtt as _, panic_probe as _}; 9use {defmt_rtt as _, panic_probe as _};
11 10
@@ -44,12 +43,7 @@ async fn main(_spawner: Spawner) {
44 info!("Hello World!"); 43 info!("Hello World!");
45 44
46 let config = Config::default(); 45 let config = Config::default();
47 let mut ipcc = Ipcc::new(p.IPCC, config); 46 let mbox = TlMbox::new(p.IPCC, Irqs, config);
48
49 let mbox = TlMbox::init(&mut ipcc, Irqs);
50
51 // initialize ble stack, does not return a response
52 mbox.shci_ble_init(&mut ipcc, Default::default());
53 47
54 info!("waiting for coprocessor to boot"); 48 info!("waiting for coprocessor to boot");
55 let event_box = mbox.read().await; 49 let event_box = mbox.read().await;
@@ -74,10 +68,11 @@ async fn main(_spawner: Spawner) {
74 ); 68 );
75 } 69 }
76 70
77 mbox.shci_ble_init(&mut ipcc, Default::default()); 71 // initialize ble stack, does not return a response
72 mbox.shci_ble_init(Default::default());
78 73
79 info!("resetting BLE"); 74 info!("resetting BLE");
80 mbox.send_ble_cmd(&mut ipcc, &[0x01, 0x03, 0x0c, 0x00, 0x00]); 75 mbox.send_ble_cmd(&[0x01, 0x03, 0x0c, 0x00, 0x00]);
81 76
82 let event_box = mbox.read().await; 77 let event_box = mbox.read().await;
83 78
@@ -92,8 +87,12 @@ async fn main(_spawner: Spawner) {
92 87
93 info!( 88 info!(
94 "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}", 89 "==> kind: {:#04x}, code: {:#04x}, payload_length: {}, payload: {:#04x}",
95 kind, code, payload_len, payload 90 kind,
91 code,
92 payload_len,
93 payload[3..]
96 ); 94 );
97 95
98 loop {} 96 info!("Test OK");
97 cortex_m::asm::bkpt();
99} 98}
diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml
index 29c4799a1..426d6e67f 100644
--- a/tests/stm32/.cargo/config.toml
+++ b/tests/stm32/.cargo/config.toml
@@ -17,4 +17,4 @@ rustflags = [
17target = "thumbv7m-none-eabi" 17target = "thumbv7m-none-eabi"
18 18
19[env] 19[env]
20DEFMT_LOG = "trace" 20DEFMT_LOG = "trace" \ No newline at end of file
diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml
index bd8d90abe..4c0597746 100644
--- a/tests/stm32/Cargo.toml
+++ b/tests/stm32/Cargo.toml
@@ -7,12 +7,12 @@ autobins = false
7 7
8[features] 8[features]
9stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill 9stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill
10stm32f429zi = ["embassy-stm32/stm32f429zi", "sdmmc", "chrono", "not-gpdma"] # Nucleo 10stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "not-gpdma"] # Nucleo "sdmmc"
11stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo 11stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo
12stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo 12stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo
13stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo 13stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo
14stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo 14stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo
15stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma"] # Nucleo 15stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble"] # Nucleo
16stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo 16stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo
17stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board 17stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board
18 18
@@ -45,8 +45,8 @@ chrono = { version = "^0.4", default-features = false, optional = true}
45# BEGIN TESTS 45# BEGIN TESTS
46# Generated by gen_test.py. DO NOT EDIT. 46# Generated by gen_test.py. DO NOT EDIT.
47[[bin]] 47[[bin]]
48name = "ble" 48name = "tl_mbox"
49path = "src/bin/ble.rs" 49path = "src/bin/tl_mbox.rs"
50required-features = [ "ble",] 50required-features = [ "ble",]
51 51
52[[bin]] 52[[bin]]
diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs
index ca76b70bb..b4583147e 100644
--- a/tests/stm32/build.rs
+++ b/tests/stm32/build.rs
@@ -9,17 +9,22 @@ fn main() -> Result<(), Box<dyn Error>> {
9 println!("cargo:rustc-link-arg-bins=--nmagic"); 9 println!("cargo:rustc-link-arg-bins=--nmagic");
10 10
11 // too little RAM to run from RAM. 11 // too little RAM to run from RAM.
12 if cfg!(any(feature = "stm32f103c8", feature = "stm32c031c6")) { 12 if cfg!(any(
13 feature = "stm32f103c8",
14 feature = "stm32c031c6",
15 feature = "stm32wb55rg"
16 )) {
13 println!("cargo:rustc-link-arg-bins=-Tlink.x"); 17 println!("cargo:rustc-link-arg-bins=-Tlink.x");
14 println!("cargo:rerun-if-changed=link.x"); 18 println!("cargo:rerun-if-changed=link.x");
15 } else if cfg!(feature = "stm32wb55rg") {
16 println!("cargo:rustc-link-arg-bins=-Tlink.x");
17 fs::write(out.join("memory.x"), include_bytes!("memory_ble.x")).unwrap();
18 } else { 19 } else {
19 println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); 20 println!("cargo:rustc-link-arg-bins=-Tlink_ram.x");
20 println!("cargo:rerun-if-changed=link_ram.x"); 21 println!("cargo:rerun-if-changed=link_ram.x");
21 } 22 }
22 23
24 if cfg!(feature = "stm32wb55rg") {
25 println!("cargo:rustc-link-arg-bins=-Ttl_mbox.x");
26 }
27
23 println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); 28 println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
24 29
25 Ok(()) 30 Ok(())
diff --git a/tests/stm32/src/bin/ble.rs b/tests/stm32/src/bin/tl_mbox.rs
index aedf9a380..626e7ac6f 100644
--- a/tests/stm32/src/bin/ble.rs
+++ b/tests/stm32/src/bin/tl_mbox.rs
@@ -7,24 +7,23 @@
7#[path = "../example_common.rs"] 7#[path = "../example_common.rs"]
8mod example_common; 8mod example_common;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_stm32::interrupt; 10use embassy_stm32::tl_mbox::{Config, TlMbox};
11use embassy_stm32::ipcc::{Config, Ipcc}; 11use embassy_stm32::{bind_interrupts, tl_mbox};
12use embassy_stm32::tl_mbox::TlMbox;
13use embassy_time::{Duration, Timer}; 12use embassy_time::{Duration, Timer};
14use example_common::*; 13use example_common::*;
15 14
15bind_interrupts!(struct Irqs{
16 IPCC_C1_RX => tl_mbox::ReceiveInterruptHandler;
17 IPCC_C1_TX => tl_mbox::TransmitInterruptHandler;
18});
19
16#[embassy_executor::main] 20#[embassy_executor::main]
17async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
18 let p = embassy_stm32::init(config()); 22 let p = embassy_stm32::init(config());
19 info!("Hello World!"); 23 info!("Hello World!");
20 24
21 let config = Config::default(); 25 let config = Config::default();
22 let mut ipcc = Ipcc::new(p.IPCC, config); 26 let mbox = TlMbox::new(p.IPCC, Irqs, config);
23
24 let rx_irq = interrupt::take!(IPCC_C1_RX);
25 let tx_irq = interrupt::take!(IPCC_C1_TX);
26
27 let mbox = TlMbox::init(&mut ipcc, rx_irq, tx_irq);
28 27
29 loop { 28 loop {
30 let wireless_fw_info = mbox.wireless_fw_info(); 29 let wireless_fw_info = mbox.wireless_fw_info();