From 251e007d59d275ffa9d70d6e525caee17db37852 Mon Sep 17 00:00:00 2001 From: Guillaume Endignoux Date: Fri, 10 Jul 2020 10:05:35 +0200 Subject: [PATCH] Import https://github.com/tock/libtock-rs at commit 828c19d into third_party/libtock-drivers/. --- third_party/libtock-drivers/Cargo.toml | 67 ++ third_party/libtock-drivers/LICENSE-APACHE | 202 ++++++ third_party/libtock-drivers/LICENSE-MIT | 25 + third_party/libtock-drivers/src/buttons.rs | 167 +++++ third_party/libtock-drivers/src/console.rs | 82 +++ third_party/libtock-drivers/src/leds.rs | 161 +++++ third_party/libtock-drivers/src/lib.rs | 25 + third_party/libtock-drivers/src/result.rs | 69 ++ third_party/libtock-drivers/src/rng.rs | 42 ++ third_party/libtock-drivers/src/timer.rs | 744 +++++++++++++++++++++ 10 files changed, 1584 insertions(+) create mode 100644 third_party/libtock-drivers/Cargo.toml create mode 100644 third_party/libtock-drivers/LICENSE-APACHE create mode 100644 third_party/libtock-drivers/LICENSE-MIT create mode 100644 third_party/libtock-drivers/src/buttons.rs create mode 100644 third_party/libtock-drivers/src/console.rs create mode 100644 third_party/libtock-drivers/src/leds.rs create mode 100644 third_party/libtock-drivers/src/lib.rs create mode 100644 third_party/libtock-drivers/src/result.rs create mode 100644 third_party/libtock-drivers/src/rng.rs create mode 100644 third_party/libtock-drivers/src/timer.rs diff --git a/third_party/libtock-drivers/Cargo.toml b/third_party/libtock-drivers/Cargo.toml new file mode 100644 index 0000000..f5cb932 --- /dev/null +++ b/third_party/libtock-drivers/Cargo.toml @@ -0,0 +1,67 @@ +[package] +name = "libtock" +version = "0.2.0" +authors = ["Tock Project Developers "] +license = "MIT/Apache-2.0" +edition = "2018" + +[features] +alloc = ["libtock_core/alloc"] +custom_panic_handler = ["libtock_core/custom_panic_handler"] +custom_alloc_error_handler = ["libtock_core/custom_alloc_error_handler"] +__internal_disable_gpio_in_integration_test = [] + +[dependencies] +libtock_core = { path = "core" } +libtock_codegen = { path = "codegen" } +futures = { version = "0.3.1", default-features = false, features = ["unstable", "cfg-target-has-atomic"] } + +[dev-dependencies] +corepack = { version = "0.4.0", default-features = false, features = ["alloc"] } +# We pin the serde version because newer serde versions may not be compatible +# with the nightly toolchain used by libtock-rs. +serde = { version = "=1.0.84", default-features = false, features = ["derive"] } + +[[example]] +name = "alloc_error" +path = "examples-features/alloc_error.rs" +required-features = ["alloc", "custom_alloc_error_handler"] + +[[example]] +name = "ble_scanning" +path = "examples-features/ble_scanning.rs" +required-features = ["alloc"] + +[[example]] +name = "libtock_test" +path = "examples-features/libtock_test.rs" +required-features = ["alloc"] + +[[example]] +name = "panic" +path = "examples-features/panic.rs" +required-features = ["custom_panic_handler"] + +[[example]] +name = "simple_ble" +path = "examples-features/simple_ble.rs" +required-features = ["alloc"] + +[profile.dev] +panic = "abort" +lto = true +debug = true + +[profile.release] +panic = "abort" +lto = true +debug = true + +[workspace] +exclude = [ "tock" ] +members = [ + "codegen", + "core", + "test_runner", + "tools/print_sizes", +] diff --git a/third_party/libtock-drivers/LICENSE-APACHE b/third_party/libtock-drivers/LICENSE-APACHE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/libtock-drivers/LICENSE-APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/libtock-drivers/LICENSE-MIT b/third_party/libtock-drivers/LICENSE-MIT new file mode 100644 index 0000000..80d95b9 --- /dev/null +++ b/third_party/libtock-drivers/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 The Tock Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/libtock-drivers/src/buttons.rs b/third_party/libtock-drivers/src/buttons.rs new file mode 100644 index 0000000..aac5cb2 --- /dev/null +++ b/third_party/libtock-drivers/src/buttons.rs @@ -0,0 +1,167 @@ +use crate::callback::CallbackSubscription; +use crate::callback::Consumer; +use crate::result::OtherError; +use crate::result::OutOfRangeError; +use crate::result::TockResult; +use crate::syscalls; +use core::marker::PhantomData; + +const DRIVER_NUMBER: usize = 0x00003; + +mod command_nr { + pub const COUNT: usize = 0; + pub const ENABLE_INTERRUPT: usize = 1; + pub const DISABLE_INTERRUPT: usize = 2; + pub const READ: usize = 3; +} + +mod subscribe_nr { + pub const SUBSCRIBE_CALLBACK: usize = 0; +} + +#[non_exhaustive] +pub struct ButtonsDriverFactory; + +impl ButtonsDriverFactory { + pub fn init_driver(&mut self) -> TockResult { + let buttons_driver = ButtonsDriver { + num_buttons: syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, + lifetime: PhantomData, + }; + Ok(buttons_driver) + } +} + +pub struct ButtonsDriver<'a> { + num_buttons: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> ButtonsDriver<'a> { + pub fn num_buttons(&self) -> usize { + self.num_buttons + } + + /// Returns the button at 0-based index `button_num` + pub fn get(&self, button_num: usize) -> Result { + if button_num < self.num_buttons { + Ok(Button { + button_num, + lifetime: PhantomData, + }) + } else { + Err(OutOfRangeError) + } + } + + pub fn buttons(&self) -> Buttons { + Buttons { + num_buttons: self.num_buttons, + curr_button: 0, + lifetime: PhantomData, + } + } + + pub fn subscribe( + &self, + callback: &'a mut CB, + ) -> TockResult { + syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + callback, + ) + .map_err(Into::into) + } +} + +struct ButtonsEventConsumer; + +impl Consumer for ButtonsEventConsumer { + fn consume(callback: &mut CB, button_num: usize, button_state: usize, _: usize) { + let button_state = match button_state { + 0 => ButtonState::Released, + 1 => ButtonState::Pressed, + _ => return, + }; + callback(button_num, button_state); + } +} + +pub struct Buttons<'a> { + num_buttons: usize, + curr_button: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Iterator for Buttons<'a> { + type Item = Button<'a>; + + fn next(&mut self) -> Option { + if self.curr_button < self.num_buttons { + let item = Button { + button_num: self.curr_button, + lifetime: PhantomData, + }; + self.curr_button += 1; + Some(item) + } else { + None + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ButtonState { + Pressed, + Released, +} + +impl From for bool { + fn from(button_state: ButtonState) -> Self { + match button_state { + ButtonState::Released => false, + ButtonState::Pressed => true, + } + } +} + +pub struct Button<'a> { + button_num: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Button<'a> { + pub fn button_num(&self) -> usize { + self.button_num + } + + pub fn read(&self) -> TockResult { + let button_state = syscalls::command(DRIVER_NUMBER, command_nr::READ, self.button_num, 0)?; + match button_state { + 0 => Ok(ButtonState::Released), + 1 => Ok(ButtonState::Pressed), + _ => Err(OtherError::ButtonsDriverInvalidState.into()), + } + } + + pub fn enable_interrupt(&self) -> TockResult<()> { + syscalls::command( + DRIVER_NUMBER, + command_nr::ENABLE_INTERRUPT, + self.button_num, + 0, + )?; + Ok(()) + } + + pub fn disable_interrupt(&self) -> TockResult<()> { + syscalls::command( + DRIVER_NUMBER, + command_nr::DISABLE_INTERRUPT, + self.button_num, + 0, + )?; + Ok(()) + } +} diff --git a/third_party/libtock-drivers/src/console.rs b/third_party/libtock-drivers/src/console.rs new file mode 100644 index 0000000..a94fc13 --- /dev/null +++ b/third_party/libtock-drivers/src/console.rs @@ -0,0 +1,82 @@ +use crate::callback::Identity0Consumer; +use crate::executor; +use crate::futures; +use crate::result::TockResult; +use crate::syscalls; +use core::cell::Cell; +use core::fmt; +use core::mem; + +const DRIVER_NUMBER: usize = 1; + +mod command_nr { + pub const WRITE: usize = 1; +} + +mod subscribe_nr { + pub const SET_ALARM: usize = 1; +} + +mod allow_nr { + pub const SHARE_BUFFER: usize = 1; +} + +#[non_exhaustive] +pub struct ConsoleDriver; + +impl ConsoleDriver { + pub fn create_console(self) -> Console { + Console { + allow_buffer: [0; 64], + } + } +} + +pub struct Console { + allow_buffer: [u8; 64], +} + +impl Console { + pub fn write>(&mut self, text: S) -> TockResult<()> { + let mut not_written_yet = text.as_ref(); + while !not_written_yet.is_empty() { + let num_bytes_to_print = self.allow_buffer.len().min(not_written_yet.len()); + self.allow_buffer[..num_bytes_to_print] + .copy_from_slice(¬_written_yet[..num_bytes_to_print]); + self.flush(num_bytes_to_print)?; + not_written_yet = ¬_written_yet[num_bytes_to_print..]; + } + Ok(()) + } + + fn flush(&mut self, num_bytes_to_print: usize) -> TockResult<()> { + let shared_memory = syscalls::allow( + DRIVER_NUMBER, + allow_nr::SHARE_BUFFER, + &mut self.allow_buffer[..num_bytes_to_print], + )?; + + let is_written = Cell::new(false); + let mut is_written_alarm = || is_written.set(true); + let subscription = syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::SET_ALARM, + &mut is_written_alarm, + )?; + + syscalls::command(DRIVER_NUMBER, command_nr::WRITE, num_bytes_to_print, 0)?; + + unsafe { executor::block_on(futures::wait_until(|| is_written.get())) }; + + mem::drop(subscription); + mem::drop(shared_memory); + + Ok(()) + } +} + +impl fmt::Write for Console { + fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> { + self.write(string).map_err(|_| fmt::Error) + } +} diff --git a/third_party/libtock-drivers/src/leds.rs b/third_party/libtock-drivers/src/leds.rs new file mode 100644 index 0000000..c505cf9 --- /dev/null +++ b/third_party/libtock-drivers/src/leds.rs @@ -0,0 +1,161 @@ +use crate::result::OutOfRangeError; +use crate::result::TockResult; +use crate::syscalls::command; +use core::marker::PhantomData; + +const DRIVER_NUMBER: usize = 0x00002; + +mod command_nr { + pub const COUNT: usize = 0; + pub const ON: usize = 1; + pub const OFF: usize = 2; + pub const TOGGLE: usize = 3; +} + +#[non_exhaustive] +pub struct LedsDriverFactory; + +impl LedsDriverFactory { + pub fn init_driver(&mut self) -> TockResult { + let driver = LedsDriver { + num_leds: command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?, + lifetime: PhantomData, + }; + Ok(driver) + } +} + +pub struct LedsDriver<'a> { + num_leds: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> LedsDriver<'a> { + pub fn num_leds(&self) -> usize { + self.num_leds + } + + pub fn leds(&self) -> Leds { + Leds { + num_leds: self.num_leds, + curr_led: 0, + lifetime: PhantomData, + } + } + + /// Returns the led at 0-based index `led_num` + pub fn get(&self, led_num: usize) -> Result { + if led_num < self.num_leds { + Ok(Led { + led_num, + lifetime: PhantomData, + }) + } else { + Err(OutOfRangeError) + } + } +} + +pub struct Leds<'a> { + num_leds: usize, + curr_led: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Iterator for Leds<'a> { + type Item = Led<'a>; + + fn next(&mut self) -> Option { + if self.curr_led < self.num_leds { + let item = Led { + led_num: self.curr_led, + lifetime: PhantomData, + }; + self.curr_led += 1; + Some(item) + } else { + None + } + } +} + +pub struct Led<'a> { + led_num: usize, + lifetime: PhantomData<&'a ()>, +} + +impl<'a> Led<'a> { + pub fn led_num(&self) -> usize { + self.led_num + } + + pub fn set(&self, state: impl Into) -> TockResult<()> { + match state.into() { + LedState::On => self.on(), + LedState::Off => self.off(), + } + } + + pub fn on(&self) -> TockResult<()> { + command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?; + Ok(()) + } + + pub fn off(&self) -> TockResult<()> { + command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?; + Ok(()) + } + + pub fn toggle(&self) -> TockResult<()> { + command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?; + Ok(()) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum LedState { + On, + Off, +} + +impl From for LedState { + fn from(from_value: bool) -> Self { + if from_value { + LedState::On + } else { + LedState::Off + } + } +} + +#[cfg(test)] +mod test { + use super::command_nr; + use super::DRIVER_NUMBER; + use crate::result::TockResult; + use crate::syscalls; + use crate::syscalls::raw::Event; + + #[test] + pub fn single_led_can_be_enabled() { + let events = syscalls::raw::run_recording_events::, _>(|next_return| { + let mut drivers = unsafe { crate::drivers::retrieve_drivers_unsafe() }; + + next_return.set(1); + + let leds_driver = drivers.leds.init_driver()?; + next_return.set(0); + + let led = leds_driver.get(0)?; + led.on()?; + Ok(()) + }); + assert_eq!( + events, + vec![ + Event::Command(DRIVER_NUMBER, command_nr::COUNT, 0, 0), + Event::Command(DRIVER_NUMBER, command_nr::ON, 0, 0), + ] + ); + } +} diff --git a/third_party/libtock-drivers/src/lib.rs b/third_party/libtock-drivers/src/lib.rs new file mode 100644 index 0000000..eb13b94 --- /dev/null +++ b/third_party/libtock-drivers/src/lib.rs @@ -0,0 +1,25 @@ +#![cfg_attr(not(test), no_std)] + +pub mod adc; +pub mod ble_composer; +pub mod ble_parser; +pub mod buttons; +pub mod console; +pub mod debug; +pub mod drivers; +pub mod electronics; +pub mod executor; +pub mod futures; +pub mod gpio; +pub mod hmac; +pub mod leds; +pub mod result; +pub mod rng; +pub mod sensors; +pub mod simple_ble; +pub mod temperature; +pub mod timer; + +pub use drivers::retrieve_drivers; +pub use libtock_codegen::main; +pub use libtock_core::*; diff --git a/third_party/libtock-drivers/src/result.rs b/third_party/libtock-drivers/src/result.rs new file mode 100644 index 0000000..84af6bf --- /dev/null +++ b/third_party/libtock-drivers/src/result.rs @@ -0,0 +1,69 @@ +use core::fmt; + +pub use libtock_core::result::*; + +pub type TockResult = Result; + +#[derive(Copy, Clone)] +pub enum TockError { + Subscribe(SubscribeError), + Command(CommandError), + Allow(AllowError), + Format, + Other(OtherError), +} + +#[cfg(not(any(target_arch = "arm", target_arch = "riscv32")))] +impl core::fmt::Debug for TockError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + writeln!(f, "impl Debug only for test builds") + } +} + +impl From for TockError { + fn from(subscribe_error: SubscribeError) -> Self { + TockError::Subscribe(subscribe_error) + } +} + +impl From for TockError { + fn from(command_error: CommandError) -> Self { + TockError::Command(command_error) + } +} + +impl From for TockError { + fn from(allow_error: AllowError) -> Self { + TockError::Allow(allow_error) + } +} + +impl From for TockError { + fn from(fmt::Error: fmt::Error) -> Self { + TockError::Format + } +} + +#[derive(Copy, Clone)] +pub enum OtherError { + ButtonsDriverInvalidState, + GpioDriverInvalidState, + TimerDriverDurationOutOfRange, + TimerDriverErroneousClockFrequency, + DriversAlreadyTaken, + OutOfRange, +} + +impl From for TockError { + fn from(other: OtherError) -> Self { + TockError::Other(other) + } +} + +pub struct OutOfRangeError; + +impl From for TockError { + fn from(_: OutOfRangeError) -> Self { + TockError::Other(OtherError::OutOfRange) + } +} diff --git a/third_party/libtock-drivers/src/rng.rs b/third_party/libtock-drivers/src/rng.rs new file mode 100644 index 0000000..039fa77 --- /dev/null +++ b/third_party/libtock-drivers/src/rng.rs @@ -0,0 +1,42 @@ +use crate::callback::Identity0Consumer; +use crate::futures; +use crate::result::TockResult; +use crate::syscalls; +use core::cell::Cell; +use core::mem; + +const DRIVER_NUMBER: usize = 0x40001; + +mod command_nr { + pub const REQUEST_RNG: usize = 1; +} + +mod subscribe_nr { + pub const BUFFER_FILLED: usize = 0; +} + +mod allow_nr { + pub const SHARE_BUFFER: usize = 0; +} + +#[non_exhaustive] +pub struct RngDriver; + +impl RngDriver { + pub async fn fill_buffer(&mut self, buf: &mut [u8]) -> TockResult<()> { + let buf_len = buf.len(); + let shared_memory = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf)?; + let is_filled = Cell::new(false); + let mut is_filled_alarm = || is_filled.set(true); + let subscription = syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::BUFFER_FILLED, + &mut is_filled_alarm, + )?; + syscalls::command(DRIVER_NUMBER, command_nr::REQUEST_RNG, buf_len, 0)?; + futures::wait_until(|| is_filled.get()).await; + mem::drop(subscription); + mem::drop(shared_memory); + Ok(()) + } +} diff --git a/third_party/libtock-drivers/src/timer.rs b/third_party/libtock-drivers/src/timer.rs new file mode 100644 index 0000000..946c6e5 --- /dev/null +++ b/third_party/libtock-drivers/src/timer.rs @@ -0,0 +1,744 @@ +//! Async timer driver. Can be used for (non-busy) sleeping. + +use crate::callback::CallbackSubscription; +use crate::callback::Consumer; +use crate::futures; +use crate::result::OtherError; +use crate::result::TockError; +use crate::result::TockResult; +use crate::result::EALREADY; +use crate::syscalls; +use core::cell::Cell; +use core::isize; +use core::marker::PhantomData; +use core::ops::{Add, AddAssign, Sub}; + +const DRIVER_NUMBER: usize = 0x00000; + +mod command_nr { + pub const IS_DRIVER_AVAILABLE: usize = 0; + pub const GET_CLOCK_FREQUENCY: usize = 1; + pub const GET_CLOCK_VALUE: usize = 2; + pub const STOP_ALARM: usize = 3; + pub const SET_ALARM: usize = 4; +} + +mod subscribe_nr { + pub const SUBSCRIBE_CALLBACK: usize = 0; +} + +pub struct WithCallback<'a, CB> { + callback: CB, + clock_frequency: ClockFrequency, + phantom: PhantomData<&'a mut ()>, +} + +struct TimerEventConsumer; + +impl Consumer> for TimerEventConsumer { + fn consume(data: &mut WithCallback, clock_value: usize, alarm_id: usize, _: usize) { + (data.callback)( + ClockValue { + num_ticks: clock_value as isize, + clock_frequency: data.clock_frequency, + }, + Alarm { alarm_id }, + ); + } +} + +impl<'a, CB: FnMut(ClockValue, Alarm)> WithCallback<'a, CB> { + pub fn init(&'a mut self) -> TockResult> { + let num_notifications = + syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?; + + let clock_frequency = + syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)?; + + if clock_frequency == 0 { + return Err(OtherError::TimerDriverErroneousClockFrequency.into()); + } + + let clock_frequency = ClockFrequency { + hz: clock_frequency, + }; + + let subscription = syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + self, + )?; + + Ok(Timer { + num_notifications, + clock_frequency, + subscription, + }) + } +} + +pub struct Timer<'a> { + num_notifications: usize, + clock_frequency: ClockFrequency, + #[allow(dead_code)] // Used in drop + subscription: CallbackSubscription<'a>, +} + +impl<'a> Timer<'a> { + pub fn num_notifications(&self) -> usize { + self.num_notifications + } + + pub fn clock_frequency(&self) -> ClockFrequency { + self.clock_frequency + } + + pub fn get_current_clock(&self) -> TockResult { + Ok(ClockValue { + num_ticks: syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0)? + as isize, + clock_frequency: self.clock_frequency, + }) + } + + pub fn stop_alarm(&mut self, alarm: Alarm) -> TockResult<()> { + syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, alarm.alarm_id, 0)?; + Ok(()) + } + + pub fn set_alarm(&mut self, duration: Duration) -> TockResult { + let now = self.get_current_clock()?; + let freq = self.clock_frequency.hz(); + let duration_ms = duration.ms() as usize; + let ticks = match duration_ms.checked_mul(freq) { + Some(x) => x / 1000, + None => { + // Divide the largest of the two operands by 1000, to improve precision of the + // result. + if duration_ms > freq { + match (duration_ms / 1000).checked_mul(freq) { + Some(y) => y, + None => return Err(OtherError::TimerDriverDurationOutOfRange.into()), + } + } else { + match (freq / 1000).checked_mul(duration_ms) { + Some(y) => y, + None => return Err(OtherError::TimerDriverDurationOutOfRange.into()), + } + } + } + }; + let alarm_instant = now.num_ticks() as usize + ticks; + + let alarm_id = syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, alarm_instant, 0)?; + + Ok(Alarm { alarm_id }) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ClockFrequency { + hz: usize, +} + +impl ClockFrequency { + pub fn hz(self) -> usize { + self.hz + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ClockValue { + num_ticks: isize, + clock_frequency: ClockFrequency, +} + +impl ClockValue { + pub fn num_ticks(self) -> isize { + self.num_ticks + } + + pub fn ms(self) -> isize { + if self.num_ticks.abs() < isize::MAX / 1000 { + (1000 * self.num_ticks) / self.clock_frequency.hz() as isize + } else { + 1000 * (self.num_ticks / self.clock_frequency.hz() as isize) + } + } + + pub fn ms_f64(self) -> f64 { + 1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() as f64) + } +} + +pub struct Alarm { + alarm_id: usize, +} + +impl Alarm { + pub fn alarm_id(&self) -> usize { + self.alarm_id + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Duration { + ms: T, +} + +impl Duration { + pub const fn from_ms(ms: T) -> Duration { + Duration { ms } + } +} + +impl Duration +where + T: Copy, +{ + pub fn ms(&self) -> T { + self.ms + } +} + +impl Sub for Duration +where + T: Sub, +{ + type Output = Duration; + + fn sub(self, other: Duration) -> Duration { + Duration { + ms: self.ms - other.ms, + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct Timestamp { + ms: T, +} + +impl Timestamp { + pub const fn from_ms(ms: T) -> Timestamp { + Timestamp { ms } + } +} + +impl Timestamp +where + T: Copy, +{ + pub fn ms(&self) -> T { + self.ms + } +} + +impl Timestamp { + pub fn from_clock_value(value: ClockValue) -> Timestamp { + Timestamp { ms: value.ms() } + } +} + +impl Timestamp { + pub fn from_clock_value(value: ClockValue) -> Timestamp { + Timestamp { ms: value.ms_f64() } + } +} + +impl Sub for Timestamp +where + T: Sub, +{ + type Output = Duration; + + fn sub(self, other: Timestamp) -> Duration { + Duration::from_ms(self.ms - other.ms) + } +} + +impl Add> for Timestamp +where + T: Copy + Add, +{ + type Output = Timestamp; + + fn add(self, duration: Duration) -> Timestamp { + Timestamp { + ms: self.ms + duration.ms(), + } + } +} + +impl AddAssign> for Timestamp +where + T: Copy + AddAssign, +{ + fn add_assign(&mut self, duration: Duration) { + self.ms += duration.ms(); + } +} + +#[derive(Copy, Clone, Default, PartialEq, Eq)] +pub(crate) struct ActiveTimer { + instant: u32, + set_at: u32, +} + +/// Context for the time driver. +/// You can create a context as follows: +/// ```no_run +/// # use libtock::result::TockResult; +/// # async fn doc() -> TockResult<()> { +/// let mut drivers = libtock::retrieve_drivers()?; +/// let mut timer_context = drivers.timer; +/// # Ok(()) +/// # } +/// ``` +#[non_exhaustive] +pub struct DriverContext { + pub(crate) active_timer: Cell>, +} + +impl DriverContext { + /// Create a driver timer from a context. + pub fn create_timer_driver(&mut self) -> TimerDriver { + TimerDriver { + callback: Callback, + context: self, + } + } + + pub fn with_callback(&mut self, callback: CB) -> WithCallback { + WithCallback { + callback, + clock_frequency: ClockFrequency { hz: 0 }, + phantom: PhantomData, + } + } +} + +/// Timer driver instance. You can create a TimerDriver from a DriverContext as follows: +/// ```no_run +/// # use libtock::result::TockResult; +/// # async fn doc() -> TockResult<()> { +/// # let mut drivers = libtock::retrieve_drivers()?; +/// # let mut timer_context = drivers.timer; +/// let mut timer_driver = timer_context.create_timer_driver(); +/// let timer_driver = timer_driver.activate()?; +/// # Ok(()) +/// # } +/// ``` +pub struct TimerDriver<'a> { + callback: Callback, + context: &'a DriverContext, +} + +struct Callback; + +struct ParallelTimerConsumer; + +impl<'a> Consumer for ParallelTimerConsumer { + fn consume(_: &mut Callback, _: usize, _: usize, _: usize) {} +} + +/// Activated time driver. Updates current time in the context and manages +/// active alarms. +/// Example usage (sleep for 1 second): +/// ```no_run +/// # use libtock::result::TockResult; +/// # use libtock::timer::Duration; +/// # async fn doc() -> TockResult<()> { +/// # let mut drivers = libtock::retrieve_drivers()?; +/// # let mut timer_driver = drivers.timer.create_timer_driver(); +/// let timer_driver = timer_driver.activate()?; +/// timer_driver.sleep(Duration::from_ms(1000)).await?; +/// # Ok(()) +/// # } +/// ``` +pub struct ParallelSleepDriver<'a> { + _callback_subscription: CallbackSubscription<'a>, + context: &'a DriverContext, +} + +impl<'a> TimerDriver<'a> { + /// Activate the timer driver, will return a ParallelSleepDriver which + /// can used to sleep. + pub fn activate(&'a mut self) -> TockResult> { + let subscription = syscalls::subscribe::( + DRIVER_NUMBER, + subscribe_nr::SUBSCRIBE_CALLBACK, + &mut self.callback, + )?; + let driver = ParallelSleepDriver { + _callback_subscription: subscription, + context: &self.context, + }; + Ok(driver) + } +} + +impl<'a> ParallelSleepDriver<'a> { + /// Sleep for the given duration + pub async fn sleep(&self, duration: Duration) -> TockResult<()> { + let now = get_current_ticks()?; + let freq = get_clock_frequency()?; + let alarm_instant = Self::compute_alarm_instant(duration.ms, now, freq)?; + let this_alarm = ActiveTimer { + instant: alarm_instant as u32, + set_at: now as u32, + }; + + let suspended_timer: Cell> = Cell::new(None); + + futures::wait_until(|| { + self.activate_current_timer(this_alarm, &suspended_timer) + .unwrap_or(false) + }) + .await; + + Ok(()) + } + + fn activate_timer(&self, timer: ActiveTimer) -> TockResult<()> { + set_alarm_at(timer.instant as usize)?; + let now = get_current_ticks()?; + if !is_over(timer, now as u32) { + self.context.active_timer.set(Some(timer)); + } else { + self.wakeup_soon()?; + } + Ok(()) + } + + fn wakeup_soon(&self) -> TockResult<()> { + self.context.active_timer.set(None); + + for i in 0.. { + let now = get_current_ticks()?; + + let next_timer = ActiveTimer { + instant: now as u32 + i, + set_at: now as u32, + }; + set_alarm_at(next_timer.instant as usize)?; + let now = get_current_ticks()?; + if !is_over(next_timer, now as u32) { + break; + } else { + stop_alarm_at(next_timer.instant as usize)?; + } + } + Ok(()) + } + + fn compute_alarm_instant( + duration_ms: usize, + num_ticks: usize, + freq: usize, + ) -> TockResult { + let ticks = match duration_ms.checked_mul(freq) { + Some(x) => x / 1000, + None => { + // Divide the largest of the two operands by 1000, to improve precision of the + // result. + if duration_ms > freq { + match (duration_ms / 1000).checked_mul(freq) { + Some(y) => y, + None => { + return Err(TockError::Other(OtherError::TimerDriverDurationOutOfRange)) + } + } + } else { + match (freq / 1000).checked_mul(duration_ms) { + Some(y) => y, + None => { + return Err(TockError::Other(OtherError::TimerDriverDurationOutOfRange)) + } + } + } + } + }; + let alarm_instant = num_ticks + ticks; + Ok(alarm_instant) + } + + fn activate_current_timer( + &self, + this_alarm: ActiveTimer, + suspended_timer: &Cell>, + ) -> TockResult { + let now = get_current_ticks()?; + + if let Some(active) = self.context.active_timer.get() { + if left_is_later(active, this_alarm) { + suspended_timer.set(Some(active)); + self.activate_timer(this_alarm)?; + } + } else { + self.activate_timer(this_alarm)?; + } + if is_over(this_alarm, now as u32) { + if let Some(paused) = suspended_timer.get() { + self.activate_timer(paused)?; + } else { + self.context.active_timer.set(None); + } + Ok(true) + } else { + Ok(false) + } + } +} + +fn get_current_ticks() -> TockResult { + syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0).map_err(|err| err.into()) +} +fn set_alarm_at(instant: usize) -> TockResult<()> { + syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, instant, 0) + .map(|_| ()) + .map_err(|err| err.into()) +} + +fn stop_alarm_at(instant: usize) -> TockResult<()> { + match syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, instant, 0) { + Ok(_) => Ok(()), + Err(error) => match error.return_code { + EALREADY => Ok(()), + _ => Err(TockError::Command(error)), + }, + } +} + +fn get_clock_frequency() -> TockResult { + syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0) + .map_err(|err| err.into()) +} + +fn is_over(timer: ActiveTimer, now: u32) -> bool { + now.wrapping_sub(timer.set_at) >= timer.instant.wrapping_sub(timer.set_at) +} + +fn left_is_later(alarm_1: ActiveTimer, alarm_2: ActiveTimer) -> bool { + if alarm_1.set_at <= alarm_1.instant && alarm_2.set_at <= alarm_2.instant { + return alarm_1.instant > alarm_2.instant; + } + if alarm_1.set_at <= alarm_1.instant && alarm_2.set_at >= alarm_2.instant { + return false; + } + if alarm_1.set_at >= alarm_1.instant && alarm_2.set_at <= alarm_2.instant { + return true; + } + if alarm_1.set_at >= alarm_1.instant && alarm_2.set_at >= alarm_2.instant { + return alarm_1.instant > alarm_2.instant; + } + false +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + pub fn duration_bigger_than_frequency() { + let x = ParallelSleepDriver::compute_alarm_instant(10000, 0, 1000) + .ok() + .unwrap(); + assert_eq!(x, 10000); + } + + #[test] + pub fn frequency_bigger_than_duration() { + let x = ParallelSleepDriver::compute_alarm_instant(1000, 0, 10000) + .ok() + .unwrap(); + assert_eq!(x, 10000); + } + + #[test] + pub fn fails_if_duration_is_too_large() { + let x = + ParallelSleepDriver::compute_alarm_instant(core::usize::MAX, 0, core::usize::MAX - 1); + assert!(x.is_err()); + } + + #[test] + pub fn fails_if_frequency_is_too_large() { + let x = + ParallelSleepDriver::compute_alarm_instant(core::usize::MAX - 1, 0, core::usize::MAX); + assert!(x.is_err()); + } + + #[test] + pub fn alarm_before_systick_wrap_expired() { + assert_eq!( + super::is_over( + super::ActiveTimer { + instant: 2u32, + set_at: 1u32 + }, + 3u32 + ), + true + ); + } + + #[test] + pub fn alarm_before_systick_wrap_not_expired() { + assert_eq!( + super::is_over( + super::ActiveTimer { + instant: 3u32, + set_at: 1u32 + }, + 2u32 + ), + false + ); + } + + #[test] + pub fn alarm_after_systick_wrap_expired() { + assert_eq!( + super::is_over( + super::ActiveTimer { + instant: 1u32, + set_at: 3u32 + }, + 2u32 + ), + true + ); + } + + #[test] + pub fn alarm_after_systick_wrap_time_before_systick_wrap_not_expired() { + assert_eq!( + super::is_over( + super::ActiveTimer { + instant: 1u32, + set_at: 3u32 + }, + 4u32 + ), + false + ); + } + + #[test] + pub fn alarm_after_systick_wrap_time_after_systick_wrap_not_expired() { + assert_eq!( + super::is_over( + super::ActiveTimer { + instant: 1u32, + set_at: 3u32 + }, + 0u32 + ), + false + ); + } + + #[test] + pub fn left_later_than_the_other_both_not_wrapped() { + let later = super::ActiveTimer { + instant: 3u32, + set_at: 1u32, + }; + let earlier = super::ActiveTimer { + instant: 2u32, + set_at: 1u32, + }; + assert_eq!(super::left_is_later(later, earlier), true); + } + + #[test] + pub fn right_later_than_the_other_both_not_wrapped() { + let later = super::ActiveTimer { + instant: 2u32, + set_at: 1u32, + }; + let earlier = super::ActiveTimer { + instant: 3u32, + set_at: 1u32, + }; + assert_eq!(super::left_is_later(later, earlier), false); + } + + #[test] + pub fn left_later_left_wrapped() { + let later = super::ActiveTimer { + instant: 1u32, + set_at: 3u32, + }; + let earlier = super::ActiveTimer { + instant: 2u32, + set_at: 1u32, + }; + assert_eq!(super::left_is_later(later, earlier), true); + } + + #[test] + pub fn right_later_right_wrapped() { + let later = super::ActiveTimer { + instant: 3u32, + set_at: 1u32, + }; + let earlier = super::ActiveTimer { + instant: 1u32, + set_at: 3u32, + }; + assert_eq!(super::left_is_later(later, earlier), false); + } + + #[test] + pub fn left_later_both_wrapped() { + let later = super::ActiveTimer { + instant: 2u32, + set_at: 3u32, + }; + let earlier = super::ActiveTimer { + instant: 1u32, + set_at: 3u32, + }; + assert_eq!(super::left_is_later(later, earlier), true); + } + + #[test] + pub fn right_later_both_wrapped() { + let later = super::ActiveTimer { + instant: 1u32, + set_at: 3u32, + }; + let earlier = super::ActiveTimer { + instant: 2u32, + set_at: 3u32, + }; + assert_eq!(super::left_is_later(later, earlier), false); + } + + #[test] + pub fn inequality_is_strict() { + let later = super::ActiveTimer { + instant: 2u32, + set_at: 1u32, + }; + let earlier = super::ActiveTimer { + instant: 2u32, + set_at: 1u32, + }; + assert_eq!(super::left_is_later(later, earlier), false); + } + + #[test] + pub fn inequality_is_strict_wrapped() { + let later = super::ActiveTimer { + instant: 1u32, + set_at: 2u32, + }; + let earlier = super::ActiveTimer { + instant: 1u32, + set_at: 2u32, + }; + assert_eq!(super::left_is_later(later, earlier), false); + } +}