aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config27
-rw-r--r--.gitignore2
-rw-r--r--.vscode/settings.json12
-rw-r--r--Cargo.toml46
-rw-r--r--LICENSE-APACHE201
-rw-r--r--LICENSE-MIT25
-rw-r--r--README.md30
-rw-r--r--embassy-nrf/Cargo.toml38
-rw-r--r--embassy-nrf/src/interrupt.rs131
-rw-r--r--embassy-nrf/src/lib.rs43
-rw-r--r--embassy-nrf/src/qspi.rs322
-rw-r--r--embassy-nrf/src/uarte.rs550
-rw-r--r--embassy/Cargo.toml14
-rw-r--r--embassy/src/flash.rs51
-rw-r--r--embassy/src/io/error.rs133
-rw-r--r--embassy/src/io/mod.rs7
-rw-r--r--embassy/src/io/traits.rs197
-rw-r--r--embassy/src/io/util/copy_buf.rs80
-rw-r--r--embassy/src/io/util/mod.rs145
-rw-r--r--embassy/src/io/util/read.rs39
-rw-r--r--embassy/src/io/util/read_buf.rs34
-rw-r--r--embassy/src/io/util/read_byte.rs36
-rw-r--r--embassy/src/io/util/read_exact.rs48
-rw-r--r--embassy/src/io/util/read_to_end.rs48
-rw-r--r--embassy/src/io/util/read_while.rs61
-rw-r--r--embassy/src/io/util/skip_while.rs45
-rw-r--r--embassy/src/io/util/split.rs40
-rw-r--r--embassy/src/io/util/write.rs33
-rw-r--r--embassy/src/io/util/write_all.rs44
-rw-r--r--embassy/src/io/util/write_byte.rs39
-rw-r--r--embassy/src/lib.rs8
-rw-r--r--embassy/src/util/drop_bomb.rs21
-rw-r--r--embassy/src/util/macros.rs32
-rw-r--r--embassy/src/util/mod.rs70
-rw-r--r--embassy/src/util/portal.rs125
-rw-r--r--embassy/src/util/signal.rs70
-rw-r--r--embassy/src/util/waker_store.rs23
-rw-r--r--examples/Cargo.toml31
-rw-r--r--examples/build.rs31
-rw-r--r--examples/memory.x7
-rw-r--r--examples/src/bin/qspi.rs123
-rw-r--r--examples/src/bin/uart.rs72
-rw-r--r--examples/src/example_common.rs68
43 files changed, 3202 insertions, 0 deletions
diff --git a/.cargo/config b/.cargo/config
new file mode 100644
index 000000000..3f319ae55
--- /dev/null
+++ b/.cargo/config
@@ -0,0 +1,27 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2runner = "probe-run --chip nRF52840_xxAA --defmt"
3
4rustflags = [
5 # LLD (shipped with the Rust toolchain) is used as the default linker
6 "-C", "link-arg=--nmagic",
7 "-C", "link-arg=-Tlink.x",
8 "-C", "link-arg=-Tdefmt.x",
9
10 # if you run into problems with LLD switch to the GNU linker by commenting out
11 # this line
12 # "-C", "linker=arm-none-eabi-ld",
13
14 # if you need to link to pre-compiled C libraries provided by a C toolchain
15 # use GCC as the linker by commenting out both lines above and then
16 # uncommenting the three lines below
17 # "-C", "linker=arm-none-eabi-gcc",
18 # "-C", "link-arg=-Wl,-Tlink.x",
19 # "-C", "link-arg=-nostartfiles",
20]
21
22[build]
23# Pick ONE of these compilation targets
24# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
25# target = "thumbv7m-none-eabi" # Cortex-M3
26# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
27target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..96ef6c0b9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
1/target
2Cargo.lock
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..ef95cf96e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,12 @@
1{
2 "editor.formatOnSave": true,
3 "rust-analyzer.cargo.allFeatures": false,
4 "rust-analyzer.checkOnSave.allFeatures": false,
5 "rust-analyzer.cargo.target": "thumbv7em-none-eabihf",
6 "rust-analyzer.checkOnSave.allTargets": false,
7 "files.watcherExclude": {
8 "**/.git/objects/**": true,
9 "**/.git/subtree-cache/**": true,
10 "**/target/**": true
11 }
12}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 000000000..4515b020c
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,46 @@
1
2[workspace]
3members = [
4 "embassy",
5 "embassy-nrf",
6 "examples",
7]
8
9[patch.crates-io]
10panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" }
11defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" }
12defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" }
13static-executor = { git = "https://github.com/Dirbaio/static-executor" }
14static-executor-cortex-m = { git = "https://github.com/Dirbaio/static-executor" }
15
16[profile.dev]
17codegen-units = 1
18debug = 2
19debug-assertions = true
20incremental = false
21opt-level = 3
22overflow-checks = true
23
24[profile.release]
25codegen-units = 1
26debug = 2
27debug-assertions = false
28incremental = false
29lto = "fat"
30opt-level = 3
31overflow-checks = false
32
33# do not optimize proc-macro crates = faster builds from scratch
34[profile.dev.build-override]
35codegen-units = 8
36debug = false
37debug-assertions = false
38opt-level = 0
39overflow-checks = false
40
41[profile.release.build-override]
42codegen-units = 8
43debug = false
44debug-assertions = false
45opt-level = 0
46overflow-checks = false
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 000000000..16fe87b06
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
1 Apache License
2 Version 2.0, January 2004
3 http://www.apache.org/licenses/
4
5TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
71. Definitions.
8
9 "License" shall mean the terms and conditions for use, reproduction,
10 and distribution as defined by Sections 1 through 9 of this document.
11
12 "Licensor" shall mean the copyright owner or entity authorized by
13 the copyright owner that is granting the License.
14
15 "Legal Entity" shall mean the union of the acting entity and all
16 other entities that control, are controlled by, or are under common
17 control with that entity. For the purposes of this definition,
18 "control" means (i) the power, direct or indirect, to cause the
19 direction or management of such entity, whether by contract or
20 otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 outstanding shares, or (iii) beneficial ownership of such entity.
22
23 "You" (or "Your") shall mean an individual or Legal Entity
24 exercising permissions granted by this License.
25
26 "Source" form shall mean the preferred form for making modifications,
27 including but not limited to software source code, documentation
28 source, and configuration files.
29
30 "Object" form shall mean any form resulting from mechanical
31 transformation or translation of a Source form, including but
32 not limited to compiled object code, generated documentation,
33 and conversions to other media types.
34
35 "Work" shall mean the work of authorship, whether in Source or
36 Object form, made available under the License, as indicated by a
37 copyright notice that is included in or attached to the work
38 (an example is provided in the Appendix below).
39
40 "Derivative Works" shall mean any work, whether in Source or Object
41 form, that is based on (or derived from) the Work and for which the
42 editorial revisions, annotations, elaborations, or other modifications
43 represent, as a whole, an original work of authorship. For the purposes
44 of this License, Derivative Works shall not include works that remain
45 separable from, or merely link (or bind by name) to the interfaces of,
46 the Work and Derivative Works thereof.
47
48 "Contribution" shall mean any work of authorship, including
49 the original version of the Work and any modifications or additions
50 to that Work or Derivative Works thereof, that is intentionally
51 submitted to Licensor for inclusion in the Work by the copyright owner
52 or by an individual or Legal Entity authorized to submit on behalf of
53 the copyright owner. For the purposes of this definition, "submitted"
54 means any form of electronic, verbal, or written communication sent
55 to the Licensor or its representatives, including but not limited to
56 communication on electronic mailing lists, source code control systems,
57 and issue tracking systems that are managed by, or on behalf of, the
58 Licensor for the purpose of discussing and improving the Work, but
59 excluding communication that is conspicuously marked or otherwise
60 designated in writing by the copyright owner as "Not a Contribution."
61
62 "Contributor" shall mean Licensor and any individual or Legal Entity
63 on behalf of whom a Contribution has been received by Licensor and
64 subsequently incorporated within the Work.
65
662. Grant of Copyright License. Subject to the terms and conditions of
67 this License, each Contributor hereby grants to You a perpetual,
68 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 copyright license to reproduce, prepare Derivative Works of,
70 publicly display, publicly perform, sublicense, and distribute the
71 Work and such Derivative Works in Source or Object form.
72
733. Grant of Patent License. Subject to the terms and conditions of
74 this License, each Contributor hereby grants to You a perpetual,
75 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 (except as stated in this section) patent license to make, have made,
77 use, offer to sell, sell, import, and otherwise transfer the Work,
78 where such license applies only to those patent claims licensable
79 by such Contributor that are necessarily infringed by their
80 Contribution(s) alone or by combination of their Contribution(s)
81 with the Work to which such Contribution(s) was submitted. If You
82 institute patent litigation against any entity (including a
83 cross-claim or counterclaim in a lawsuit) alleging that the Work
84 or a Contribution incorporated within the Work constitutes direct
85 or contributory patent infringement, then any patent licenses
86 granted to You under this License for that Work shall terminate
87 as of the date such litigation is filed.
88
894. Redistribution. You may reproduce and distribute copies of the
90 Work or Derivative Works thereof in any medium, with or without
91 modifications, and in Source or Object form, provided that You
92 meet the following conditions:
93
94 (a) You must give any other recipients of the Work or
95 Derivative Works a copy of this License; and
96
97 (b) You must cause any modified files to carry prominent notices
98 stating that You changed the files; and
99
100 (c) You must retain, in the Source form of any Derivative Works
101 that You distribute, all copyright, patent, trademark, and
102 attribution notices from the Source form of the Work,
103 excluding those notices that do not pertain to any part of
104 the Derivative Works; and
105
106 (d) If the Work includes a "NOTICE" text file as part of its
107 distribution, then any Derivative Works that You distribute must
108 include a readable copy of the attribution notices contained
109 within such NOTICE file, excluding those notices that do not
110 pertain to any part of the Derivative Works, in at least one
111 of the following places: within a NOTICE text file distributed
112 as part of the Derivative Works; within the Source form or
113 documentation, if provided along with the Derivative Works; or,
114 within a display generated by the Derivative Works, if and
115 wherever such third-party notices normally appear. The contents
116 of the NOTICE file are for informational purposes only and
117 do not modify the License. You may add Your own attribution
118 notices within Derivative Works that You distribute, alongside
119 or as an addendum to the NOTICE text from the Work, provided
120 that such additional attribution notices cannot be construed
121 as modifying the License.
122
123 You may add Your own copyright statement to Your modifications and
124 may provide additional or different license terms and conditions
125 for use, reproduction, or distribution of Your modifications, or
126 for any such Derivative Works as a whole, provided Your use,
127 reproduction, and distribution of the Work otherwise complies with
128 the conditions stated in this License.
129
1305. Submission of Contributions. Unless You explicitly state otherwise,
131 any Contribution intentionally submitted for inclusion in the Work
132 by You to the Licensor shall be under the terms and conditions of
133 this License, without any additional terms or conditions.
134 Notwithstanding the above, nothing herein shall supersede or modify
135 the terms of any separate license agreement you may have executed
136 with Licensor regarding such Contributions.
137
1386. Trademarks. This License does not grant permission to use the trade
139 names, trademarks, service marks, or product names of the Licensor,
140 except as required for reasonable and customary use in describing the
141 origin of the Work and reproducing the content of the NOTICE file.
142
1437. Disclaimer of Warranty. Unless required by applicable law or
144 agreed to in writing, Licensor provides the Work (and each
145 Contributor provides its Contributions) on an "AS IS" BASIS,
146 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 implied, including, without limitation, any warranties or conditions
148 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 PARTICULAR PURPOSE. You are solely responsible for determining the
150 appropriateness of using or redistributing the Work and assume any
151 risks associated with Your exercise of permissions under this License.
152
1538. Limitation of Liability. In no event and under no legal theory,
154 whether in tort (including negligence), contract, or otherwise,
155 unless required by applicable law (such as deliberate and grossly
156 negligent acts) or agreed to in writing, shall any Contributor be
157 liable to You for damages, including any direct, indirect, special,
158 incidental, or consequential damages of any character arising as a
159 result of this License or out of the use or inability to use the
160 Work (including but not limited to damages for loss of goodwill,
161 work stoppage, computer failure or malfunction, or any and all
162 other commercial damages or losses), even if such Contributor
163 has been advised of the possibility of such damages.
164
1659. Accepting Warranty or Additional Liability. While redistributing
166 the Work or Derivative Works thereof, You may choose to offer,
167 and charge a fee for, acceptance of support, warranty, indemnity,
168 or other liability obligations and/or rights consistent with this
169 License. However, in accepting such obligations, You may act only
170 on Your own behalf and on Your sole responsibility, not on behalf
171 of any other Contributor, and only if You agree to indemnify,
172 defend, and hold each Contributor harmless for any liability
173 incurred by, or claims asserted against, such Contributor by reason
174 of your accepting any such warranty or additional liability.
175
176END OF TERMS AND CONDITIONS
177
178APPENDIX: How to apply the Apache License to your work.
179
180 To apply the Apache License to your work, attach the following
181 boilerplate notice, with the fields enclosed by brackets "[]"
182 replaced with your own identifying information. (Don't include
183 the brackets!) The text should be enclosed in the appropriate
184 comment syntax for the file format. We also recommend that a
185 file or class name and description of purpose be included on the
186 same "printed page" as the copyright notice for easier
187 identification within third-party archives.
188
189Copyright [yyyy] [name of copyright owner]
190
191Licensed under the Apache License, Version 2.0 (the "License");
192you may not use this file except in compliance with the License.
193You may obtain a copy of the License at
194
195 http://www.apache.org/licenses/LICENSE-2.0
196
197Unless required by applicable law or agreed to in writing, software
198distributed under the License is distributed on an "AS IS" BASIS,
199WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200See the License for the specific language governing permissions and
201limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 000000000..dacc57b2b
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
1Copyright (c) 2020 Dario Nieuwenhuis
2
3Permission is hereby granted, free of charge, to any
4person obtaining a copy of this software and associated
5documentation files (the "Software"), to deal in the
6Software without restriction, including without
7limitation the rights to use, copy, modify, merge,
8publish, distribute, sublicense, and/or sell copies of
9the Software, and to permit persons to whom the Software
10is furnished to do so, subject to the following
11conditions:
12
13The above copyright notice and this permission notice
14shall be included in all copies or substantial portions
15of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..ffa60a864
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
1# Embassy
2
3Embassy is a project to make async/await a first-class option for embedded development.
4
5The `embassy` crate defines some traits.
6
7- `embassy::io`: Traits for byte-stream IO, essentially `no_std` compatible versions of `futures::io`.
8- `embassy::flash`: Trait for an async flash device.
9- More traits for SPI, I2C, UART async HAL coming soon.
10
11The `embassy-nrf` crate contains implementations for nRF 52 series SoCs.
12
13- `uarte`: UARTE driver implementing `AsyncBufRead` and `AsyncWrite`.
14- `qspi`: QSPI driver implementing `Flash`.
15
16Currently Embassy requires a recent nightly, mainly for `generic_associated_types` (for trait funcs returning futures) and `type_alias_impl_trait` (for returning futures implemented with `async{}` blocks). Stable support is a non-goal.
17
18## Why the name?
19
20EMBedded ASYnc.
21
22## License
23
24This work is licensed under either of
25
26- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
27 http://www.apache.org/licenses/LICENSE-2.0)
28- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
29
30at your option.
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
new file mode 100644
index 000000000..b367de839
--- /dev/null
+++ b/embassy-nrf/Cargo.toml
@@ -0,0 +1,38 @@
1[package]
2name = "embassy-nrf"
3version = "0.1.0"
4authors = ["Dario Nieuwenhuis <[email protected]>"]
5edition = "2018"
6
7[features]
8default = [
9 "defmt-default",
10]
11defmt-default = []
12defmt-trace = []
13defmt-debug = []
14defmt-info = []
15defmt-warn = []
16defmt-error = []
17
18nrf52810 = ["nrf52810-pac"]
19nrf52811 = ["nrf52811-pac"]
20nrf52832 = ["nrf52832-pac"]
21nrf52833 = ["nrf52833-pac"]
22nrf52840 = ["nrf52840-pac"]
23
24
25[dependencies]
26embassy = { version = "0.1.0", path = "../embassy" }
27cortex-m-rt = "0.6.12"
28cortex-m = { version = "0.6.3" }
29embedded-hal = { version = "0.2.4" }
30nrf52840-hal = { version = "0.11.0" }
31bare-metal = { version = "0.2.0", features = ["const-fn"] }
32defmt = "0.1.0"
33
34nrf52810-pac = { version = "0.9.0", optional = true }
35nrf52811-pac = { version = "0.9.0", optional = true }
36nrf52832-pac = { version = "0.9.0", optional = true }
37nrf52833-pac = { version = "0.9.0", optional = true }
38nrf52840-pac = { version = "0.9.0", optional = true }
diff --git a/embassy-nrf/src/interrupt.rs b/embassy-nrf/src/interrupt.rs
new file mode 100644
index 000000000..e227032cb
--- /dev/null
+++ b/embassy-nrf/src/interrupt.rs
@@ -0,0 +1,131 @@
1//! Interrupt management
2//!
3//! This module implements an API for managing interrupts compatible with
4//! nrf_softdevice::interrupt. Intended for switching between the two at compile-time.
5
6use core::sync::atomic::{compiler_fence, AtomicBool, Ordering};
7
8use crate::pac::{NVIC, NVIC_PRIO_BITS};
9
10// Re-exports
11pub use crate::pac::Interrupt;
12pub use crate::pac::Interrupt::*; // needed for cortex-m-rt #[interrupt]
13pub use bare_metal::{CriticalSection, Mutex};
14
15#[derive(defmt::Format, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
16#[repr(u8)]
17pub enum Priority {
18 Level0 = 0,
19 Level1 = 1,
20 Level2 = 2,
21 Level3 = 3,
22 Level4 = 4,
23 Level5 = 5,
24 Level6 = 6,
25 Level7 = 7,
26}
27
28impl Priority {
29 #[inline]
30 fn to_nvic(self) -> u8 {
31 (self as u8) << (8 - NVIC_PRIO_BITS)
32 }
33
34 #[inline]
35 fn from_nvic(priority: u8) -> Self {
36 match priority >> (8 - NVIC_PRIO_BITS) {
37 0 => Self::Level0,
38 1 => Self::Level1,
39 2 => Self::Level2,
40 3 => Self::Level3,
41 4 => Self::Level4,
42 5 => Self::Level5,
43 6 => Self::Level6,
44 7 => Self::Level7,
45 _ => unreachable!(),
46 }
47 }
48}
49
50static CS_FLAG: AtomicBool = AtomicBool::new(false);
51static mut CS_MASK: [u32; 2] = [0; 2];
52
53#[inline]
54pub fn free<F, R>(f: F) -> R
55where
56 F: FnOnce(&CriticalSection) -> R,
57{
58 unsafe {
59 // TODO: assert that we're in privileged level
60 // Needed because disabling irqs in non-privileged level is a noop, which would break safety.
61
62 let primask: u32;
63 asm!("mrs {}, PRIMASK", out(reg) primask);
64
65 asm!("cpsid i");
66
67 // Prevent compiler from reordering operations inside/outside the critical section.
68 compiler_fence(Ordering::SeqCst);
69
70 let r = f(&CriticalSection::new());
71
72 compiler_fence(Ordering::SeqCst);
73
74 if primask & 1 == 0 {
75 asm!("cpsie i");
76 }
77
78 r
79 }
80}
81
82#[inline]
83pub fn enable(irq: Interrupt) {
84 unsafe {
85 NVIC::unmask(irq);
86 }
87}
88
89#[inline]
90pub fn disable(irq: Interrupt) {
91 NVIC::mask(irq);
92}
93
94#[inline]
95pub fn is_active(irq: Interrupt) -> bool {
96 NVIC::is_active(irq)
97}
98
99#[inline]
100pub fn is_enabled(irq: Interrupt) -> bool {
101 NVIC::is_enabled(irq)
102}
103
104#[inline]
105pub fn is_pending(irq: Interrupt) -> bool {
106 NVIC::is_pending(irq)
107}
108
109#[inline]
110pub fn pend(irq: Interrupt) {
111 NVIC::pend(irq)
112}
113
114#[inline]
115pub fn unpend(irq: Interrupt) {
116 NVIC::unpend(irq)
117}
118
119#[inline]
120pub fn get_priority(irq: Interrupt) -> Priority {
121 Priority::from_nvic(NVIC::get_priority(irq))
122}
123
124#[inline]
125pub fn set_priority(irq: Interrupt, prio: Priority) {
126 unsafe {
127 cortex_m::peripheral::Peripherals::steal()
128 .NVIC
129 .set_priority(irq, prio.to_nvic())
130 }
131}
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
new file mode 100644
index 000000000..f1ce0cbf9
--- /dev/null
+++ b/embassy-nrf/src/lib.rs
@@ -0,0 +1,43 @@
1#![no_std]
2#![feature(generic_associated_types)]
3#![feature(asm)]
4#![feature(type_alias_impl_trait)]
5
6#[cfg(not(any(
7 feature = "nrf52810",
8 feature = "nrf52811",
9 feature = "nrf52832",
10 feature = "nrf52833",
11 feature = "nrf52840",
12)))]
13compile_error!("No chip feature activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840");
14
15#[cfg(any(
16 all(feature = "nrf52810", feature = "nrf52811"),
17 all(feature = "nrf52810", feature = "nrf52832"),
18 all(feature = "nrf52810", feature = "nrf52833"),
19 all(feature = "nrf52810", feature = "nrf52840"),
20 all(feature = "nrf52811", feature = "nrf52832"),
21 all(feature = "nrf52811", feature = "nrf52833"),
22 all(feature = "nrf52811", feature = "nrf52840"),
23 all(feature = "nrf52832", feature = "nrf52833"),
24 all(feature = "nrf52832", feature = "nrf52840"),
25 all(feature = "nrf52833", feature = "nrf52840"),
26))]
27compile_error!("Multile chip features activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840");
28
29#[cfg(feature = "nrf52810")]
30pub use nrf52810_pac as pac;
31#[cfg(feature = "nrf52811")]
32pub use nrf52811_pac as pac;
33#[cfg(feature = "nrf52832")]
34pub use nrf52832_pac as pac;
35#[cfg(feature = "nrf52833")]
36pub use nrf52833_pac as pac;
37#[cfg(feature = "nrf52840")]
38pub use nrf52840_pac as pac;
39
40pub mod interrupt;
41pub mod qspi;
42pub mod uarte;
43pub use cortex_m_rt::interrupt;
diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs
new file mode 100644
index 000000000..d2caddee0
--- /dev/null
+++ b/embassy-nrf/src/qspi.rs
@@ -0,0 +1,322 @@
1use crate::pac::{Interrupt, QSPI};
2use core::future::Future;
3use nrf52840_hal::gpio::{Output, Pin as GpioPin, Port as GpioPort, PushPull};
4
5pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode;
6pub use crate::pac::qspi::ifconfig0::PPSIZE_A as WritePageSize;
7pub use crate::pac::qspi::ifconfig0::READOC_A as ReadOpcode;
8pub use crate::pac::qspi::ifconfig0::WRITEOC_A as WriteOpcode;
9
10// TODO
11// - config:
12// - 32bit address mode
13// - SPI freq
14// - SPI sck delay
15// - Deep power down mode (DPM)
16// - SPI mode 3
17// - activate/deactivate
18// - set gpio in high drive
19
20use embassy::flash::{Error, Flash};
21use embassy::util::{DropBomb, Signal};
22
23use crate::interrupt;
24
25pub struct Pins {
26 pub sck: GpioPin<Output<PushPull>>,
27 pub csn: GpioPin<Output<PushPull>>,
28 pub io0: GpioPin<Output<PushPull>>,
29 pub io1: GpioPin<Output<PushPull>>,
30 pub io2: Option<GpioPin<Output<PushPull>>>,
31 pub io3: Option<GpioPin<Output<PushPull>>>,
32}
33
34pub struct Config {
35 pub pins: Pins,
36 pub xip_offset: u32,
37 pub read_opcode: ReadOpcode,
38 pub write_opcode: WriteOpcode,
39 pub write_page_size: WritePageSize,
40}
41
42pub struct Qspi {
43 inner: QSPI,
44}
45
46fn port_bit(port: GpioPort) -> bool {
47 match port {
48 GpioPort::Port0 => false,
49 GpioPort::Port1 => true,
50 }
51}
52
53impl Qspi {
54 pub fn new(qspi: QSPI, config: Config) -> Self {
55 qspi.psel.sck.write(|w| {
56 let pin = &config.pins.sck;
57 let w = unsafe { w.pin().bits(pin.pin()) };
58 let w = w.port().bit(port_bit(pin.port()));
59 w.connect().connected()
60 });
61 qspi.psel.csn.write(|w| {
62 let pin = &config.pins.csn;
63 let w = unsafe { w.pin().bits(pin.pin()) };
64 let w = w.port().bit(port_bit(pin.port()));
65 w.connect().connected()
66 });
67 qspi.psel.io0.write(|w| {
68 let pin = &config.pins.io0;
69 let w = unsafe { w.pin().bits(pin.pin()) };
70 let w = w.port().bit(port_bit(pin.port()));
71 w.connect().connected()
72 });
73 qspi.psel.io1.write(|w| {
74 let pin = &config.pins.io1;
75 let w = unsafe { w.pin().bits(pin.pin()) };
76 let w = w.port().bit(port_bit(pin.port()));
77 w.connect().connected()
78 });
79 qspi.psel.io2.write(|w| {
80 if let Some(ref pin) = config.pins.io2 {
81 let w = unsafe { w.pin().bits(pin.pin()) };
82 let w = w.port().bit(port_bit(pin.port()));
83 w.connect().connected()
84 } else {
85 w.connect().disconnected()
86 }
87 });
88 qspi.psel.io3.write(|w| {
89 if let Some(ref pin) = config.pins.io3 {
90 let w = unsafe { w.pin().bits(pin.pin()) };
91 let w = w.port().bit(port_bit(pin.port()));
92 w.connect().connected()
93 } else {
94 w.connect().disconnected()
95 }
96 });
97
98 qspi.ifconfig0.write(|w| {
99 let w = w.addrmode().variant(AddressMode::_24BIT);
100 let w = w.dpmenable().disable();
101 let w = w.ppsize().variant(config.write_page_size);
102 let w = w.readoc().variant(config.read_opcode);
103 let w = w.writeoc().variant(config.write_opcode);
104 w
105 });
106
107 qspi.ifconfig1.write(|w| {
108 let w = unsafe { w.sckdelay().bits(80) };
109 let w = w.dpmen().exit();
110 let w = w.spimode().mode0();
111 let w = unsafe { w.sckfreq().bits(3) };
112 w
113 });
114
115 qspi.xipoffset
116 .write(|w| unsafe { w.xipoffset().bits(config.xip_offset) });
117
118 // Enable it
119 qspi.enable.write(|w| w.enable().enabled());
120
121 qspi.events_ready.reset();
122 qspi.tasks_activate.write(|w| w.tasks_activate().bit(true));
123 while qspi.events_ready.read().bits() == 0 {}
124 qspi.events_ready.reset();
125
126 // Enable READY interrupt
127 qspi.intenset.write(|w| w.ready().set());
128 interrupt::set_priority(Interrupt::QSPI, interrupt::Priority::Level7);
129 interrupt::enable(Interrupt::QSPI);
130
131 Self { inner: qspi }
132 }
133
134 pub fn custom_instruction<'a>(
135 &'a mut self,
136 opcode: u8,
137 req: &'a [u8],
138 resp: &'a mut [u8],
139 ) -> impl Future<Output = Result<(), Error>> + 'a {
140 async move {
141 let bomb = DropBomb::new();
142
143 assert!(req.len() <= 8);
144 assert!(resp.len() <= 8);
145
146 let mut dat0: u32 = 0;
147 let mut dat1: u32 = 0;
148
149 for i in 0..4 {
150 if i < req.len() {
151 dat0 |= (req[i] as u32) << (i * 8);
152 }
153 }
154 for i in 0..4 {
155 if i + 4 < req.len() {
156 dat1 |= (req[i + 4] as u32) << (i * 8);
157 }
158 }
159
160 let len = core::cmp::max(req.len(), resp.len()) as u8;
161
162 self.inner.cinstrdat0.write(|w| unsafe { w.bits(dat0) });
163 self.inner.cinstrdat1.write(|w| unsafe { w.bits(dat1) });
164 self.inner.events_ready.reset();
165 self.inner.cinstrconf.write(|w| {
166 let w = unsafe { w.opcode().bits(opcode) };
167 let w = unsafe { w.length().bits(len + 1) };
168 let w = w.lio2().bit(true);
169 let w = w.lio3().bit(true);
170 let w = w.wipwait().bit(true);
171 let w = w.wren().bit(true);
172 let w = w.lfen().bit(false);
173 let w = w.lfstop().bit(false);
174 w
175 });
176
177 SIGNAL.wait().await;
178
179 let dat0 = self.inner.cinstrdat0.read().bits();
180 let dat1 = self.inner.cinstrdat1.read().bits();
181 for i in 0..4 {
182 if i < resp.len() {
183 resp[i] = (dat0 >> (i * 8)) as u8;
184 }
185 }
186 for i in 0..4 {
187 if i + 4 < resp.len() {
188 resp[i] = (dat1 >> (i * 8)) as u8;
189 }
190 }
191
192 bomb.defuse();
193
194 Ok(())
195 }
196 }
197}
198
199impl Flash for Qspi {
200 type ReadFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
201 type WriteFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
202 type ErasePageFuture<'a> = impl Future<Output = Result<(), Error>> + 'a;
203
204 fn read<'a>(&'a mut self, address: usize, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
205 async move {
206 let bomb = DropBomb::new();
207
208 assert_eq!(data.as_ptr() as u32 % 4, 0);
209 assert_eq!(data.len() as u32 % 4, 0);
210 assert_eq!(address as u32 % 4, 0);
211
212 self.inner
213 .read
214 .src
215 .write(|w| unsafe { w.src().bits(address as u32) });
216 self.inner
217 .read
218 .dst
219 .write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) });
220 self.inner
221 .read
222 .cnt
223 .write(|w| unsafe { w.cnt().bits(data.len() as u32) });
224
225 self.inner.events_ready.reset();
226 self.inner
227 .tasks_readstart
228 .write(|w| w.tasks_readstart().bit(true));
229
230 SIGNAL.wait().await;
231
232 bomb.defuse();
233
234 Ok(())
235 }
236 }
237
238 fn write<'a>(&'a mut self, address: usize, data: &'a [u8]) -> Self::WriteFuture<'a> {
239 async move {
240 let bomb = DropBomb::new();
241
242 assert_eq!(data.as_ptr() as u32 % 4, 0);
243 assert_eq!(data.len() as u32 % 4, 0);
244 assert_eq!(address as u32 % 4, 0);
245
246 self.inner
247 .write
248 .src
249 .write(|w| unsafe { w.src().bits(data.as_ptr() as u32) });
250 self.inner
251 .write
252 .dst
253 .write(|w| unsafe { w.dst().bits(address as u32) });
254 self.inner
255 .write
256 .cnt
257 .write(|w| unsafe { w.cnt().bits(data.len() as u32) });
258
259 self.inner.events_ready.reset();
260 self.inner
261 .tasks_writestart
262 .write(|w| w.tasks_writestart().bit(true));
263
264 SIGNAL.wait().await;
265
266 bomb.defuse();
267
268 Ok(())
269 }
270 }
271
272 fn erase<'a>(&'a mut self, address: usize) -> Self::ErasePageFuture<'a> {
273 async move {
274 let bomb = DropBomb::new();
275
276 assert_eq!(address as u32 % 4096, 0);
277
278 self.inner
279 .erase
280 .ptr
281 .write(|w| unsafe { w.ptr().bits(address as u32) });
282 self.inner.erase.len.write(|w| w.len()._4kb());
283 self.inner.events_ready.reset();
284 self.inner
285 .tasks_erasestart
286 .write(|w| w.tasks_erasestart().bit(true));
287
288 SIGNAL.wait().await;
289
290 bomb.defuse();
291
292 Ok(())
293 }
294 }
295
296 fn size(&self) -> usize {
297 256 * 4096 // TODO
298 }
299
300 fn read_size(&self) -> usize {
301 4 // TODO
302 }
303
304 fn write_size(&self) -> usize {
305 4 // TODO
306 }
307
308 fn erase_size(&self) -> usize {
309 4096 // TODO
310 }
311}
312
313static SIGNAL: Signal<()> = Signal::new();
314
315#[interrupt]
316unsafe fn QSPI() {
317 let p = unsafe { crate::pac::Peripherals::steal().QSPI };
318 if p.events_ready.read().events_ready().bit_is_set() {
319 p.events_ready.reset();
320 SIGNAL.signal(());
321 }
322}
diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs
new file mode 100644
index 000000000..b904f006a
--- /dev/null
+++ b/embassy-nrf/src/uarte.rs
@@ -0,0 +1,550 @@
1//! HAL interface to the UARTE peripheral
2//!
3//! See product specification:
4//!
5//! - nrf52832: Section 35
6//! - nrf52840: Section 6.34
7use core::cell::UnsafeCell;
8use core::cmp::min;
9use core::marker::PhantomPinned;
10use core::ops::Deref;
11use core::pin::Pin;
12use core::ptr;
13use core::sync::atomic::{compiler_fence, Ordering};
14use core::task::{Context, Poll};
15
16use crate::interrupt;
17use crate::interrupt::CriticalSection;
18use crate::pac::{uarte0, Interrupt, UARTE0, UARTE1};
19use embedded_hal::digital::v2::OutputPin;
20use nrf52840_hal::gpio::{Floating, Input, Output, Pin as GpioPin, Port as GpioPort, PushPull};
21
22// Re-export SVD variants to allow user to directly set values
23pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
24
25use embassy::io::{AsyncBufRead, AsyncWrite, Result};
26use embassy::util::WakerStore;
27
28use defmt::trace;
29
30//use crate::trace;
31
32const RINGBUF_SIZE: usize = 512;
33struct RingBuf {
34 buf: [u8; RINGBUF_SIZE],
35 start: usize,
36 end: usize,
37 empty: bool,
38}
39
40impl RingBuf {
41 fn new() -> Self {
42 RingBuf {
43 buf: [0; RINGBUF_SIZE],
44 start: 0,
45 end: 0,
46 empty: true,
47 }
48 }
49
50 fn push_buf(&mut self) -> &mut [u8] {
51 if self.start == self.end && !self.empty {
52 trace!(" ringbuf: push_buf empty");
53 return &mut self.buf[..0];
54 }
55
56 let n = if self.start <= self.end {
57 RINGBUF_SIZE - self.end
58 } else {
59 self.start - self.end
60 };
61
62 trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
63 &mut self.buf[self.end..self.end + n]
64 }
65
66 fn push(&mut self, n: usize) {
67 trace!(" ringbuf: push {:?}", n);
68 if n == 0 {
69 return;
70 }
71
72 self.end = Self::wrap(self.end + n);
73 self.empty = false;
74 }
75
76 fn pop_buf(&mut self) -> &mut [u8] {
77 if self.empty {
78 trace!(" ringbuf: pop_buf empty");
79 return &mut self.buf[..0];
80 }
81
82 let n = if self.end <= self.start {
83 RINGBUF_SIZE - self.start
84 } else {
85 self.end - self.start
86 };
87
88 trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
89 &mut self.buf[self.start..self.start + n]
90 }
91
92 fn pop(&mut self, n: usize) {
93 trace!(" ringbuf: pop {:?}", n);
94 if n == 0 {
95 return;
96 }
97
98 self.start = Self::wrap(self.start + n);
99 self.empty = self.start == self.end;
100 }
101
102 fn wrap(n: usize) -> usize {
103 assert!(n <= RINGBUF_SIZE);
104 if n == RINGBUF_SIZE {
105 0
106 } else {
107 n
108 }
109 }
110}
111
112#[derive(Copy, Clone, Debug, PartialEq)]
113enum RxState {
114 Idle,
115 Receiving,
116 ReceivingReady,
117 Stopping,
118}
119#[derive(Copy, Clone, Debug, PartialEq)]
120enum TxState {
121 Idle,
122 Transmitting(usize),
123}
124
125/// Interface to a UARTE instance
126///
127/// This is a very basic interface that comes with the following limitations:
128/// - The UARTE instances share the same address space with instances of UART.
129/// You need to make sure that conflicting instances
130/// are disabled before using `Uarte`. See product specification:
131/// - nrf52832: Section 15.2
132/// - nrf52840: Section 6.1.2
133pub struct Uarte<T: Instance> {
134 started: bool,
135 state: UnsafeCell<UarteState<T>>,
136}
137
138// public because it needs to be used in Instance::{get_state, set_state}, but
139// should not be used outside the module
140#[doc(hidden)]
141pub struct UarteState<T> {
142 inner: T,
143
144 rx: RingBuf,
145 rx_state: RxState,
146 rx_waker: WakerStore,
147
148 tx: RingBuf,
149 tx_state: TxState,
150 tx_waker: WakerStore,
151
152 _pin: PhantomPinned,
153}
154
155fn port_bit(port: GpioPort) -> bool {
156 match port {
157 GpioPort::Port0 => false,
158 GpioPort::Port1 => true,
159 }
160}
161
162impl<T: Instance> Uarte<T> {
163 pub fn new(uarte: T, mut pins: Pins, parity: Parity, baudrate: Baudrate) -> Self {
164 // Select pins
165 uarte.psel.rxd.write(|w| {
166 let w = unsafe { w.pin().bits(pins.rxd.pin()) };
167 let w = w.port().bit(port_bit(pins.rxd.port()));
168 w.connect().connected()
169 });
170 pins.txd.set_high().unwrap();
171 uarte.psel.txd.write(|w| {
172 let w = unsafe { w.pin().bits(pins.txd.pin()) };
173 let w = w.port().bit(port_bit(pins.txd.port()));
174 w.connect().connected()
175 });
176
177 // Optional pins
178 uarte.psel.cts.write(|w| {
179 if let Some(ref pin) = pins.cts {
180 let w = unsafe { w.pin().bits(pin.pin()) };
181 let w = w.port().bit(port_bit(pin.port()));
182 w.connect().connected()
183 } else {
184 w.connect().disconnected()
185 }
186 });
187
188 uarte.psel.rts.write(|w| {
189 if let Some(ref pin) = pins.rts {
190 let w = unsafe { w.pin().bits(pin.pin()) };
191 let w = w.port().bit(port_bit(pin.port()));
192 w.connect().connected()
193 } else {
194 w.connect().disconnected()
195 }
196 });
197
198 // Enable UARTE instance
199 uarte.enable.write(|w| w.enable().enabled());
200
201 // Enable interrupts
202 uarte.intenset.write(|w| w.endrx().set().endtx().set());
203
204 // Configure
205 let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some();
206 uarte
207 .config
208 .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity));
209
210 // Configure frequency
211 uarte.baudrate.write(|w| w.baudrate().variant(baudrate));
212
213 Uarte {
214 started: false,
215 state: UnsafeCell::new(UarteState {
216 inner: uarte,
217
218 rx: RingBuf::new(),
219 rx_state: RxState::Idle,
220 rx_waker: WakerStore::new(),
221
222 tx: RingBuf::new(),
223 tx_state: TxState::Idle,
224 tx_waker: WakerStore::new(),
225
226 _pin: PhantomPinned,
227 }),
228 }
229 }
230
231 fn with_state<'a, R>(
232 self: Pin<&'a mut Self>,
233 f: impl FnOnce(Pin<&'a mut UarteState<T>>) -> R,
234 ) -> R {
235 let Self { state, started } = unsafe { self.get_unchecked_mut() };
236
237 interrupt::free(|cs| {
238 let ptr = state.get();
239
240 if !*started {
241 T::set_state(cs, ptr);
242
243 *started = true;
244
245 // safety: safe because critical section ensures only one *mut UartState
246 // exists at the same time.
247 unsafe { Pin::new_unchecked(&mut *ptr) }.start();
248 }
249
250 // safety: safe because critical section ensures only one *mut UartState
251 // exists at the same time.
252 f(unsafe { Pin::new_unchecked(&mut *ptr) })
253 })
254 }
255}
256
257impl<T: Instance> Drop for Uarte<T> {
258 fn drop(&mut self) {
259 // stop DMA before dropping, because DMA is using the buffer in `self`.
260 todo!()
261 }
262}
263
264impl<T: Instance> AsyncBufRead for Uarte<T> {
265 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
266 self.with_state(|s| s.poll_fill_buf(cx))
267 }
268
269 fn consume(self: Pin<&mut Self>, amt: usize) {
270 self.with_state(|s| s.consume(amt))
271 }
272}
273
274impl<T: Instance> AsyncWrite for Uarte<T> {
275 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
276 self.with_state(|s| s.poll_write(cx, buf))
277 }
278}
279
280impl<T: Instance> UarteState<T> {
281 pub fn start(self: Pin<&mut Self>) {
282 interrupt::set_priority(T::interrupt(), interrupt::Priority::Level7);
283 interrupt::enable(T::interrupt());
284 interrupt::pend(T::interrupt());
285 }
286
287 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
288 let this = unsafe { self.get_unchecked_mut() };
289
290 // Conservative compiler fence to prevent optimizations that do not
291 // take in to account actions by DMA. The fence has been placed here,
292 // before any DMA action has started
293 compiler_fence(Ordering::SeqCst);
294 trace!("poll_read");
295
296 // We have data ready in buffer? Return it.
297 let buf = this.rx.pop_buf();
298 if buf.len() != 0 {
299 trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
300 return Poll::Ready(Ok(buf));
301 }
302
303 trace!(" empty");
304
305 if this.rx_state == RxState::ReceivingReady {
306 trace!(" stopping");
307 this.rx_state = RxState::Stopping;
308 this.inner.tasks_stoprx.write(|w| unsafe { w.bits(1) });
309 }
310
311 this.rx_waker.store(cx.waker());
312 Poll::Pending
313 }
314
315 fn consume(self: Pin<&mut Self>, amt: usize) {
316 let this = unsafe { self.get_unchecked_mut() };
317 trace!("consume {:?}", amt);
318 this.rx.pop(amt);
319 interrupt::pend(T::interrupt());
320 }
321
322 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
323 let this = unsafe { self.get_unchecked_mut() };
324
325 trace!("poll_write: {:?}", buf.len());
326
327 let tx_buf = this.tx.push_buf();
328 if tx_buf.len() == 0 {
329 trace!("poll_write: pending");
330 this.tx_waker.store(cx.waker());
331 return Poll::Pending;
332 }
333
334 let n = min(tx_buf.len(), buf.len());
335 tx_buf[..n].copy_from_slice(&buf[..n]);
336 this.tx.push(n);
337
338 trace!("poll_write: queued {:?}", n);
339
340 // Conservative compiler fence to prevent optimizations that do not
341 // take in to account actions by DMA. The fence has been placed here,
342 // before any DMA action has started
343 compiler_fence(Ordering::SeqCst);
344
345 interrupt::pend(T::interrupt());
346
347 Poll::Ready(Ok(n))
348 }
349
350 fn on_interrupt(&mut self) {
351 trace!("irq: start");
352 let mut more_work = true;
353 while more_work {
354 more_work = false;
355 match self.rx_state {
356 RxState::Idle => {
357 trace!(" irq_rx: in state idle");
358
359 if self.inner.events_rxdrdy.read().bits() != 0 {
360 trace!(" irq_rx: rxdrdy?????");
361 self.inner.events_rxdrdy.reset();
362 }
363
364 if self.inner.events_endrx.read().bits() != 0 {
365 panic!("unexpected endrx");
366 }
367
368 let buf = self.rx.push_buf();
369 if buf.len() != 0 {
370 trace!(" irq_rx: starting {:?}", buf.len());
371 self.rx_state = RxState::Receiving;
372
373 // Set up the DMA read
374 self.inner.rxd.ptr.write(|w|
375 // The PTR field is a full 32 bits wide and accepts the full range
376 // of values.
377 unsafe { w.ptr().bits(buf.as_ptr() as u32) });
378 self.inner.rxd.maxcnt.write(|w|
379 // We're giving it the length of the buffer, so no danger of
380 // accessing invalid memory. We have verified that the length of the
381 // buffer fits in an `u8`, so the cast to `u8` is also fine.
382 //
383 // The MAXCNT field is at least 8 bits wide and accepts the full
384 // range of values.
385 unsafe { w.maxcnt().bits(buf.len() as _) });
386 trace!(" irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len());
387
388 // Enable RXRDY interrupt.
389 self.inner.events_rxdrdy.reset();
390 self.inner.intenset.write(|w| w.rxdrdy().set());
391
392 // Start UARTE Receive transaction
393 self.inner.tasks_startrx.write(|w|
394 // `1` is a valid value to write to task registers.
395 unsafe { w.bits(1) });
396 }
397 }
398 RxState::Receiving => {
399 trace!(" irq_rx: in state receiving");
400 if self.inner.events_rxdrdy.read().bits() != 0 {
401 trace!(" irq_rx: rxdrdy");
402
403 // Disable the RXRDY event interrupt
404 // RXRDY is triggered for every byte, but we only care about whether we have
405 // some bytes or not. So as soon as we have at least one, disable it, to avoid
406 // wasting CPU cycles in interrupts.
407 self.inner.intenclr.write(|w| w.rxdrdy().clear());
408
409 self.inner.events_rxdrdy.reset();
410
411 self.rx_waker.wake();
412 self.rx_state = RxState::ReceivingReady;
413 more_work = true; // in case we also have endrx pending
414 }
415 }
416 RxState::ReceivingReady | RxState::Stopping => {
417 trace!(" irq_rx: in state ReceivingReady");
418
419 if self.inner.events_rxdrdy.read().bits() != 0 {
420 trace!(" irq_rx: rxdrdy");
421 self.inner.events_rxdrdy.reset();
422 }
423
424 if self.inner.events_endrx.read().bits() != 0 {
425 let n: usize = self.inner.rxd.amount.read().amount().bits() as usize;
426 trace!(" irq_rx: endrx {:?}", n);
427 self.rx.push(n);
428
429 self.inner.events_endrx.reset();
430
431 self.rx_waker.wake();
432 self.rx_state = RxState::Idle;
433 more_work = true; // start another rx if possible
434 }
435 }
436 }
437 }
438
439 more_work = true;
440 while more_work {
441 more_work = false;
442 match self.tx_state {
443 TxState::Idle => {
444 trace!(" irq_tx: in state Idle");
445 let buf = self.tx.pop_buf();
446 if buf.len() != 0 {
447 trace!(" irq_tx: starting {:?}", buf.len());
448 self.tx_state = TxState::Transmitting(buf.len());
449
450 // Set up the DMA write
451 self.inner.txd.ptr.write(|w|
452 // The PTR field is a full 32 bits wide and accepts the full range
453 // of values.
454 unsafe { w.ptr().bits(buf.as_ptr() as u32) });
455 self.inner.txd.maxcnt.write(|w|
456 // We're giving it the length of the buffer, so no danger of
457 // accessing invalid memory. We have verified that the length of the
458 // buffer fits in an `u8`, so the cast to `u8` is also fine.
459 //
460 // The MAXCNT field is 8 bits wide and accepts the full range of
461 // values.
462 unsafe { w.maxcnt().bits(buf.len() as _) });
463
464 // Start UARTE Transmit transaction
465 self.inner.tasks_starttx.write(|w|
466 // `1` is a valid value to write to task registers.
467 unsafe { w.bits(1) });
468 }
469 }
470 TxState::Transmitting(n) => {
471 trace!(" irq_tx: in state Transmitting");
472 if self.inner.events_endtx.read().bits() != 0 {
473 self.inner.events_endtx.reset();
474
475 trace!(" irq_tx: endtx {:?}", n);
476 self.tx.pop(n);
477 self.tx_waker.wake();
478 self.tx_state = TxState::Idle;
479 more_work = true; // start another tx if possible
480 }
481 }
482 }
483 }
484 trace!("irq: end");
485 }
486}
487
488pub struct Pins {
489 pub rxd: GpioPin<Input<Floating>>,
490 pub txd: GpioPin<Output<PushPull>>,
491 pub cts: Option<GpioPin<Input<Floating>>>,
492 pub rts: Option<GpioPin<Output<PushPull>>>,
493}
494
495mod private {
496 use nrf52840_pac::{UARTE0, UARTE1};
497 pub trait Sealed {}
498
499 impl Sealed for UARTE0 {}
500 impl Sealed for UARTE1 {}
501}
502
503pub trait Instance: Deref<Target = uarte0::RegisterBlock> + Sized + private::Sealed {
504 fn interrupt() -> Interrupt;
505
506 #[doc(hidden)]
507 fn get_state(_cs: &CriticalSection) -> *mut UarteState<Self>;
508
509 #[doc(hidden)]
510 fn set_state(_cs: &CriticalSection, state: *mut UarteState<Self>);
511}
512
513#[interrupt]
514unsafe fn UARTE0_UART0() {
515 interrupt::free(|cs| UARTE0::get_state(cs).as_mut().unwrap().on_interrupt());
516}
517
518#[interrupt]
519unsafe fn UARTE1() {
520 interrupt::free(|cs| UARTE1::get_state(cs).as_mut().unwrap().on_interrupt());
521}
522
523static mut UARTE0_STATE: *mut UarteState<UARTE0> = ptr::null_mut();
524static mut UARTE1_STATE: *mut UarteState<UARTE1> = ptr::null_mut();
525
526impl Instance for UARTE0 {
527 fn interrupt() -> Interrupt {
528 Interrupt::UARTE0_UART0
529 }
530
531 fn get_state(_cs: &CriticalSection) -> *mut UarteState<Self> {
532 unsafe { UARTE0_STATE } // Safe because of CriticalSection
533 }
534 fn set_state(_cs: &CriticalSection, state: *mut UarteState<Self>) {
535 unsafe { UARTE0_STATE = state } // Safe because of CriticalSection
536 }
537}
538
539impl Instance for UARTE1 {
540 fn interrupt() -> Interrupt {
541 Interrupt::UARTE1
542 }
543
544 fn get_state(_cs: &CriticalSection) -> *mut UarteState<Self> {
545 unsafe { UARTE1_STATE } // Safe because of CriticalSection
546 }
547 fn set_state(_cs: &CriticalSection, state: *mut UarteState<Self>) {
548 unsafe { UARTE1_STATE = state } // Safe because of CriticalSection
549 }
550}
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml
new file mode 100644
index 000000000..f621015ab
--- /dev/null
+++ b/embassy/Cargo.toml
@@ -0,0 +1,14 @@
1[package]
2name = "embassy"
3version = "0.1.0"
4authors = ["Dario Nieuwenhuis <[email protected]>"]
5edition = "2018"
6
7[features]
8std = []
9
10[dependencies]
11defmt = "0.1.0"
12cortex-m = "0.6.3"
13futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] }
14pin-project = { version = "0.4.23", default-features = false }
diff --git a/embassy/src/flash.rs b/embassy/src/flash.rs
new file mode 100644
index 000000000..bf6d59804
--- /dev/null
+++ b/embassy/src/flash.rs
@@ -0,0 +1,51 @@
1
2use core::future::Future;
3
4#[derive(defmt::Format, Copy, Clone, Debug, Eq, PartialEq)]
5pub enum Error {
6 Failed,
7 AddressMisaligned,
8 BufferMisaligned,
9
10 _NonExhaustive,
11}
12
13pub trait Flash {
14 type ReadFuture<'a>: Future<Output = Result<(), Error>>;
15 type WriteFuture<'a>: Future<Output = Result<(), Error>>;
16 type ErasePageFuture<'a>: Future<Output = Result<(), Error>>;
17
18 /// Reads data from the flash device.
19 ///
20 /// address must be a multiple of self.read_size().
21 /// buf.len() must be a multiple of self.read_size().
22 fn read<'a>(&'a mut self, address: usize, buf: &'a mut [u8]) -> Self::ReadFuture<'a>;
23
24 /// Writes data to the flash device.
25 ///
26 /// address must be a multiple of self.write_size().
27 /// buf.len() must be a multiple of self.write_size().
28 fn write<'a>(&'a mut self, address: usize, buf: &'a [u8]) -> Self::WriteFuture<'a>;
29
30 /// Erases a single page from the flash device.
31 ///
32 /// address must be a multiple of self.erase_size().
33 fn erase<'a>(&'a mut self, address: usize) -> Self::ErasePageFuture<'a>;
34
35 /// Returns the total size, in bytes.
36 /// This is not guaranteed to be a power of 2.
37 fn size(&self) -> usize;
38
39 /// Returns the read size in bytes.
40 /// This is guaranteed to be a power of 2.
41 fn read_size(&self) -> usize;
42
43 /// Returns the write size in bytes.
44 /// This is guaranteed to be a power of 2.
45 fn write_size(&self) -> usize;
46
47 /// Returns the erase size in bytes.
48 /// This is guaranteed to be a power of 2.
49 fn erase_size(&self) -> usize;
50}
51
diff --git a/embassy/src/io/error.rs b/embassy/src/io/error.rs
new file mode 100644
index 000000000..2f1d4810e
--- /dev/null
+++ b/embassy/src/io/error.rs
@@ -0,0 +1,133 @@
1#[cfg(feature = "std")]
2use core::convert::From;
3#[cfg(feature = "std")]
4use futures::io;
5
6/// Categories of errors that can occur.
7///
8/// This list is intended to grow over time and it is not recommended to
9/// exhaustively match against it.
10#[derive(defmt::Format, Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Error {
12 /// An entity was not found, often a file.
13 NotFound,
14 /// The operation lacked the necessary privileges to complete.
15 PermissionDenied,
16 /// The connection was refused by the remote server.
17 ConnectionRefused,
18 /// The connection was reset by the remote server.
19 ConnectionReset,
20 /// The connection was aborted (terminated) by the remote server.
21 ConnectionAborted,
22 /// The network operation failed because it was not connected yet.
23 NotConnected,
24 /// A socket address could not be bound because the address is already in
25 /// use elsewhere.
26 AddrInUse,
27 /// A nonexistent interface was requested or the requested address was not
28 /// local.
29 AddrNotAvailable,
30 /// The operation failed because a pipe was closed.
31 BrokenPipe,
32 /// An entity already exists, often a file.
33 AlreadyExists,
34 /// The operation needs to block to complete, but the blocking operation was
35 /// requested to not occur.
36 WouldBlock,
37 /// A parameter was incorrect.
38 InvalidInput,
39 /// Data not valid for the operation were encountered.
40 ///
41 /// Unlike [`InvalidInput`], this typically means that the operation
42 /// parameters were valid, however the error was caused by malformed
43 /// input data.
44 ///
45 /// For example, a function that reads a file into a string will error with
46 /// `InvalidData` if the file's contents are not valid UTF-8.
47 ///
48 /// [`InvalidInput`]: #variant.InvalidInput
49 InvalidData,
50 /// The I/O operation's timeout expired, causing it to be canceled.
51 TimedOut,
52 /// An error returned when an operation could not be completed because a
53 /// call to [`write`] returned [`Ok(0)`].
54 ///
55 /// This typically means that an operation could only succeed if it wrote a
56 /// particular number of bytes but only a smaller number of bytes could be
57 /// written.
58 ///
59 /// [`write`]: ../../std/io/trait.Write.html#tymethod.write
60 /// [`Ok(0)`]: ../../std/io/type.Result.html
61 WriteZero,
62 /// This operation was interrupted.
63 ///
64 /// Interrupted operations can typically be retried.
65 Interrupted,
66
67 /// An error returned when an operation could not be completed because an
68 /// "end of file" was reached prematurely.
69 ///
70 /// This typically means that an operation could only succeed if it read a
71 /// particular number of bytes but only a smaller number of bytes could be
72 /// read.
73 UnexpectedEof,
74
75 /// An operation would have read more data if the given buffer was large.
76 ///
77 /// This typically means that the buffer has been filled with the first N bytes
78 /// of the read data.
79 Truncated,
80
81 /// Any I/O error not part of this list.
82 Other,
83}
84
85pub type Result<T> = core::result::Result<T, Error>;
86
87#[cfg(feature = "std")]
88impl From<io::Error> for Error {
89 fn from(err: io::Error) -> Error {
90 match err.kind() {
91 io::ErrorKind::NotFound => Error::NotFound,
92 io::ErrorKind::PermissionDenied => Error::PermissionDenied,
93 io::ErrorKind::ConnectionRefused => Error::ConnectionRefused,
94 io::ErrorKind::ConnectionReset => Error::ConnectionReset,
95 io::ErrorKind::ConnectionAborted => Error::ConnectionAborted,
96 io::ErrorKind::NotConnected => Error::NotConnected,
97 io::ErrorKind::AddrInUse => Error::AddrInUse,
98 io::ErrorKind::AddrNotAvailable => Error::AddrNotAvailable,
99 io::ErrorKind::BrokenPipe => Error::BrokenPipe,
100 io::ErrorKind::AlreadyExists => Error::AlreadyExists,
101 io::ErrorKind::WouldBlock => Error::WouldBlock,
102 io::ErrorKind::InvalidInput => Error::InvalidInput,
103 io::ErrorKind::InvalidData => Error::InvalidData,
104 io::ErrorKind::TimedOut => Error::TimedOut,
105 io::ErrorKind::WriteZero => Error::WriteZero,
106 io::ErrorKind::Interrupted => Error::Interrupted,
107 io::ErrorKind::UnexpectedEof => Error::UnexpectedEof,
108 _ => Error::Other,
109 }
110 }
111}
112
113#[cfg(feature = "std")]
114impl std::error::Error for Error {}
115
116/*
117impl From<smoltcp::Error> for Error {
118 fn from(err: smoltcp::Error) -> Error {
119 match err {
120 smoltcp::Error::Exhausted => Error::Exhausted,
121 smoltcp::Error::Illegal => Error::Illegal,
122 smoltcp::Error::Unaddressable => Error::Unaddressable,
123 smoltcp::Error::Truncated => Error::Truncated,
124 smoltcp::Error::Checksum => Error::Checksum,
125 smoltcp::Error::Unrecognized => Error::Unrecognized,
126 smoltcp::Error::Fragmented => Error::Fragmented,
127 smoltcp::Error::Malformed => Error::Malformed,
128 smoltcp::Error::Dropped => Error::Dropped,
129 _ => Error::Other,
130 }
131 }
132}
133*/
diff --git a/embassy/src/io/mod.rs b/embassy/src/io/mod.rs
new file mode 100644
index 000000000..8445f6e80
--- /dev/null
+++ b/embassy/src/io/mod.rs
@@ -0,0 +1,7 @@
1mod error;
2mod traits;
3mod util;
4
5pub use self::error::*;
6pub use self::traits::*;
7pub use self::util::*;
diff --git a/embassy/src/io/traits.rs b/embassy/src/io/traits.rs
new file mode 100644
index 000000000..f1f91a46c
--- /dev/null
+++ b/embassy/src/io/traits.rs
@@ -0,0 +1,197 @@
1
2use core::ops::DerefMut;
3use core::pin::Pin;
4use core::task::{Context, Poll};
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8
9#[cfg(feature = "std")]
10use futures::io as std_io;
11
12use super::error::Result;
13
14/// Read bytes asynchronously.
15///
16/// This trait is analogous to the `std::io::BufRead` trait, but integrates
17/// with the asynchronous task system. In particular, the `poll_fill_buf`
18/// method, unlike `BufRead::fill_buf`, will automatically queue the current task
19/// for wakeup and return if data is not yet available, rather than blocking
20/// the calling thread.
21pub trait AsyncBufRead {
22 /// Attempt to return the contents of the internal buffer, filling it with more data
23 /// from the inner reader if it is empty.
24 ///
25 /// On success, returns `Poll::Ready(Ok(buf))`.
26 ///
27 /// If no data is available for reading, the method returns
28 /// `Poll::Pending` and arranges for the current task (via
29 /// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
30 /// readable or is closed.
31 ///
32 /// This function is a lower-level call. It needs to be paired with the
33 /// [`consume`] method to function properly. When calling this
34 /// method, none of the contents will be "read" in the sense that later
35 /// calling [`poll_read`] may return the same contents. As such, [`consume`] must
36 /// be called with the number of bytes that are consumed from this buffer to
37 /// ensure that the bytes are never returned twice.
38 ///
39 /// [`poll_read`]: AsyncBufRead::poll_read
40 /// [`consume`]: AsyncBufRead::consume
41 ///
42 /// An empty buffer returned indicates that the stream has reached EOF.
43 ///
44 /// # Implementation
45 ///
46 /// This function may not return errors of kind `WouldBlock` or
47 /// `Interrupted`. Implementations must convert `WouldBlock` into
48 /// `Poll::Pending` and either internally retry or convert
49 /// `Interrupted` into another error kind.
50 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>>;
51
52 /// Tells this buffer that `amt` bytes have been consumed from the buffer,
53 /// so they should no longer be returned in calls to [`poll_read`].
54 ///
55 /// This function is a lower-level call. It needs to be paired with the
56 /// [`poll_fill_buf`] method to function properly. This function does
57 /// not perform any I/O, it simply informs this object that some amount of
58 /// its buffer, returned from [`poll_fill_buf`], has been consumed and should
59 /// no longer be returned. As such, this function may do odd things if
60 /// [`poll_fill_buf`] isn't called before calling it.
61 ///
62 /// The `amt` must be `<=` the number of bytes in the buffer returned by
63 /// [`poll_fill_buf`].
64 ///
65 /// [`poll_read`]: AsyncBufRead::poll_read
66 /// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
67 fn consume(self: Pin<&mut Self>, amt: usize);
68}
69
70/// Write bytes asynchronously.
71///
72/// This trait is analogous to the `core::io::Write` trait, but integrates
73/// with the asynchronous task system. In particular, the `poll_write`
74/// method, unlike `Write::write`, will automatically queue the current task
75/// for wakeup and return if the writer cannot take more data, rather than blocking
76/// the calling thread.
77pub trait AsyncWrite {
78 /// Attempt to write bytes from `buf` into the object.
79 ///
80 /// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
81 ///
82 /// If the object is not ready for writing, the method returns
83 /// `Poll::Pending` and arranges for the current task (via
84 /// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
85 /// writable or is closed.
86 ///
87 /// # Implementation
88 ///
89 /// This function may not return errors of kind `WouldBlock` or
90 /// `Interrupted`. Implementations must convert `WouldBlock` into
91 /// `Poll::Pending` and either internally retry or convert
92 /// `Interrupted` into another error kind.
93 ///
94 /// `poll_write` must try to make progress by flushing the underlying object if
95 /// that is the only way the underlying object can become writable again.
96 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
97}
98
99macro_rules! defer_async_read {
100 () => {
101 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
102 Pin::new(&mut **self.get_mut()).poll_fill_buf(cx)
103 }
104
105 fn consume(mut self: Pin<&mut Self>, amt: usize) {
106 Pin::new(&mut **self).consume(amt)
107 }
108 };
109}
110
111#[cfg(feature = "alloc")]
112impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> {
113 defer_async_read!();
114}
115
116impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T {
117 defer_async_read!();
118}
119
120impl<P> AsyncBufRead for Pin<P>
121where
122 P: DerefMut + Unpin,
123 P::Target: AsyncBufRead,
124{
125 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
126 self.get_mut().as_mut().poll_fill_buf(cx)
127 }
128
129 fn consume(self: Pin<&mut Self>, amt: usize) {
130 self.get_mut().as_mut().consume(amt)
131 }
132}
133
134macro_rules! deref_async_write {
135 () => {
136 fn poll_write(
137 mut self: Pin<&mut Self>,
138 cx: &mut Context<'_>,
139 buf: &[u8],
140 ) -> Poll<Result<usize>> {
141 Pin::new(&mut **self).poll_write(cx, buf)
142 }
143 };
144}
145
146#[cfg(feature = "alloc")]
147impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
148 deref_async_write!();
149}
150
151impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T {
152 deref_async_write!();
153}
154
155impl<P> AsyncWrite for Pin<P>
156where
157 P: DerefMut + Unpin,
158 P::Target: AsyncWrite,
159{
160 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
161 self.get_mut().as_mut().poll_write(cx, buf)
162 }
163}
164
165#[cfg(feature = "std")]
166pub struct FromStdIo<T>(T);
167
168#[cfg(feature = "std")]
169impl<T> FromStdIo<T> {
170 pub fn new(inner: T) -> Self {
171 Self(inner)
172 }
173}
174
175#[cfg(feature = "std")]
176impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
177 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
178 let Self(inner) = unsafe { self.get_unchecked_mut() };
179 unsafe { Pin::new_unchecked(inner) }
180 .poll_fill_buf(cx)
181 .map_err(|e| e.into())
182 }
183 fn consume(self: Pin<&mut Self>, amt: usize) {
184 let Self(inner) = unsafe { self.get_unchecked_mut() };
185 unsafe { Pin::new_unchecked(inner) }.consume(amt)
186 }
187}
188
189#[cfg(feature = "std")]
190impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
191 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
192 let Self(inner) = unsafe { self.get_unchecked_mut() };
193 unsafe { Pin::new_unchecked(inner) }
194 .poll_write(cx, buf)
195 .map_err(|e| e.into())
196 }
197}
diff --git a/embassy/src/io/util/copy_buf.rs b/embassy/src/io/util/copy_buf.rs
new file mode 100644
index 000000000..c037f3b02
--- /dev/null
+++ b/embassy/src/io/util/copy_buf.rs
@@ -0,0 +1,80 @@
1use core::future::Future;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use futures::ready;
5use pin_project::pin_project;
6
7use crate::io::{AsyncBufRead, AsyncWrite, Error, Result};
8
9/// Creates a future which copies all the bytes from one object to another.
10///
11/// The returned future will copy all the bytes read from this `AsyncBufRead` into the
12/// `writer` specified. This future will only complete once the `reader` has hit
13/// EOF and all bytes have been written to and flushed from the `writer`
14/// provided.
15///
16/// On success the number of bytes is returned.
17///
18/// # Examples
19///
20/// ```
21/// # futures::executor::block_on(async {
22/// use futures::io::{self, AsyncWriteExt, Cursor};
23///
24/// let reader = Cursor::new([1, 2, 3, 4]);
25/// let mut writer = Cursor::new(vec![0u8; 5]);
26///
27/// let bytes = io::copy_buf(reader, &mut writer).await?;
28/// writer.close().await?;
29///
30/// assert_eq!(bytes, 4);
31/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
32/// # Ok::<(), Box<dyn std::error::Error>>(()) }).unwrap();
33/// ```
34pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W>
35where
36 R: AsyncBufRead,
37 W: AsyncWrite + Unpin + ?Sized,
38{
39 CopyBuf {
40 reader,
41 writer,
42 amt: 0,
43 }
44}
45
46/// Future for the [`copy_buf()`] function.
47#[pin_project]
48#[derive(Debug)]
49#[must_use = "futures do nothing unless you `.await` or poll them"]
50pub struct CopyBuf<'a, R, W: ?Sized> {
51 #[pin]
52 reader: R,
53 writer: &'a mut W,
54 amt: usize,
55}
56
57impl<R, W> Future for CopyBuf<'_, R, W>
58where
59 R: AsyncBufRead,
60 W: AsyncWrite + Unpin + ?Sized,
61{
62 type Output = Result<usize>;
63
64 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
65 let mut this = self.project();
66 loop {
67 let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?;
68 if buffer.is_empty() {
69 return Poll::Ready(Ok(*this.amt));
70 }
71
72 let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?;
73 if i == 0 {
74 return Poll::Ready(Err(Error::WriteZero.into()));
75 }
76 *this.amt += i;
77 this.reader.as_mut().consume(i);
78 }
79 }
80}
diff --git a/embassy/src/io/util/mod.rs b/embassy/src/io/util/mod.rs
new file mode 100644
index 000000000..c95a23f0a
--- /dev/null
+++ b/embassy/src/io/util/mod.rs
@@ -0,0 +1,145 @@
1use core::cmp::min;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4use futures::ready;
5
6mod read;
7pub use self::read::Read;
8
9mod read_buf;
10pub use self::read_buf::ReadBuf;
11
12mod read_byte;
13pub use self::read_byte::ReadByte;
14
15mod read_exact;
16pub use self::read_exact::ReadExact;
17
18mod read_while;
19pub use self::read_while::ReadWhile;
20
21mod read_to_end;
22pub use self::read_to_end::ReadToEnd;
23
24mod skip_while;
25pub use self::skip_while::SkipWhile;
26
27mod write;
28pub use self::write::Write;
29
30mod write_all;
31pub use self::write_all::WriteAll;
32
33mod write_byte;
34pub use self::write_byte::WriteByte;
35
36#[cfg(feature = "alloc")]
37mod split;
38#[cfg(feature = "alloc")]
39pub use self::split::{split, ReadHalf, WriteHalf};
40
41mod copy_buf;
42pub use self::copy_buf::{copy_buf, CopyBuf};
43
44use super::error::Result;
45use super::traits::{AsyncBufRead, AsyncWrite};
46
47pub trait AsyncBufReadExt: AsyncBufRead {
48 fn poll_read(
49 mut self: Pin<&mut Self>,
50 cx: &mut Context<'_>,
51 buf: &mut [u8],
52 ) -> Poll<Result<usize>>
53 where
54 Self: Unpin,
55 {
56 let mut this = &mut *self;
57 let rbuf = ready!(Pin::new(&mut this).poll_fill_buf(cx))?;
58 let n = min(buf.len(), rbuf.len());
59 buf[..n].copy_from_slice(&rbuf[..n]);
60 Pin::new(&mut this).consume(n);
61 Poll::Ready(Ok(n))
62 }
63
64 fn read_while<'a, F: Fn(u8) -> bool>(
65 &'a mut self,
66 buf: &'a mut [u8],
67 f: F,
68 ) -> ReadWhile<'a, Self, F>
69 where
70 Self: Unpin,
71 {
72 ReadWhile::new(self, f, buf)
73 }
74
75 fn skip_while<'a, F: Fn(u8) -> bool>(&'a mut self, f: F) -> SkipWhile<'a, Self, F>
76 where
77 Self: Unpin,
78 {
79 SkipWhile::new(self, f)
80 }
81
82 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
83 where
84 Self: Unpin,
85 {
86 Read::new(self, buf)
87 }
88
89 fn read_buf<'a>(&'a mut self) -> ReadBuf<'a, Self>
90 where
91 Self: Unpin,
92 {
93 ReadBuf::new(self)
94 }
95
96 fn read_byte<'a>(&'a mut self) -> ReadByte<'a, Self>
97 where
98 Self: Unpin,
99 {
100 ReadByte::new(self)
101 }
102
103 fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
104 where
105 Self: Unpin,
106 {
107 ReadExact::new(self, buf)
108 }
109
110 fn read_to_end<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadToEnd<'a, Self>
111 where
112 Self: Unpin,
113 {
114 ReadToEnd::new(self, buf)
115 }
116}
117
118impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {}
119
120pub async fn read_line<R: AsyncBufRead + Unpin>(r: &mut R, buf: &mut [u8]) -> Result<usize> {
121 r.skip_while(|b| b == b'\r' || b == b'\n').await?;
122 let n = r.read_while(buf, |b| b != b'\r' && b != b'\n').await?;
123 r.skip_while(|b| b == b'\r').await?;
124 //assert_eq!(b'\n', r.read_byte().await?);
125 r.read_byte().await?;
126 Ok(n)
127}
128
129pub trait AsyncWriteExt: AsyncWrite {
130 fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self>
131 where
132 Self: Unpin,
133 {
134 WriteAll::new(self, buf)
135 }
136
137 fn write_byte<'a>(&'a mut self, byte: u8) -> WriteByte<'a, Self>
138 where
139 Self: Unpin,
140 {
141 WriteByte::new(self, byte)
142 }
143}
144
145impl<R: AsyncWrite + ?Sized> AsyncWriteExt for R {}
diff --git a/embassy/src/io/util/read.rs b/embassy/src/io/util/read.rs
new file mode 100644
index 000000000..31aaa0da5
--- /dev/null
+++ b/embassy/src/io/util/read.rs
@@ -0,0 +1,39 @@
1use super::super::error::{Result};
2use super::super::traits::AsyncBufRead;
3
4use core::cmp::min;
5
6use core::pin::Pin;
7use futures::future::Future;
8use futures::ready;
9use futures::task::{Context, Poll};
10
11/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
12#[derive(Debug)]
13#[must_use = "futures do nothing unless you `.await` or poll them"]
14pub struct Read<'a, R: ?Sized> {
15 reader: &'a mut R,
16 buf: &'a mut [u8],
17}
18
19impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {}
20
21impl<'a, R: AsyncBufRead + ?Sized + Unpin> Read<'a, R> {
22 pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
23 Read { reader, buf }
24 }
25}
26
27impl<R: AsyncBufRead + ?Sized + Unpin> Future for Read<'_, R> {
28 type Output = Result<usize>;
29
30 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
31 let this = &mut *self;
32 let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
33
34 let n = min(this.buf.len(), buf.len());
35 this.buf[..n].copy_from_slice(&buf[..n]);
36 Pin::new(&mut this.reader).consume(n);
37 Poll::Ready(Ok(n))
38 }
39}
diff --git a/embassy/src/io/util/read_buf.rs b/embassy/src/io/util/read_buf.rs
new file mode 100644
index 000000000..7489eac26
--- /dev/null
+++ b/embassy/src/io/util/read_buf.rs
@@ -0,0 +1,34 @@
1use super::super::error::{Result};
2use super::super::traits::AsyncBufRead;
3
4use core::pin::Pin;
5use futures::future::Future;
6use futures::ready;
7use futures::task::{Context, Poll};
8
9pub struct ReadBuf<'a, R: ?Sized> {
10 reader: Option<&'a mut R>,
11}
12
13impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {}
14
15impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadBuf<'a, R> {
16 pub(super) fn new(reader: &'a mut R) -> Self {
17 ReadBuf {
18 reader: Some(reader),
19 }
20 }
21}
22
23impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadBuf<'a, R> {
24 type Output = Result<&'a [u8]>;
25
26 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
27 let this = &mut *self;
28
29 let buf = ready!(Pin::new(this.reader.as_mut().unwrap()).poll_fill_buf(cx))?;
30 let buf: &'a [u8] = unsafe { core::mem::transmute(buf) };
31 this.reader = None;
32 Poll::Ready(Ok(buf))
33 }
34}
diff --git a/embassy/src/io/util/read_byte.rs b/embassy/src/io/util/read_byte.rs
new file mode 100644
index 000000000..7b7865ba9
--- /dev/null
+++ b/embassy/src/io/util/read_byte.rs
@@ -0,0 +1,36 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::{Error, Result};
7use super::super::traits::AsyncBufRead;
8
9pub struct ReadByte<'a, R: ?Sized> {
10 reader: &'a mut R,
11}
12
13impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {}
14
15impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> {
16 pub(super) fn new(reader: &'a mut R) -> Self {
17 Self { reader }
18 }
19}
20
21impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadByte<'a, R> {
22 type Output = Result<u8>;
23
24 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
25 let Self { reader } = &mut *self;
26 let mut reader = Pin::new(reader);
27 let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
28 if rbuf.len() == 0 {
29 return Poll::Ready(Err(Error::UnexpectedEof));
30 }
31
32 let r = rbuf[0];
33 reader.as_mut().consume(1);
34 Poll::Ready(Ok(r))
35 }
36}
diff --git a/embassy/src/io/util/read_exact.rs b/embassy/src/io/util/read_exact.rs
new file mode 100644
index 000000000..b7f7355ef
--- /dev/null
+++ b/embassy/src/io/util/read_exact.rs
@@ -0,0 +1,48 @@
1use super::super::error::{Error, Result};
2use super::super::traits::AsyncBufRead;
3
4use core::cmp::min;
5use core::mem;
6use core::pin::Pin;
7use futures::future::Future;
8use futures::ready;
9use futures::task::{Context, Poll};
10
11/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
12#[derive(Debug)]
13#[must_use = "futures do nothing unless you `.await` or poll them"]
14pub struct ReadExact<'a, R: ?Sized> {
15 reader: &'a mut R,
16 buf: &'a mut [u8],
17}
18
19impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {}
20
21impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadExact<'a, R> {
22 pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
23 ReadExact { reader, buf }
24 }
25}
26
27impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadExact<'_, R> {
28 type Output = Result<()>;
29
30 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
31 let this = &mut *self;
32 while !this.buf.is_empty() {
33 let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
34 if buf.len() == 0 {
35 return Poll::Ready(Err(Error::UnexpectedEof));
36 }
37
38 let n = min(this.buf.len(), buf.len());
39 this.buf[..n].copy_from_slice(&buf[..n]);
40 Pin::new(&mut this.reader).consume(n);
41 {
42 let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n);
43 this.buf = rest;
44 }
45 }
46 Poll::Ready(Ok(()))
47 }
48}
diff --git a/embassy/src/io/util/read_to_end.rs b/embassy/src/io/util/read_to_end.rs
new file mode 100644
index 000000000..2da6c74d8
--- /dev/null
+++ b/embassy/src/io/util/read_to_end.rs
@@ -0,0 +1,48 @@
1use core::cmp::min;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::{Error, Result};
8use super::super::traits::AsyncBufRead;
9
10pub struct ReadToEnd<'a, R: ?Sized> {
11 reader: &'a mut R,
12 buf: &'a mut [u8],
13 n: usize,
14}
15
16impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {}
17
18impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadToEnd<'a, R> {
19 pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
20 Self { reader, buf, n: 0 }
21 }
22}
23
24impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadToEnd<'a, R> {
25 type Output = Result<usize>;
26
27 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
28 let Self { reader, buf, n } = &mut *self;
29 let mut reader = Pin::new(reader);
30 loop {
31 let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
32 if rbuf.len() == 0 {
33 return Poll::Ready(Ok(*n));
34 }
35
36 if *n == buf.len() {
37 return Poll::Ready(Err(Error::Truncated));
38 }
39
40 // truncate data if it doesn't fit in buf
41 let p = min(rbuf.len(), buf.len() - *n);
42 buf[*n..*n + p].copy_from_slice(&rbuf[..p]);
43 *n += p;
44
45 reader.as_mut().consume(p);
46 }
47 }
48}
diff --git a/embassy/src/io/util/read_while.rs b/embassy/src/io/util/read_while.rs
new file mode 100644
index 000000000..ab46cee38
--- /dev/null
+++ b/embassy/src/io/util/read_while.rs
@@ -0,0 +1,61 @@
1use core::cmp::min;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::{Error, Result};
8use super::super::traits::AsyncBufRead;
9
10pub struct ReadWhile<'a, R: ?Sized, F> {
11 reader: &'a mut R,
12 buf: &'a mut [u8],
13 n: usize,
14 f: F,
15}
16
17impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {}
18
19impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> ReadWhile<'a, R, F> {
20 pub(super) fn new(reader: &'a mut R, f: F, buf: &'a mut [u8]) -> Self {
21 Self {
22 reader,
23 f,
24 buf,
25 n: 0,
26 }
27 }
28}
29
30impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for ReadWhile<'a, R, F> {
31 type Output = Result<usize>;
32
33 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
34 let Self { reader, f, buf, n } = &mut *self;
35 let mut reader = Pin::new(reader);
36 loop {
37 let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
38 if rbuf.len() == 0 {
39 return Poll::Ready(Err(Error::UnexpectedEof));
40 }
41
42 let (p, done) = match rbuf.iter().position(|&b| !f(b)) {
43 Some(p) => (p, true),
44 None => (rbuf.len(), false),
45 };
46
47 // truncate data if it doesn't fit in buf
48 let p2 = min(p, buf.len() - *n);
49 buf[*n..*n + p2].copy_from_slice(&rbuf[..p2]);
50 *n += p2;
51
52 // consume it all, even if it doesn't fit.
53 // Otherwise we can deadlock because we never read to the ending char
54 reader.as_mut().consume(p);
55
56 if done {
57 return Poll::Ready(Ok(*n));
58 }
59 }
60 }
61}
diff --git a/embassy/src/io/util/skip_while.rs b/embassy/src/io/util/skip_while.rs
new file mode 100644
index 000000000..8c81ad209
--- /dev/null
+++ b/embassy/src/io/util/skip_while.rs
@@ -0,0 +1,45 @@
1use core::iter::Iterator;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::{Error, Result};
8use super::super::traits::AsyncBufRead;
9
10pub struct SkipWhile<'a, R: ?Sized, F> {
11 reader: &'a mut R,
12 f: F,
13}
14
15impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {}
16
17impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> SkipWhile<'a, R, F> {
18 pub(super) fn new(reader: &'a mut R, f: F) -> Self {
19 Self { reader, f }
20 }
21}
22
23impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for SkipWhile<'a, R, F> {
24 type Output = Result<()>;
25
26 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
27 let Self { reader, f } = &mut *self;
28 let mut reader = Pin::new(reader);
29 loop {
30 let buf = ready!(reader.as_mut().poll_fill_buf(cx))?;
31 if buf.len() == 0 {
32 return Poll::Ready(Err(Error::UnexpectedEof));
33 }
34
35 let (p, done) = match buf.iter().position(|b| !f(*b)) {
36 Some(p) => (p, true),
37 None => (buf.len(), false),
38 };
39 reader.as_mut().consume(p);
40 if done {
41 return Poll::Ready(Ok(()));
42 }
43 }
44 }
45}
diff --git a/embassy/src/io/util/split.rs b/embassy/src/io/util/split.rs
new file mode 100644
index 000000000..0cebb5cbd
--- /dev/null
+++ b/embassy/src/io/util/split.rs
@@ -0,0 +1,40 @@
1use alloc::rc::Rc;
2use core::cell::UnsafeCell;
3use core::pin::Pin;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::{AsyncBufRead, AsyncWrite};
8
9/// The readable half of an object returned from `AsyncBufRead::split`.
10#[derive(Debug)]
11pub struct ReadHalf<T> {
12 handle: Rc<UnsafeCell<T>>,
13}
14
15/// The writable half of an object returned from `AsyncBufRead::split`.
16#[derive(Debug)]
17pub struct WriteHalf<T> {
18 handle: Rc<UnsafeCell<T>>,
19}
20
21impl<T: AsyncBufRead + Unpin> AsyncBufRead for ReadHalf<T> {
22 fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
23 Pin::new(unsafe { &mut *self.handle.get() }).poll_fill_buf(cx)
24 }
25
26 fn consume(self: Pin<&mut Self>, amt: usize) {
27 Pin::new(unsafe { &mut *self.handle.get() }).consume(amt)
28 }
29}
30
31impl<T: AsyncWrite + Unpin> AsyncWrite for WriteHalf<T> {
32 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
33 Pin::new(unsafe { &mut *self.handle.get() }).poll_write(cx, buf)
34 }
35}
36
37pub fn split<T: AsyncBufRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
38 let c = Rc::new(UnsafeCell::new(t));
39 (ReadHalf { handle: c.clone() }, WriteHalf { handle: c })
40}
diff --git a/embassy/src/io/util/write.rs b/embassy/src/io/util/write.rs
new file mode 100644
index 000000000..403cd59fe
--- /dev/null
+++ b/embassy/src/io/util/write.rs
@@ -0,0 +1,33 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::AsyncWrite;
8
9/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
10#[derive(Debug)]
11#[must_use = "futures do nothing unless you `.await` or poll them"]
12pub struct Write<'a, W: ?Sized> {
13 writer: &'a mut W,
14 buf: &'a [u8],
15}
16
17impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {}
18
19impl<'a, W: AsyncWrite + ?Sized + Unpin> Write<'a, W> {
20 pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
21 Write { writer, buf }
22 }
23}
24
25impl<W: AsyncWrite + ?Sized + Unpin> Future for Write<'_, W> {
26 type Output = Result<usize>;
27
28 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<usize>> {
29 let this = &mut *self;
30 let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
31 Poll::Ready(Ok(n))
32 }
33}
diff --git a/embassy/src/io/util/write_all.rs b/embassy/src/io/util/write_all.rs
new file mode 100644
index 000000000..76b6ec092
--- /dev/null
+++ b/embassy/src/io/util/write_all.rs
@@ -0,0 +1,44 @@
1use core::mem;
2use core::pin::Pin;
3use futures::future::Future;
4use futures::ready;
5use futures::task::{Context, Poll};
6
7use super::super::error::Result;
8use super::super::traits::AsyncWrite;
9
10/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
11#[derive(Debug)]
12#[must_use = "futures do nothing unless you `.await` or poll them"]
13pub struct WriteAll<'a, W: ?Sized> {
14 writer: &'a mut W,
15 buf: &'a [u8],
16}
17
18impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {}
19
20impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> {
21 pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
22 WriteAll { writer, buf }
23 }
24}
25
26impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteAll<'_, W> {
27 type Output = Result<()>;
28
29 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
30 let this = &mut *self;
31 while !this.buf.is_empty() {
32 let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
33 {
34 let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n);
35 this.buf = rest;
36 }
37 if n == 0 {
38 panic!();
39 }
40 }
41
42 Poll::Ready(Ok(()))
43 }
44}
diff --git a/embassy/src/io/util/write_byte.rs b/embassy/src/io/util/write_byte.rs
new file mode 100644
index 000000000..659e427b1
--- /dev/null
+++ b/embassy/src/io/util/write_byte.rs
@@ -0,0 +1,39 @@
1use core::pin::Pin;
2use futures::future::Future;
3use futures::ready;
4use futures::task::{Context, Poll};
5
6use super::super::error::Result;
7use super::super::traits::AsyncWrite;
8
9/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
10#[derive(Debug)]
11#[must_use = "futures do nothing unless you `.await` or poll them"]
12pub struct WriteByte<'a, W: ?Sized> {
13 writer: &'a mut W,
14 byte: u8,
15}
16
17impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {}
18
19impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteByte<'a, W> {
20 pub(super) fn new(writer: &'a mut W, byte: u8) -> Self {
21 WriteByte { writer, byte }
22 }
23}
24
25impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteByte<'_, W> {
26 type Output = Result<()>;
27
28 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
29 let this = &mut *self;
30 let buf = [this.byte; 1];
31 let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &buf))?;
32 if n == 0 {
33 panic!();
34 }
35 assert!(n == 1);
36
37 Poll::Ready(Ok(()))
38 }
39}
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs
new file mode 100644
index 000000000..45716c6cf
--- /dev/null
+++ b/embassy/src/lib.rs
@@ -0,0 +1,8 @@
1#![no_std]
2#![feature(slice_fill)]
3#![feature(generic_associated_types)]
4#![feature(const_fn)]
5
6pub mod flash;
7pub mod util;
8pub mod io;
diff --git a/embassy/src/util/drop_bomb.rs b/embassy/src/util/drop_bomb.rs
new file mode 100644
index 000000000..2a995a826
--- /dev/null
+++ b/embassy/src/util/drop_bomb.rs
@@ -0,0 +1,21 @@
1use core::mem;
2
3pub struct DropBomb {
4 _private: (),
5}
6
7impl DropBomb {
8 pub fn new() -> Self {
9 Self { _private: () }
10 }
11
12 pub fn defuse(self) {
13 mem::forget(self)
14 }
15}
16
17impl Drop for DropBomb {
18 fn drop(&mut self) {
19 depanic!("boom")
20 }
21}
diff --git a/embassy/src/util/macros.rs b/embassy/src/util/macros.rs
new file mode 100644
index 000000000..69987e42c
--- /dev/null
+++ b/embassy/src/util/macros.rs
@@ -0,0 +1,32 @@
1#![macro_use]
2
3macro_rules! depanic {
4 ($( $i:expr ),*) => {
5 {
6 defmt::error!($( $i ),*);
7 panic!();
8 }
9 }
10}
11
12macro_rules! deassert {
13 ($cond:expr) => {
14 deassert!($cond, "assertion failed");
15 };
16 ($cond:expr, $msg:literal) => {
17 {
18 if !$cond {
19 defmt::error!($msg);
20 panic!();
21 }
22 }
23 };
24 ($cond:expr, $msg:literal, $( $i:expr ),*) => {
25 {
26 if !$cond {
27 defmt::error!($msg, $( $i ),*);
28 panic!();
29 }
30 }
31 };
32}
diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs
new file mode 100644
index 000000000..3a0f11e6f
--- /dev/null
+++ b/embassy/src/util/mod.rs
@@ -0,0 +1,70 @@
1#![macro_use]
2
3mod macros;
4
5mod signal;
6pub use signal::*;
7mod portal;
8pub use portal::*;
9mod waker_store;
10pub use waker_store::*;
11mod drop_bomb;
12pub use drop_bomb::*;
13
14use defmt::{warn, error};
15
16pub trait Dewrap<T> {
17 /// dewrap = defmt unwrap
18 fn dewrap(self) -> T;
19
20 /// dexpect = defmt expect
21 fn dexpect<M: defmt::Format>(self, msg: M) -> T;
22
23 fn dewarn<M: defmt::Format>(self, msg: M) -> Self;
24}
25
26impl<T> Dewrap<T> for Option<T> {
27 fn dewrap(self) -> T {
28 match self {
29 Some(t) => t,
30 None => depanic!("unwrap failed: enum is none"),
31 }
32 }
33
34 fn dexpect<M: defmt::Format>(self, msg: M) -> T {
35 match self {
36 Some(t) => t,
37 None => depanic!("unexpected None: {:?}", msg),
38 }
39 }
40
41 fn dewarn<M: defmt::Format>(self, msg: M) -> Self {
42 if self.is_none() {
43 warn!("{:?} is none", msg);
44 }
45 self
46 }
47}
48
49impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> {
50 fn dewrap(self) -> T {
51 match self {
52 Ok(t) => t,
53 Err(e) => depanic!("unwrap failed: {:?}", e),
54 }
55 }
56
57 fn dexpect<M: defmt::Format>(self, msg: M) -> T {
58 match self {
59 Ok(t) => t,
60 Err(e) => depanic!("unexpected error: {:?}: {:?}", msg, e),
61 }
62 }
63
64 fn dewarn<M: defmt::Format>(self, msg: M) -> Self {
65 if let Err(e) = &self {
66 warn!("{:?} err: {:?}", msg, e);
67 }
68 self
69 }
70}
diff --git a/embassy/src/util/portal.rs b/embassy/src/util/portal.rs
new file mode 100644
index 000000000..e01968c5f
--- /dev/null
+++ b/embassy/src/util/portal.rs
@@ -0,0 +1,125 @@
1use core::cell::UnsafeCell;
2use core::future::Future;
3use core::mem;
4use core::mem::MaybeUninit;
5
6use crate::util::*;
7
8/// Utility to call a closure across tasks.
9pub struct Portal<T> {
10 state: UnsafeCell<State<T>>,
11}
12
13enum State<T> {
14 None,
15 Running,
16 Waiting(*mut dyn FnMut(T)),
17}
18
19impl<T> Portal<T> {
20 pub const fn new() -> Self {
21 Self {
22 state: UnsafeCell::new(State::None),
23 }
24 }
25
26 pub fn call(&self, val: T) {
27 unsafe {
28 match *self.state.get() {
29 State::None => {}
30 State::Running => depanic!("Portall::call() called reentrantly"),
31 State::Waiting(func) => (*func)(val),
32 }
33 }
34 }
35
36 pub fn wait_once<'a, R, F>(&'a self, mut func: F) -> impl Future<Output = R> + 'a
37 where
38 F: FnMut(T) -> R + 'a,
39 {
40 async move {
41 let bomb = DropBomb::new();
42
43 let signal = Signal::new();
44 let mut result: MaybeUninit<R> = MaybeUninit::uninit();
45 let mut call_func = |val: T| {
46 unsafe {
47 let state = &mut *self.state.get();
48 *state = State::None;
49 result.as_mut_ptr().write(func(val))
50 };
51 signal.signal(());
52 };
53
54 let func_ptr: *mut dyn FnMut(T) = &mut call_func as _;
55 let func_ptr: *mut dyn FnMut(T) = unsafe { mem::transmute(func_ptr) };
56
57 unsafe {
58 let state = &mut *self.state.get();
59 match state {
60 State::None => {}
61 _ => depanic!("Multiple tasks waiting on same portal"),
62 }
63 *state = State::Waiting(func_ptr);
64 }
65
66 signal.wait().await;
67
68 bomb.defuse();
69
70 unsafe { result.assume_init() }
71 }
72 }
73
74 pub fn wait_many<'a, R, F>(&'a self, mut func: F) -> impl Future<Output = R> + 'a
75 where
76 F: FnMut(T) -> Option<R> + 'a,
77 {
78 async move {
79 let bomb = DropBomb::new();
80
81 let signal = Signal::new();
82 let mut result: MaybeUninit<R> = MaybeUninit::uninit();
83 let mut call_func = |val: T| {
84 unsafe {
85 let state = &mut *self.state.get();
86
87 let func_ptr = match *state {
88 State::Waiting(p) => p,
89 _ => unreachable!(),
90 };
91
92 // Set state to Running while running the function to avoid reentrancy.
93 *state = State::Running;
94
95 *state = match func(val) {
96 None => State::Waiting(func_ptr),
97 Some(res) => {
98 result.as_mut_ptr().write(res);
99 signal.signal(());
100 State::None
101 }
102 };
103 };
104 };
105
106 let func_ptr: *mut dyn FnMut(T) = &mut call_func as _;
107 let func_ptr: *mut dyn FnMut(T) = unsafe { mem::transmute(func_ptr) };
108
109 unsafe {
110 let state = &mut *self.state.get();
111 match *state {
112 State::None => {}
113 _ => depanic!("Multiple tasks waiting on same portal"),
114 }
115 *state = State::Waiting(func_ptr);
116 }
117
118 signal.wait().await;
119
120 bomb.defuse();
121
122 unsafe { result.assume_init() }
123 }
124 }
125}
diff --git a/embassy/src/util/signal.rs b/embassy/src/util/signal.rs
new file mode 100644
index 000000000..32286a30e
--- /dev/null
+++ b/embassy/src/util/signal.rs
@@ -0,0 +1,70 @@
1use core::cell::UnsafeCell;
2use core::future::Future;
3use core::mem;
4use core::pin::Pin;
5use core::task::{Context, Poll, Waker};
6
7pub struct Signal<T> {
8 state: UnsafeCell<State<T>>,
9}
10
11enum State<T> {
12 None,
13 Waiting(Waker),
14 Signaled(T),
15}
16
17unsafe impl<T: Send> Send for Signal<T> {}
18unsafe impl<T: Send> Sync for Signal<T> {}
19
20impl<T: Send> Signal<T> {
21 pub const fn new() -> Self {
22 Self {
23 state: UnsafeCell::new(State::None),
24 }
25 }
26
27 pub fn signal(&self, val: T) {
28 unsafe {
29 cortex_m::interrupt::free(|_| {
30 let state = &mut *self.state.get();
31 match mem::replace(state, State::Signaled(val)) {
32 State::Waiting(waker) => waker.wake(),
33 _ => {}
34 }
35 })
36 }
37 }
38
39 pub fn wait<'a>(&'a self) -> impl Future<Output = T> + 'a {
40 WaitFuture { signal: self }
41 }
42}
43
44struct WaitFuture<'a, T> {
45 signal: &'a Signal<T>,
46}
47
48impl<'a, T: Send> Future for WaitFuture<'a, T> {
49 type Output = T;
50
51 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
52 unsafe {
53 cortex_m::interrupt::free(|_| {
54 let state = &mut *self.signal.state.get();
55 match state {
56 State::None => {
57 *state = State::Waiting(cx.waker().clone());
58 Poll::Pending
59 }
60 State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending,
61 State::Waiting(_) => depanic!("waker overflow"),
62 State::Signaled(_) => match mem::replace(state, State::None) {
63 State::Signaled(res) => Poll::Ready(res),
64 _ => unreachable!(),
65 },
66 }
67 })
68 }
69 }
70}
diff --git a/embassy/src/util/waker_store.rs b/embassy/src/util/waker_store.rs
new file mode 100644
index 000000000..0b2f09f4b
--- /dev/null
+++ b/embassy/src/util/waker_store.rs
@@ -0,0 +1,23 @@
1use core::task::Waker;
2
3pub struct WakerStore {
4 waker: Option<Waker>,
5}
6
7impl WakerStore {
8 pub const fn new() -> Self {
9 Self { waker: None }
10 }
11
12 pub fn store(&mut self, w: &Waker) {
13 match self.waker {
14 Some(ref w2) if (w2.will_wake(w)) => {}
15 Some(_) => panic!("Waker overflow"),
16 None => self.waker = Some(w.clone()),
17 }
18 }
19
20 pub fn wake(&mut self) {
21 self.waker.take().map(|w| w.wake());
22 }
23}
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
new file mode 100644
index 000000000..c243691a7
--- /dev/null
+++ b/examples/Cargo.toml
@@ -0,0 +1,31 @@
1[package]
2authors = ["Dario Nieuwenhuis <[email protected]>"]
3edition = "2018"
4name = "embassy-examples"
5version = "0.1.0"
6
7[features]
8default = [
9 "defmt-default",
10]
11defmt-default = []
12defmt-trace = []
13defmt-debug = []
14defmt-info = []
15defmt-warn = []
16defmt-error = []
17
18
19[dependencies]
20cortex-m = { version = "0.6.3" }
21cortex-m-rt = "0.6.12"
22defmt = "0.1.0"
23embedded-hal = { version = "0.2.4" }
24defmt-rtt = "0.1.0"
25panic-probe = "0.1.0"
26nrf52840-hal = { version = "0.11.0" }
27embassy = { version = "0.1.0", path = "../embassy" }
28embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "nrf52840"] }
29static-executor = { version = "0.1.0", features=["defmt"]}
30static-executor-cortex-m = { version = "0.1.0" }
31futures = { version = "0.3.5", default-features = false }
diff --git a/examples/build.rs b/examples/build.rs
new file mode 100644
index 000000000..d534cc3df
--- /dev/null
+++ b/examples/build.rs
@@ -0,0 +1,31 @@
1//! This build script copies the `memory.x` file from the crate root into
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}
diff --git a/examples/memory.x b/examples/memory.x
new file mode 100644
index 000000000..9b04edec0
--- /dev/null
+++ b/examples/memory.x
@@ -0,0 +1,7 @@
1MEMORY
2{
3 /* NOTE 1 K = 1 KiBi = 1024 bytes */
4 /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
5 FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
6 RAM : ORIGIN = 0x20000000, LENGTH = 256K
7}
diff --git a/examples/src/bin/qspi.rs b/examples/src/bin/qspi.rs
new file mode 100644
index 000000000..395422e7f
--- /dev/null
+++ b/examples/src/bin/qspi.rs
@@ -0,0 +1,123 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use example_common::*;
8
9use cortex_m_rt::entry;
10use embassy::flash::Flash;
11use embassy_nrf::qspi;
12use nrf52840_hal::gpio;
13
14const PAGE_SIZE: usize = 4096;
15
16// Workaround for alignment requirements.
17// Nicer API will probably come in the future.
18#[repr(C, align(4))]
19struct AlignedBuf([u8; 4096]);
20
21#[static_executor::task]
22async fn run() {
23 let p = embassy_nrf::pac::Peripherals::take().dewrap();
24
25 let port0 = gpio::p0::Parts::new(p.P0);
26
27 let pins = qspi::Pins {
28 csn: port0
29 .p0_17
30 .into_push_pull_output(gpio::Level::High)
31 .degrade(),
32 sck: port0
33 .p0_19
34 .into_push_pull_output(gpio::Level::High)
35 .degrade(),
36 io0: port0
37 .p0_20
38 .into_push_pull_output(gpio::Level::High)
39 .degrade(),
40 io1: port0
41 .p0_21
42 .into_push_pull_output(gpio::Level::High)
43 .degrade(),
44 io2: Some(
45 port0
46 .p0_22
47 .into_push_pull_output(gpio::Level::High)
48 .degrade(),
49 ),
50 io3: Some(
51 port0
52 .p0_23
53 .into_push_pull_output(gpio::Level::High)
54 .degrade(),
55 ),
56 };
57
58 let config = qspi::Config {
59 pins,
60 read_opcode: qspi::ReadOpcode::READ4IO,
61 write_opcode: qspi::WriteOpcode::PP4IO,
62 xip_offset: 0,
63 write_page_size: qspi::WritePageSize::_256BYTES,
64 };
65
66 let mut q = qspi::Qspi::new(p.QSPI, config);
67
68 let mut id = [1; 3];
69 q.custom_instruction(0x9F, &[], &mut id).await.unwrap();
70 info!("id: {:[u8]}", id);
71
72 // Read status register
73 let mut status = [0; 1];
74 q.custom_instruction(0x05, &[], &mut status).await.unwrap();
75
76 info!("status: {:?}", status[0]);
77
78 if status[0] & 0x40 == 0 {
79 status[0] |= 0x40;
80
81 q.custom_instruction(0x01, &status, &mut []).await.unwrap();
82
83 info!("enabled quad in status");
84 }
85
86 let mut buf = AlignedBuf([0u8; PAGE_SIZE]);
87
88 let pattern = |a: u32| (a ^ (a >> 8) ^ (a >> 16) ^ (a >> 24)) as u8;
89
90 for i in 0..8 {
91 info!("page {:?}: erasing... ", i);
92 q.erase(i * PAGE_SIZE).await.unwrap();
93
94 for j in 0..PAGE_SIZE {
95 buf.0[j] = pattern((j + i * PAGE_SIZE) as u32);
96 }
97
98 info!("programming...");
99 q.write(i * PAGE_SIZE, &buf.0).await.unwrap();
100 }
101
102 for i in 0..8 {
103 info!("page {:?}: reading... ", i);
104 q.read(i * PAGE_SIZE, &mut buf.0).await.unwrap();
105
106 info!("verifying...");
107 for j in 0..PAGE_SIZE {
108 assert_eq!(buf.0[j], pattern((j + i * PAGE_SIZE) as u32));
109 }
110 }
111
112 info!("done!")
113}
114
115#[entry]
116fn main() -> ! {
117 info!("Hello World!");
118
119 unsafe {
120 run.spawn().dewrap();
121 static_executor::run();
122 }
123}
diff --git a/examples/src/bin/uart.rs b/examples/src/bin/uart.rs
new file mode 100644
index 000000000..21e26e3ad
--- /dev/null
+++ b/examples/src/bin/uart.rs
@@ -0,0 +1,72 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7use example_common::*;
8
9use cortex_m_rt::entry;
10use embassy::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt};
11use embassy_nrf::uarte;
12use futures::pin_mut;
13use nrf52840_hal::gpio;
14
15#[static_executor::task]
16async fn run() {
17 let p = embassy_nrf::pac::Peripherals::take().dewrap();
18
19 let port0 = gpio::p0::Parts::new(p.P0);
20
21 let pins = uarte::Pins {
22 rxd: port0.p0_08.into_floating_input().degrade(),
23 txd: port0
24 .p0_06
25 .into_push_pull_output(gpio::Level::Low)
26 .degrade(),
27 cts: None,
28 rts: None,
29 };
30
31 let u = uarte::Uarte::new(
32 p.UARTE0,
33 pins,
34 uarte::Parity::EXCLUDED,
35 uarte::Baudrate::BAUD115200,
36 );
37 pin_mut!(u);
38
39 info!("uarte initialized!");
40
41 u.write_all(b"Hello!\r\n").await.dewrap();
42 info!("wrote hello in uart!");
43
44 // Simple demo, reading 8-char chunks and echoing them back reversed.
45 loop {
46 info!("reading...");
47 let mut buf = [0u8; 8];
48 u.read_exact(&mut buf).await.dewrap();
49 info!("read done, got {:[u8]}", buf);
50
51 // Reverse buf
52 for i in 0..4 {
53 let tmp = buf[i];
54 buf[i] = buf[7 - i];
55 buf[7 - i] = tmp;
56 }
57
58 info!("writing...");
59 u.write_all(&buf).await.dewrap();
60 info!("write done");
61 }
62}
63
64#[entry]
65fn main() -> ! {
66 info!("Hello World!");
67
68 unsafe {
69 run.spawn().dewrap();
70 static_executor::run();
71 }
72}
diff --git a/examples/src/example_common.rs b/examples/src/example_common.rs
new file mode 100644
index 000000000..e9919153c
--- /dev/null
+++ b/examples/src/example_common.rs
@@ -0,0 +1,68 @@
1#![macro_use]
2
3use defmt_rtt as _; // global logger
4use nrf52840_hal as _;
5use panic_probe as _;
6use static_executor_cortex_m as _;
7
8pub use defmt::{info, intern};
9
10use core::sync::atomic::{AtomicUsize, Ordering};
11
12#[defmt::timestamp]
13fn timestamp() -> u64 {
14 static COUNT: AtomicUsize = AtomicUsize::new(0);
15 // NOTE(no-CAS) `timestamps` runs with interrupts disabled
16 let n = COUNT.load(Ordering::Relaxed);
17 COUNT.store(n + 1, Ordering::Relaxed);
18 n as u64
19}
20
21macro_rules! depanic {
22 ($( $i:expr ),*) => {
23 {
24 defmt::error!($( $i ),*);
25 panic!();
26 }
27 }
28}
29
30pub trait Dewrap<T> {
31 /// dewrap = defmt unwrap
32 fn dewrap(self) -> T;
33
34 /// dexpect = defmt expect
35 fn dexpect<M: defmt::Format>(self, msg: M) -> T;
36}
37
38impl<T> Dewrap<T> for Option<T> {
39 fn dewrap(self) -> T {
40 match self {
41 Some(t) => t,
42 None => depanic!("Dewrap failed: enum is none"),
43 }
44 }
45
46 fn dexpect<M: defmt::Format>(self, msg: M) -> T {
47 match self {
48 Some(t) => t,
49 None => depanic!("Unexpected None: {:?}", msg),
50 }
51 }
52}
53
54impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> {
55 fn dewrap(self) -> T {
56 match self {
57 Ok(t) => t,
58 Err(e) => depanic!("Dewrap failed: {:?}", e),
59 }
60 }
61
62 fn dexpect<M: defmt::Format>(self, msg: M) -> T {
63 match self {
64 Ok(t) => t,
65 Err(e) => depanic!("Unexpected error: {:?}: {:?}", msg, e),
66 }
67 }
68}