diff options
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"))'] | ||
| 2 | runner = "probe-run --chip nRF52840_xxAA --defmt" | ||
| 3 | |||
| 4 | rustflags = [ | ||
| 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) | ||
| 27 | target = "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 | ||
| 2 | Cargo.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] | ||
| 3 | members = [ | ||
| 4 | "embassy", | ||
| 5 | "embassy-nrf", | ||
| 6 | "examples", | ||
| 7 | ] | ||
| 8 | |||
| 9 | [patch.crates-io] | ||
| 10 | panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } | ||
| 11 | defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | ||
| 12 | defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | ||
| 13 | static-executor = { git = "https://github.com/Dirbaio/static-executor" } | ||
| 14 | static-executor-cortex-m = { git = "https://github.com/Dirbaio/static-executor" } | ||
| 15 | |||
| 16 | [profile.dev] | ||
| 17 | codegen-units = 1 | ||
| 18 | debug = 2 | ||
| 19 | debug-assertions = true | ||
| 20 | incremental = false | ||
| 21 | opt-level = 3 | ||
| 22 | overflow-checks = true | ||
| 23 | |||
| 24 | [profile.release] | ||
| 25 | codegen-units = 1 | ||
| 26 | debug = 2 | ||
| 27 | debug-assertions = false | ||
| 28 | incremental = false | ||
| 29 | lto = "fat" | ||
| 30 | opt-level = 3 | ||
| 31 | overflow-checks = false | ||
| 32 | |||
| 33 | # do not optimize proc-macro crates = faster builds from scratch | ||
| 34 | [profile.dev.build-override] | ||
| 35 | codegen-units = 8 | ||
| 36 | debug = false | ||
| 37 | debug-assertions = false | ||
| 38 | opt-level = 0 | ||
| 39 | overflow-checks = false | ||
| 40 | |||
| 41 | [profile.release.build-override] | ||
| 42 | codegen-units = 8 | ||
| 43 | debug = false | ||
| 44 | debug-assertions = false | ||
| 45 | opt-level = 0 | ||
| 46 | overflow-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 | |||
| 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
| 6 | |||
| 7 | 1. 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 | |||
| 66 | 2. 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 | |||
| 73 | 3. 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 | |||
| 89 | 4. 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 | |||
| 130 | 5. 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 | |||
| 138 | 6. 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 | |||
| 143 | 7. 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 | |||
| 153 | 8. 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 | |||
| 165 | 9. 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 | |||
| 176 | END OF TERMS AND CONDITIONS | ||
| 177 | |||
| 178 | APPENDIX: 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 | |||
| 189 | Copyright [yyyy] [name of copyright owner] | ||
| 190 | |||
| 191 | Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 192 | you may not use this file except in compliance with the License. | ||
| 193 | You may obtain a copy of the License at | ||
| 194 | |||
| 195 | http://www.apache.org/licenses/LICENSE-2.0 | ||
| 196 | |||
| 197 | Unless required by applicable law or agreed to in writing, software | ||
| 198 | distributed under the License is distributed on an "AS IS" BASIS, | ||
| 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 200 | See the License for the specific language governing permissions and | ||
| 201 | limitations 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 @@ | |||
| 1 | Copyright (c) 2020 Dario Nieuwenhuis | ||
| 2 | |||
| 3 | Permission is hereby granted, free of charge, to any | ||
| 4 | person obtaining a copy of this software and associated | ||
| 5 | documentation files (the "Software"), to deal in the | ||
| 6 | Software without restriction, including without | ||
| 7 | limitation the rights to use, copy, modify, merge, | ||
| 8 | publish, distribute, sublicense, and/or sell copies of | ||
| 9 | the Software, and to permit persons to whom the Software | ||
| 10 | is furnished to do so, subject to the following | ||
| 11 | conditions: | ||
| 12 | |||
| 13 | The above copyright notice and this permission notice | ||
| 14 | shall be included in all copies or substantial portions | ||
| 15 | of the Software. | ||
| 16 | |||
| 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF | ||
| 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED | ||
| 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | ||
| 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | ||
| 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR | ||
| 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
| 25 | DEALINGS 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 | |||
| 3 | Embassy is a project to make async/await a first-class option for embedded development. | ||
| 4 | |||
| 5 | The `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 | |||
| 11 | The `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 | |||
| 16 | Currently 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 | |||
| 20 | EMBedded ASYnc. | ||
| 21 | |||
| 22 | ## License | ||
| 23 | |||
| 24 | This 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 | |||
| 30 | at 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] | ||
| 2 | name = "embassy-nrf" | ||
| 3 | version = "0.1.0" | ||
| 4 | authors = ["Dario Nieuwenhuis <[email protected]>"] | ||
| 5 | edition = "2018" | ||
| 6 | |||
| 7 | [features] | ||
| 8 | default = [ | ||
| 9 | "defmt-default", | ||
| 10 | ] | ||
| 11 | defmt-default = [] | ||
| 12 | defmt-trace = [] | ||
| 13 | defmt-debug = [] | ||
| 14 | defmt-info = [] | ||
| 15 | defmt-warn = [] | ||
| 16 | defmt-error = [] | ||
| 17 | |||
| 18 | nrf52810 = ["nrf52810-pac"] | ||
| 19 | nrf52811 = ["nrf52811-pac"] | ||
| 20 | nrf52832 = ["nrf52832-pac"] | ||
| 21 | nrf52833 = ["nrf52833-pac"] | ||
| 22 | nrf52840 = ["nrf52840-pac"] | ||
| 23 | |||
| 24 | |||
| 25 | [dependencies] | ||
| 26 | embassy = { version = "0.1.0", path = "../embassy" } | ||
| 27 | cortex-m-rt = "0.6.12" | ||
| 28 | cortex-m = { version = "0.6.3" } | ||
| 29 | embedded-hal = { version = "0.2.4" } | ||
| 30 | nrf52840-hal = { version = "0.11.0" } | ||
| 31 | bare-metal = { version = "0.2.0", features = ["const-fn"] } | ||
| 32 | defmt = "0.1.0" | ||
| 33 | |||
| 34 | nrf52810-pac = { version = "0.9.0", optional = true } | ||
| 35 | nrf52811-pac = { version = "0.9.0", optional = true } | ||
| 36 | nrf52832-pac = { version = "0.9.0", optional = true } | ||
| 37 | nrf52833-pac = { version = "0.9.0", optional = true } | ||
| 38 | nrf52840-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 | |||
| 6 | use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; | ||
| 7 | |||
| 8 | use crate::pac::{NVIC, NVIC_PRIO_BITS}; | ||
| 9 | |||
| 10 | // Re-exports | ||
| 11 | pub use crate::pac::Interrupt; | ||
| 12 | pub use crate::pac::Interrupt::*; // needed for cortex-m-rt #[interrupt] | ||
| 13 | pub use bare_metal::{CriticalSection, Mutex}; | ||
| 14 | |||
| 15 | #[derive(defmt::Format, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | ||
| 16 | #[repr(u8)] | ||
| 17 | pub 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 | |||
| 28 | impl 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 | |||
| 50 | static CS_FLAG: AtomicBool = AtomicBool::new(false); | ||
| 51 | static mut CS_MASK: [u32; 2] = [0; 2]; | ||
| 52 | |||
| 53 | #[inline] | ||
| 54 | pub fn free<F, R>(f: F) -> R | ||
| 55 | where | ||
| 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] | ||
| 83 | pub fn enable(irq: Interrupt) { | ||
| 84 | unsafe { | ||
| 85 | NVIC::unmask(irq); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | #[inline] | ||
| 90 | pub fn disable(irq: Interrupt) { | ||
| 91 | NVIC::mask(irq); | ||
| 92 | } | ||
| 93 | |||
| 94 | #[inline] | ||
| 95 | pub fn is_active(irq: Interrupt) -> bool { | ||
| 96 | NVIC::is_active(irq) | ||
| 97 | } | ||
| 98 | |||
| 99 | #[inline] | ||
| 100 | pub fn is_enabled(irq: Interrupt) -> bool { | ||
| 101 | NVIC::is_enabled(irq) | ||
| 102 | } | ||
| 103 | |||
| 104 | #[inline] | ||
| 105 | pub fn is_pending(irq: Interrupt) -> bool { | ||
| 106 | NVIC::is_pending(irq) | ||
| 107 | } | ||
| 108 | |||
| 109 | #[inline] | ||
| 110 | pub fn pend(irq: Interrupt) { | ||
| 111 | NVIC::pend(irq) | ||
| 112 | } | ||
| 113 | |||
| 114 | #[inline] | ||
| 115 | pub fn unpend(irq: Interrupt) { | ||
| 116 | NVIC::unpend(irq) | ||
| 117 | } | ||
| 118 | |||
| 119 | #[inline] | ||
| 120 | pub fn get_priority(irq: Interrupt) -> Priority { | ||
| 121 | Priority::from_nvic(NVIC::get_priority(irq)) | ||
| 122 | } | ||
| 123 | |||
| 124 | #[inline] | ||
| 125 | pub 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 | )))] | ||
| 13 | compile_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 | ))] | ||
| 27 | compile_error!("Multile chip features activated. You must activate exactly one of the following features: nrf52810, nrf52811, nrf52832, nrf52833, nrf52840"); | ||
| 28 | |||
| 29 | #[cfg(feature = "nrf52810")] | ||
| 30 | pub use nrf52810_pac as pac; | ||
| 31 | #[cfg(feature = "nrf52811")] | ||
| 32 | pub use nrf52811_pac as pac; | ||
| 33 | #[cfg(feature = "nrf52832")] | ||
| 34 | pub use nrf52832_pac as pac; | ||
| 35 | #[cfg(feature = "nrf52833")] | ||
| 36 | pub use nrf52833_pac as pac; | ||
| 37 | #[cfg(feature = "nrf52840")] | ||
| 38 | pub use nrf52840_pac as pac; | ||
| 39 | |||
| 40 | pub mod interrupt; | ||
| 41 | pub mod qspi; | ||
| 42 | pub mod uarte; | ||
| 43 | pub 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 @@ | |||
| 1 | use crate::pac::{Interrupt, QSPI}; | ||
| 2 | use core::future::Future; | ||
| 3 | use nrf52840_hal::gpio::{Output, Pin as GpioPin, Port as GpioPort, PushPull}; | ||
| 4 | |||
| 5 | pub use crate::pac::qspi::ifconfig0::ADDRMODE_A as AddressMode; | ||
| 6 | pub use crate::pac::qspi::ifconfig0::PPSIZE_A as WritePageSize; | ||
| 7 | pub use crate::pac::qspi::ifconfig0::READOC_A as ReadOpcode; | ||
| 8 | pub 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 | |||
| 20 | use embassy::flash::{Error, Flash}; | ||
| 21 | use embassy::util::{DropBomb, Signal}; | ||
| 22 | |||
| 23 | use crate::interrupt; | ||
| 24 | |||
| 25 | pub 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 | |||
| 34 | pub 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 | |||
| 42 | pub struct Qspi { | ||
| 43 | inner: QSPI, | ||
| 44 | } | ||
| 45 | |||
| 46 | fn port_bit(port: GpioPort) -> bool { | ||
| 47 | match port { | ||
| 48 | GpioPort::Port0 => false, | ||
| 49 | GpioPort::Port1 => true, | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | impl 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 | |||
| 199 | impl 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 | |||
| 313 | static SIGNAL: Signal<()> = Signal::new(); | ||
| 314 | |||
| 315 | #[interrupt] | ||
| 316 | unsafe 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 | ||
| 7 | use core::cell::UnsafeCell; | ||
| 8 | use core::cmp::min; | ||
| 9 | use core::marker::PhantomPinned; | ||
| 10 | use core::ops::Deref; | ||
| 11 | use core::pin::Pin; | ||
| 12 | use core::ptr; | ||
| 13 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 14 | use core::task::{Context, Poll}; | ||
| 15 | |||
| 16 | use crate::interrupt; | ||
| 17 | use crate::interrupt::CriticalSection; | ||
| 18 | use crate::pac::{uarte0, Interrupt, UARTE0, UARTE1}; | ||
| 19 | use embedded_hal::digital::v2::OutputPin; | ||
| 20 | use 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 | ||
| 23 | pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; | ||
| 24 | |||
| 25 | use embassy::io::{AsyncBufRead, AsyncWrite, Result}; | ||
| 26 | use embassy::util::WakerStore; | ||
| 27 | |||
| 28 | use defmt::trace; | ||
| 29 | |||
| 30 | //use crate::trace; | ||
| 31 | |||
| 32 | const RINGBUF_SIZE: usize = 512; | ||
| 33 | struct RingBuf { | ||
| 34 | buf: [u8; RINGBUF_SIZE], | ||
| 35 | start: usize, | ||
| 36 | end: usize, | ||
| 37 | empty: bool, | ||
| 38 | } | ||
| 39 | |||
| 40 | impl 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)] | ||
| 113 | enum RxState { | ||
| 114 | Idle, | ||
| 115 | Receiving, | ||
| 116 | ReceivingReady, | ||
| 117 | Stopping, | ||
| 118 | } | ||
| 119 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
| 120 | enum 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 | ||
| 133 | pub 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)] | ||
| 141 | pub 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 | |||
| 155 | fn port_bit(port: GpioPort) -> bool { | ||
| 156 | match port { | ||
| 157 | GpioPort::Port0 => false, | ||
| 158 | GpioPort::Port1 => true, | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | impl<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 | |||
| 257 | impl<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 | |||
| 264 | impl<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 | |||
| 274 | impl<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 | |||
| 280 | impl<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 | |||
| 488 | pub 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 | |||
| 495 | mod private { | ||
| 496 | use nrf52840_pac::{UARTE0, UARTE1}; | ||
| 497 | pub trait Sealed {} | ||
| 498 | |||
| 499 | impl Sealed for UARTE0 {} | ||
| 500 | impl Sealed for UARTE1 {} | ||
| 501 | } | ||
| 502 | |||
| 503 | pub 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] | ||
| 514 | unsafe fn UARTE0_UART0() { | ||
| 515 | interrupt::free(|cs| UARTE0::get_state(cs).as_mut().unwrap().on_interrupt()); | ||
| 516 | } | ||
| 517 | |||
| 518 | #[interrupt] | ||
| 519 | unsafe fn UARTE1() { | ||
| 520 | interrupt::free(|cs| UARTE1::get_state(cs).as_mut().unwrap().on_interrupt()); | ||
| 521 | } | ||
| 522 | |||
| 523 | static mut UARTE0_STATE: *mut UarteState<UARTE0> = ptr::null_mut(); | ||
| 524 | static mut UARTE1_STATE: *mut UarteState<UARTE1> = ptr::null_mut(); | ||
| 525 | |||
| 526 | impl 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 | |||
| 539 | impl 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] | ||
| 2 | name = "embassy" | ||
| 3 | version = "0.1.0" | ||
| 4 | authors = ["Dario Nieuwenhuis <[email protected]>"] | ||
| 5 | edition = "2018" | ||
| 6 | |||
| 7 | [features] | ||
| 8 | std = [] | ||
| 9 | |||
| 10 | [dependencies] | ||
| 11 | defmt = "0.1.0" | ||
| 12 | cortex-m = "0.6.3" | ||
| 13 | futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] } | ||
| 14 | pin-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 | |||
| 2 | use core::future::Future; | ||
| 3 | |||
| 4 | #[derive(defmt::Format, Copy, Clone, Debug, Eq, PartialEq)] | ||
| 5 | pub enum Error { | ||
| 6 | Failed, | ||
| 7 | AddressMisaligned, | ||
| 8 | BufferMisaligned, | ||
| 9 | |||
| 10 | _NonExhaustive, | ||
| 11 | } | ||
| 12 | |||
| 13 | pub 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")] | ||
| 2 | use core::convert::From; | ||
| 3 | #[cfg(feature = "std")] | ||
| 4 | use 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)] | ||
| 11 | pub 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 | |||
| 85 | pub type Result<T> = core::result::Result<T, Error>; | ||
| 86 | |||
| 87 | #[cfg(feature = "std")] | ||
| 88 | impl 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")] | ||
| 114 | impl std::error::Error for Error {} | ||
| 115 | |||
| 116 | /* | ||
| 117 | impl 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 @@ | |||
| 1 | mod error; | ||
| 2 | mod traits; | ||
| 3 | mod util; | ||
| 4 | |||
| 5 | pub use self::error::*; | ||
| 6 | pub use self::traits::*; | ||
| 7 | pub 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 | |||
| 2 | use core::ops::DerefMut; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use core::task::{Context, Poll}; | ||
| 5 | |||
| 6 | #[cfg(feature = "alloc")] | ||
| 7 | use alloc::boxed::Box; | ||
| 8 | |||
| 9 | #[cfg(feature = "std")] | ||
| 10 | use futures::io as std_io; | ||
| 11 | |||
| 12 | use 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. | ||
| 21 | pub 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. | ||
| 77 | pub 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 | |||
| 99 | macro_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")] | ||
| 112 | impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> { | ||
| 113 | defer_async_read!(); | ||
| 114 | } | ||
| 115 | |||
| 116 | impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T { | ||
| 117 | defer_async_read!(); | ||
| 118 | } | ||
| 119 | |||
| 120 | impl<P> AsyncBufRead for Pin<P> | ||
| 121 | where | ||
| 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 | |||
| 134 | macro_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")] | ||
| 147 | impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> { | ||
| 148 | deref_async_write!(); | ||
| 149 | } | ||
| 150 | |||
| 151 | impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T { | ||
| 152 | deref_async_write!(); | ||
| 153 | } | ||
| 154 | |||
| 155 | impl<P> AsyncWrite for Pin<P> | ||
| 156 | where | ||
| 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")] | ||
| 166 | pub struct FromStdIo<T>(T); | ||
| 167 | |||
| 168 | #[cfg(feature = "std")] | ||
| 169 | impl<T> FromStdIo<T> { | ||
| 170 | pub fn new(inner: T) -> Self { | ||
| 171 | Self(inner) | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | #[cfg(feature = "std")] | ||
| 176 | impl<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")] | ||
| 190 | impl<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 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | use futures::ready; | ||
| 5 | use pin_project::pin_project; | ||
| 6 | |||
| 7 | use 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 | /// ``` | ||
| 34 | pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W> | ||
| 35 | where | ||
| 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"] | ||
| 50 | pub struct CopyBuf<'a, R, W: ?Sized> { | ||
| 51 | #[pin] | ||
| 52 | reader: R, | ||
| 53 | writer: &'a mut W, | ||
| 54 | amt: usize, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl<R, W> Future for CopyBuf<'_, R, W> | ||
| 58 | where | ||
| 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 @@ | |||
| 1 | use core::cmp::min; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | use futures::ready; | ||
| 5 | |||
| 6 | mod read; | ||
| 7 | pub use self::read::Read; | ||
| 8 | |||
| 9 | mod read_buf; | ||
| 10 | pub use self::read_buf::ReadBuf; | ||
| 11 | |||
| 12 | mod read_byte; | ||
| 13 | pub use self::read_byte::ReadByte; | ||
| 14 | |||
| 15 | mod read_exact; | ||
| 16 | pub use self::read_exact::ReadExact; | ||
| 17 | |||
| 18 | mod read_while; | ||
| 19 | pub use self::read_while::ReadWhile; | ||
| 20 | |||
| 21 | mod read_to_end; | ||
| 22 | pub use self::read_to_end::ReadToEnd; | ||
| 23 | |||
| 24 | mod skip_while; | ||
| 25 | pub use self::skip_while::SkipWhile; | ||
| 26 | |||
| 27 | mod write; | ||
| 28 | pub use self::write::Write; | ||
| 29 | |||
| 30 | mod write_all; | ||
| 31 | pub use self::write_all::WriteAll; | ||
| 32 | |||
| 33 | mod write_byte; | ||
| 34 | pub use self::write_byte::WriteByte; | ||
| 35 | |||
| 36 | #[cfg(feature = "alloc")] | ||
| 37 | mod split; | ||
| 38 | #[cfg(feature = "alloc")] | ||
| 39 | pub use self::split::{split, ReadHalf, WriteHalf}; | ||
| 40 | |||
| 41 | mod copy_buf; | ||
| 42 | pub use self::copy_buf::{copy_buf, CopyBuf}; | ||
| 43 | |||
| 44 | use super::error::Result; | ||
| 45 | use super::traits::{AsyncBufRead, AsyncWrite}; | ||
| 46 | |||
| 47 | pub 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 | |||
| 118 | impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {} | ||
| 119 | |||
| 120 | pub 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 | |||
| 129 | pub 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 | |||
| 145 | impl<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 @@ | |||
| 1 | use super::super::error::{Result}; | ||
| 2 | use super::super::traits::AsyncBufRead; | ||
| 3 | |||
| 4 | use core::cmp::min; | ||
| 5 | |||
| 6 | use core::pin::Pin; | ||
| 7 | use futures::future::Future; | ||
| 8 | use futures::ready; | ||
| 9 | use 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"] | ||
| 14 | pub struct Read<'a, R: ?Sized> { | ||
| 15 | reader: &'a mut R, | ||
| 16 | buf: &'a mut [u8], | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {} | ||
| 20 | |||
| 21 | impl<'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 | |||
| 27 | impl<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 @@ | |||
| 1 | use super::super::error::{Result}; | ||
| 2 | use super::super::traits::AsyncBufRead; | ||
| 3 | |||
| 4 | use core::pin::Pin; | ||
| 5 | use futures::future::Future; | ||
| 6 | use futures::ready; | ||
| 7 | use futures::task::{Context, Poll}; | ||
| 8 | |||
| 9 | pub struct ReadBuf<'a, R: ?Sized> { | ||
| 10 | reader: Option<&'a mut R>, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {} | ||
| 14 | |||
| 15 | impl<'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 | |||
| 23 | impl<'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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::{Error, Result}; | ||
| 7 | use super::super::traits::AsyncBufRead; | ||
| 8 | |||
| 9 | pub struct ReadByte<'a, R: ?Sized> { | ||
| 10 | reader: &'a mut R, | ||
| 11 | } | ||
| 12 | |||
| 13 | impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {} | ||
| 14 | |||
| 15 | impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> { | ||
| 16 | pub(super) fn new(reader: &'a mut R) -> Self { | ||
| 17 | Self { reader } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | impl<'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 @@ | |||
| 1 | use super::super::error::{Error, Result}; | ||
| 2 | use super::super::traits::AsyncBufRead; | ||
| 3 | |||
| 4 | use core::cmp::min; | ||
| 5 | use core::mem; | ||
| 6 | use core::pin::Pin; | ||
| 7 | use futures::future::Future; | ||
| 8 | use futures::ready; | ||
| 9 | use 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"] | ||
| 14 | pub struct ReadExact<'a, R: ?Sized> { | ||
| 15 | reader: &'a mut R, | ||
| 16 | buf: &'a mut [u8], | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {} | ||
| 20 | |||
| 21 | impl<'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 | |||
| 27 | impl<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 @@ | |||
| 1 | use core::cmp::min; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::{Error, Result}; | ||
| 8 | use super::super::traits::AsyncBufRead; | ||
| 9 | |||
| 10 | pub struct ReadToEnd<'a, R: ?Sized> { | ||
| 11 | reader: &'a mut R, | ||
| 12 | buf: &'a mut [u8], | ||
| 13 | n: usize, | ||
| 14 | } | ||
| 15 | |||
| 16 | impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {} | ||
| 17 | |||
| 18 | impl<'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 | |||
| 24 | impl<'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 @@ | |||
| 1 | use core::cmp::min; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::{Error, Result}; | ||
| 8 | use super::super::traits::AsyncBufRead; | ||
| 9 | |||
| 10 | pub struct ReadWhile<'a, R: ?Sized, F> { | ||
| 11 | reader: &'a mut R, | ||
| 12 | buf: &'a mut [u8], | ||
| 13 | n: usize, | ||
| 14 | f: F, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {} | ||
| 18 | |||
| 19 | impl<'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 | |||
| 30 | impl<'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 @@ | |||
| 1 | use core::iter::Iterator; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::{Error, Result}; | ||
| 8 | use super::super::traits::AsyncBufRead; | ||
| 9 | |||
| 10 | pub struct SkipWhile<'a, R: ?Sized, F> { | ||
| 11 | reader: &'a mut R, | ||
| 12 | f: F, | ||
| 13 | } | ||
| 14 | |||
| 15 | impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {} | ||
| 16 | |||
| 17 | impl<'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 | |||
| 23 | impl<'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 @@ | |||
| 1 | use alloc::rc::Rc; | ||
| 2 | use core::cell::UnsafeCell; | ||
| 3 | use core::pin::Pin; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use super::super::traits::{AsyncBufRead, AsyncWrite}; | ||
| 8 | |||
| 9 | /// The readable half of an object returned from `AsyncBufRead::split`. | ||
| 10 | #[derive(Debug)] | ||
| 11 | pub struct ReadHalf<T> { | ||
| 12 | handle: Rc<UnsafeCell<T>>, | ||
| 13 | } | ||
| 14 | |||
| 15 | /// The writable half of an object returned from `AsyncBufRead::split`. | ||
| 16 | #[derive(Debug)] | ||
| 17 | pub struct WriteHalf<T> { | ||
| 18 | handle: Rc<UnsafeCell<T>>, | ||
| 19 | } | ||
| 20 | |||
| 21 | impl<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 | |||
| 31 | impl<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 | |||
| 37 | pub 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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use 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"] | ||
| 12 | pub struct Write<'a, W: ?Sized> { | ||
| 13 | writer: &'a mut W, | ||
| 14 | buf: &'a [u8], | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {} | ||
| 18 | |||
| 19 | impl<'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 | |||
| 25 | impl<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 @@ | |||
| 1 | use core::mem; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use futures::future::Future; | ||
| 4 | use futures::ready; | ||
| 5 | use futures::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use super::super::error::Result; | ||
| 8 | use 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"] | ||
| 13 | pub struct WriteAll<'a, W: ?Sized> { | ||
| 14 | writer: &'a mut W, | ||
| 15 | buf: &'a [u8], | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {} | ||
| 19 | |||
| 20 | impl<'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 | |||
| 26 | impl<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 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use futures::future::Future; | ||
| 3 | use futures::ready; | ||
| 4 | use futures::task::{Context, Poll}; | ||
| 5 | |||
| 6 | use super::super::error::Result; | ||
| 7 | use 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"] | ||
| 12 | pub struct WriteByte<'a, W: ?Sized> { | ||
| 13 | writer: &'a mut W, | ||
| 14 | byte: u8, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {} | ||
| 18 | |||
| 19 | impl<'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 | |||
| 25 | impl<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 | |||
| 6 | pub mod flash; | ||
| 7 | pub mod util; | ||
| 8 | pub 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 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 3 | pub struct DropBomb { | ||
| 4 | _private: (), | ||
| 5 | } | ||
| 6 | |||
| 7 | impl DropBomb { | ||
| 8 | pub fn new() -> Self { | ||
| 9 | Self { _private: () } | ||
| 10 | } | ||
| 11 | |||
| 12 | pub fn defuse(self) { | ||
| 13 | mem::forget(self) | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | impl 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 | |||
| 3 | macro_rules! depanic { | ||
| 4 | ($( $i:expr ),*) => { | ||
| 5 | { | ||
| 6 | defmt::error!($( $i ),*); | ||
| 7 | panic!(); | ||
| 8 | } | ||
| 9 | } | ||
| 10 | } | ||
| 11 | |||
| 12 | macro_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 | |||
| 3 | mod macros; | ||
| 4 | |||
| 5 | mod signal; | ||
| 6 | pub use signal::*; | ||
| 7 | mod portal; | ||
| 8 | pub use portal::*; | ||
| 9 | mod waker_store; | ||
| 10 | pub use waker_store::*; | ||
| 11 | mod drop_bomb; | ||
| 12 | pub use drop_bomb::*; | ||
| 13 | |||
| 14 | use defmt::{warn, error}; | ||
| 15 | |||
| 16 | pub 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 | |||
| 26 | impl<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 | |||
| 49 | impl<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 @@ | |||
| 1 | use core::cell::UnsafeCell; | ||
| 2 | use core::future::Future; | ||
| 3 | use core::mem; | ||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | |||
| 6 | use crate::util::*; | ||
| 7 | |||
| 8 | /// Utility to call a closure across tasks. | ||
| 9 | pub struct Portal<T> { | ||
| 10 | state: UnsafeCell<State<T>>, | ||
| 11 | } | ||
| 12 | |||
| 13 | enum State<T> { | ||
| 14 | None, | ||
| 15 | Running, | ||
| 16 | Waiting(*mut dyn FnMut(T)), | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<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 @@ | |||
| 1 | use core::cell::UnsafeCell; | ||
| 2 | use core::future::Future; | ||
| 3 | use core::mem; | ||
| 4 | use core::pin::Pin; | ||
| 5 | use core::task::{Context, Poll, Waker}; | ||
| 6 | |||
| 7 | pub struct Signal<T> { | ||
| 8 | state: UnsafeCell<State<T>>, | ||
| 9 | } | ||
| 10 | |||
| 11 | enum State<T> { | ||
| 12 | None, | ||
| 13 | Waiting(Waker), | ||
| 14 | Signaled(T), | ||
| 15 | } | ||
| 16 | |||
| 17 | unsafe impl<T: Send> Send for Signal<T> {} | ||
| 18 | unsafe impl<T: Send> Sync for Signal<T> {} | ||
| 19 | |||
| 20 | impl<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 | |||
| 44 | struct WaitFuture<'a, T> { | ||
| 45 | signal: &'a Signal<T>, | ||
| 46 | } | ||
| 47 | |||
| 48 | impl<'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 @@ | |||
| 1 | use core::task::Waker; | ||
| 2 | |||
| 3 | pub struct WakerStore { | ||
| 4 | waker: Option<Waker>, | ||
| 5 | } | ||
| 6 | |||
| 7 | impl 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] | ||
| 2 | authors = ["Dario Nieuwenhuis <[email protected]>"] | ||
| 3 | edition = "2018" | ||
| 4 | name = "embassy-examples" | ||
| 5 | version = "0.1.0" | ||
| 6 | |||
| 7 | [features] | ||
| 8 | default = [ | ||
| 9 | "defmt-default", | ||
| 10 | ] | ||
| 11 | defmt-default = [] | ||
| 12 | defmt-trace = [] | ||
| 13 | defmt-debug = [] | ||
| 14 | defmt-info = [] | ||
| 15 | defmt-warn = [] | ||
| 16 | defmt-error = [] | ||
| 17 | |||
| 18 | |||
| 19 | [dependencies] | ||
| 20 | cortex-m = { version = "0.6.3" } | ||
| 21 | cortex-m-rt = "0.6.12" | ||
| 22 | defmt = "0.1.0" | ||
| 23 | embedded-hal = { version = "0.2.4" } | ||
| 24 | defmt-rtt = "0.1.0" | ||
| 25 | panic-probe = "0.1.0" | ||
| 26 | nrf52840-hal = { version = "0.11.0" } | ||
| 27 | embassy = { version = "0.1.0", path = "../embassy" } | ||
| 28 | embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "nrf52840"] } | ||
| 29 | static-executor = { version = "0.1.0", features=["defmt"]} | ||
| 30 | static-executor-cortex-m = { version = "0.1.0" } | ||
| 31 | futures = { 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 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn 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 @@ | |||
| 1 | MEMORY | ||
| 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"] | ||
| 6 | mod example_common; | ||
| 7 | use example_common::*; | ||
| 8 | |||
| 9 | use cortex_m_rt::entry; | ||
| 10 | use embassy::flash::Flash; | ||
| 11 | use embassy_nrf::qspi; | ||
| 12 | use nrf52840_hal::gpio; | ||
| 13 | |||
| 14 | const PAGE_SIZE: usize = 4096; | ||
| 15 | |||
| 16 | // Workaround for alignment requirements. | ||
| 17 | // Nicer API will probably come in the future. | ||
| 18 | #[repr(C, align(4))] | ||
| 19 | struct AlignedBuf([u8; 4096]); | ||
| 20 | |||
| 21 | #[static_executor::task] | ||
| 22 | async 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] | ||
| 116 | fn 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"] | ||
| 6 | mod example_common; | ||
| 7 | use example_common::*; | ||
| 8 | |||
| 9 | use cortex_m_rt::entry; | ||
| 10 | use embassy::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; | ||
| 11 | use embassy_nrf::uarte; | ||
| 12 | use futures::pin_mut; | ||
| 13 | use nrf52840_hal::gpio; | ||
| 14 | |||
| 15 | #[static_executor::task] | ||
| 16 | async 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] | ||
| 65 | fn 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 | |||
| 3 | use defmt_rtt as _; // global logger | ||
| 4 | use nrf52840_hal as _; | ||
| 5 | use panic_probe as _; | ||
| 6 | use static_executor_cortex_m as _; | ||
| 7 | |||
| 8 | pub use defmt::{info, intern}; | ||
| 9 | |||
| 10 | use core::sync::atomic::{AtomicUsize, Ordering}; | ||
| 11 | |||
| 12 | #[defmt::timestamp] | ||
| 13 | fn 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 | |||
| 21 | macro_rules! depanic { | ||
| 22 | ($( $i:expr ),*) => { | ||
| 23 | { | ||
| 24 | defmt::error!($( $i ),*); | ||
| 25 | panic!(); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | pub 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 | |||
| 38 | impl<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 | |||
| 54 | impl<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 | } | ||
