├── .cargo └── config.toml ├── .github └── workflows │ ├── artifacts.yml │ ├── ci.yml │ ├── mac-os.yml │ └── size-diff.yml ├── .gitignore ├── .gitmodules ├── .ignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── apis ├── interface │ ├── buttons │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── buzzer │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── console │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ └── leds │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ └── tests.rs ├── kernel │ └── low_level_debug │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ └── tests.rs ├── net │ └── ieee802154 │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ ├── rx.rs │ │ └── tests.rs ├── peripherals │ ├── adc │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── alarm │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── gpio │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── i2c_master │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── i2c_master_slave │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── rng │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── spi_controller │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs ├── sensors │ ├── air_quality │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── ambient_light │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── ninedof │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── proximity │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ ├── sound_pressure │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── tests.rs │ └── temperature │ │ ├── Cargo.toml │ │ └── src │ │ ├── lib.rs │ │ └── tests.rs └── storage │ └── key_value │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── tests.rs ├── build.rs ├── build_scripts ├── Cargo.toml ├── README.md ├── libtock_layout.ld └── src │ └── lib.rs ├── demos ├── st7789-slint │ ├── Cargo.toml │ ├── build.rs │ ├── src │ │ └── main.rs │ └── ui │ │ └── appwindow.slint └── st7789 │ ├── Cargo.toml │ ├── build.rs │ └── src │ └── main.rs ├── doc ├── CargoFeatures.md ├── CodeReview.md ├── Dependencies.md ├── DesignConsiderations.md ├── FaultDebuggingExample.md ├── MiriTips.md ├── Overview.md ├── PlatformDesignStory.md ├── Startup.md ├── Style.md ├── Testing.md └── UnitTestOwnership.md ├── examples ├── adc.rs ├── ambient_light.rs ├── blink.rs ├── buttons.rs ├── console.rs ├── gpio.rs ├── i2c_master_write_read.rs ├── i2c_slave_send_recv.rs ├── ieee802154.rs ├── ieee802154_rx.rs ├── ieee802154_rx_tx.rs ├── ieee802154_tx.rs ├── kv.rs ├── leds.rs ├── low_level_debug.rs ├── music.rs ├── ninedof.rs ├── proximity.rs ├── rng.rs ├── rng_async.rs ├── sound_pressure.rs ├── spi_controller_write_read.rs ├── temperature.rs └── usb_i2c_mctp.rs ├── nightly └── rust-toolchain.toml ├── panic_handlers ├── debug_panic │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── small_panic │ ├── Cargo.toml │ └── src │ └── lib.rs ├── platform ├── Cargo.toml └── src │ ├── allow_ro.rs │ ├── allow_rw.rs │ ├── command_return.rs │ ├── command_return_tests.rs │ ├── constants.rs │ ├── default_config.rs │ ├── error_code.rs │ ├── error_code_tests.rs │ ├── exit_on_drop.rs │ ├── lib.rs │ ├── raw_syscalls.rs │ ├── register.rs │ ├── return_variant.rs │ ├── share │ ├── handle.rs │ ├── mod.rs │ ├── tests.rs │ └── tuple_impls.rs │ ├── subscribe.rs │ ├── syscalls.rs │ ├── syscalls_impl.rs │ ├── termination.rs │ └── yield_types.rs ├── runner ├── Cargo.toml └── src │ ├── elf2tab.rs │ ├── main.rs │ ├── output_processor.rs │ ├── qemu.rs │ └── tockloader.rs ├── runtime ├── Cargo.toml └── src │ ├── lib.rs │ ├── startup │ ├── asm_arm.s │ ├── asm_riscv32.s │ ├── mod.rs │ └── start_prototype.rs │ ├── syscalls_impl_arm.rs │ └── syscalls_impl_riscv.rs ├── rust-toolchain.toml ├── rustfmt.toml ├── src ├── lib.rs └── spi_controller.rs ├── syscalls_tests ├── Cargo.toml └── src │ ├── allow_ro.rs │ ├── allow_rw.rs │ ├── command_tests.rs │ ├── exit_on_drop.rs │ ├── lib.rs │ ├── memop_tests.rs │ ├── subscribe_tests.rs │ └── yield_tests.rs ├── tools └── print_sizes │ ├── Cargo.toml │ └── src │ └── main.rs ├── ufmt ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── macros │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── src │ ├── helpers.rs │ ├── impls.rs │ ├── impls │ │ ├── array.rs │ │ ├── core.rs │ │ ├── ixx.rs │ │ ├── nz.rs │ │ ├── ptr.rs │ │ ├── std.rs │ │ ├── tuple.rs │ │ └── uxx.rs │ ├── lib.rs │ └── macros.rs ├── tests │ └── vs-std-write.rs ├── utils │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── write │ ├── Cargo.toml │ └── src │ └── lib.rs └── unittest ├── Cargo.toml └── src ├── allow_db.rs ├── allow_db_test.rs ├── command_return.rs ├── driver_info.rs ├── exit_test ├── mod.rs └── tests.rs ├── expected_syscall.rs ├── fake ├── adc │ ├── mod.rs │ └── tests.rs ├── air_quality │ ├── mod.rs │ └── tests.rs ├── alarm │ ├── mod.rs │ └── tests.rs ├── ambient_light │ ├── mod.rs │ └── tests.rs ├── buttons │ ├── mod.rs │ └── tests.rs ├── buzzer │ ├── mod.rs │ └── tests.rs ├── console │ ├── mod.rs │ └── tests.rs ├── gpio │ ├── mod.rs │ └── tests.rs ├── ieee802154 │ └── mod.rs ├── kernel.rs ├── kernel_tests.rs ├── key_value │ └── mod.rs ├── leds │ ├── mod.rs │ └── tests.rs ├── low_level_debug │ ├── mod.rs │ └── tests.rs ├── mod.rs ├── ninedof │ ├── mod.rs │ └── tests.rs ├── proximity │ ├── mod.rs │ └── tests.rs ├── sound_pressure │ ├── mod.rs │ └── tests.rs ├── syscall_driver.rs ├── syscalls │ ├── allow_ro_impl.rs │ ├── allow_ro_impl_tests.rs │ ├── allow_rw_impl.rs │ ├── allow_rw_impl_tests.rs │ ├── command_impl.rs │ ├── command_impl_tests.rs │ ├── exit_impl.rs │ ├── exit_impl_tests.rs │ ├── memop_impl.rs │ ├── memop_impl_tests.rs │ ├── mod.rs │ ├── raw_syscalls_impl.rs │ ├── raw_syscalls_impl_tests.rs │ ├── subscribe_impl.rs │ ├── subscribe_impl_tests.rs │ ├── yield_impl.rs │ └── yield_impl_tests.rs └── temperature │ ├── mod.rs │ └── tests.rs ├── kernel_data.rs ├── lib.rs ├── share_data.rs ├── syscall_log.rs └── upcall.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | rriscv32imac = "run --release --target=riscv32imac-unknown-none-elf --example" 3 | rrv32imac = "rriscv32imac" 4 | rriscv32imc = "run --release --target=riscv32imc-unknown-none-elf --example" 5 | rrv32imc = "rriscv32imc" 6 | rthumbv7em = "run --release --target=thumbv7em-none-eabi --example" 7 | rtv7em = "rthumbv7em" 8 | 9 | # Common settings for all embedded targets 10 | [target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'] 11 | rustflags = [ 12 | "-C", "relocation-model=static", 13 | "-C", "link-arg=-icf=all", 14 | ] 15 | runner = ["cargo", "run", "-p", "runner", "--release"] 16 | -------------------------------------------------------------------------------- /.github/workflows/artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Artifacts 2 | on: 3 | push: 4 | branches: [master] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v3 12 | 13 | - name: Install dependencies 14 | run: | 15 | cargo install elf2tab 16 | 17 | - name: Build LEDs 18 | run: | 19 | make -j2 EXAMPLE=leds apollo3 20 | make -j2 EXAMPLE=leds hail 21 | make -j2 EXAMPLE=leds imix 22 | make -j2 EXAMPLE=leds nucleo_f429zi 23 | make -j2 EXAMPLE=leds nucleo_f446re 24 | make -j2 EXAMPLE=leds nrf52840 25 | make -j2 EXAMPLE=leds opentitan 26 | make -j2 EXAMPLE=leds hifive1 27 | make -j2 EXAMPLE=leds nrf52 28 | 29 | - name: Build Low Level Debug 30 | run: | 31 | make -j2 EXAMPLE=low_level_debug apollo3 32 | make -j2 EXAMPLE=low_level_debug hail 33 | make -j2 EXAMPLE=low_level_debug imix 34 | make -j2 EXAMPLE=low_level_debug nucleo_f429zi 35 | make -j2 EXAMPLE=low_level_debug nucleo_f446re 36 | make -j2 EXAMPLE=low_level_debug nrf52840 37 | make -j2 EXAMPLE=low_level_debug opentitan 38 | make -j2 EXAMPLE=low_level_debug hifive1 39 | make -j2 EXAMPLE=low_level_debug nrf52 40 | 41 | - name: Archive artifacts 42 | uses: actions/upload-artifact@v3 43 | with: 44 | name: libtock-rs examples 45 | path: target/tbf 46 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow provides automated testing. It builds and runs tests on each PR. 2 | 3 | name: ci 4 | 5 | # We want to run CI on all pull requests. Additionally, GitHub actions merge 6 | # queue needs workflows to run on the `merge_queue` trigger to block merges on 7 | # them. 8 | on: 9 | pull_request: 10 | merge_group: 11 | 12 | jobs: 13 | ci: 14 | # Using ubuntu-latest can cause breakage when ubuntu-latest is updated to 15 | # point at a new Ubuntu version. Instead, explicitly specify the version, so 16 | # we can update when we need to. This *could* break if we don't update it 17 | # until support for this version is dropped, but it is likely we'll have a 18 | # reason to update to a newer Ubuntu before then anyway. 19 | runs-on: ubuntu-22.04 20 | 21 | steps: 22 | # Clones a single commit from the libtock-rs repository. The commit cloned 23 | # is a merge commit between the PR's target branch and the PR's source. 24 | # Note that we checkout submodules so that we can invoke Tock's CI setup 25 | # scripts, but we do not recursively checkout submodules as we need Tock's 26 | # makefile to set up the qemu submodule itself. 27 | - name: Clone repository 28 | uses: actions/checkout@v3 29 | with: 30 | submodules: true 31 | 32 | # The main test step. We let the makefile do most of the work because the 33 | # makefile can be tested locally. We experimentally determined that -j2 is 34 | # optimal for the Azure Standard_DS2_v2 VM, which is the VM type used by 35 | # GitHub Actions at the time of this writing. 36 | # 37 | # We have to append the "-D warnings" flag to .cargo/config.toml rather 38 | # than using the RUSTFLAGS environment variable because if we set 39 | # RUSTFLAGS cargo will ignore the rustflags config in .cargo/config, 40 | # breaking relocation. 41 | - name: Build and Test 42 | run: | 43 | sudo apt-get install ninja-build 44 | cd "${GITHUB_WORKSPACE}" 45 | echo "[target.'cfg(all())']" >> .cargo/config.toml 46 | echo 'rustflags = ["-D", "warnings"]' >> .cargo/config.toml 47 | make -j2 setup 48 | make -j2 test 49 | -------------------------------------------------------------------------------- /.github/workflows/mac-os.yml: -------------------------------------------------------------------------------- 1 | # This workflow verifies libtock-rs is usable on Mac OS. 2 | 3 | name: ci-mac-os 4 | 5 | # We run this workflow during pull request review, but not as a required status 6 | # for GitHub actions merge-queue merges. We can change this if the workflow is 7 | # reasonably quick and reliable. 8 | on: pull_request 9 | 10 | jobs: 11 | ci-mac-os: 12 | runs-on: macos-latest 13 | 14 | steps: 15 | # Clones a single commit from the libtock-rs repository. The commit cloned 16 | # is a merge commit between the PR's target branch and the PR's source. 17 | - name: Clone repository 18 | uses: actions/checkout@v3 19 | 20 | - name: Build and Test 21 | run: | 22 | cd "${GITHUB_WORKSPACE}" 23 | LIBTOCK_PLATFORM=nrf52 cargo build -p libtock \ 24 | --target=thumbv7em-none-eabi 25 | LIBTOCK_PLATFORM=hifive1 cargo build -p libtock \ 26 | --target=riscv32imac-unknown-none-elf 27 | -------------------------------------------------------------------------------- /.github/workflows/size-diff.yml: -------------------------------------------------------------------------------- 1 | # Calculates the size diffs for the each example binary. Runs when a pull 2 | # request is created or modified. 3 | 4 | name: size-diff 5 | 6 | # We want to run this on all pull requests. Additionally, GitHub actions merge 7 | # queue needs workflows to run on the `merge_queue` trigger to block merges on 8 | # them. 9 | on: 10 | pull_request: 11 | merge_group: 12 | 13 | jobs: 14 | size-diff: 15 | # Using ubuntu-latest can cause breakage when ubuntu-latest is updated to 16 | # point at a new Ubuntu version. Instead, explicitly specify the version, so 17 | # we can update when we need to. This *could* break if we don't update it 18 | # until support for this version is dropped, but it is likely we'll have a 19 | # reason to update to a newer Ubuntu before then anyway. 20 | runs-on: ubuntu-22.04 21 | 22 | steps: 23 | # Clones a single commit from the libtock-rs repository. The commit cloned 24 | # is a merge commit between the PR's target branch and the PR's source. 25 | # We'll later add another commit (the pre-merge target branch) to the 26 | # repository. 27 | - name: Clone repository 28 | uses: actions/checkout@v3 29 | 30 | # The main diff script. Stores the sizes of the example binaries for both 31 | # the merge commit and the target branch. We display the diff in a 32 | # separate step to make it easy to navigate to in the GitHub Actions UI. 33 | # 34 | # If the build on master doesn't work (`make -j2 examples` fails), we 35 | # output a warning message and ignore the error. Ignoring the error 36 | # prevents this workflow from blocking PRs that fix a broken build in 37 | # master. 38 | - name: Compute sizes 39 | run: | 40 | UPSTREAM_REMOTE_NAME="${UPSTREAM_REMOTE_NAME:-origin}" 41 | GITHUB_BASE_REF="${GITHUB_BASE_REF:-master}" 42 | cd "${GITHUB_WORKSPACE}" 43 | make -j2 examples # The VM this runs on has 2 logical cores. 44 | cargo run --release -p print_sizes >'${{runner.temp}}/merge-sizes' 45 | git remote set-branches "${UPSTREAM_REMOTE_NAME}" "${GITHUB_BASE_REF}" 46 | git fetch --depth=1 "${UPSTREAM_REMOTE_NAME}" "${GITHUB_BASE_REF}" 47 | git checkout "${UPSTREAM_REMOTE_NAME}/${GITHUB_BASE_REF}" 48 | make -j2 examples && \ 49 | cargo run --release -p print_sizes >'${{runner.temp}}/base-sizes' || \ 50 | echo 'Broken build on the master branch.' 51 | 52 | # Computes and displays the size diff. diff returns a nonzero status code 53 | # if the files differ, and GitHub interprets a nonzero status code as an 54 | # error. To avoid GitHub interpreting a difference as an error, we add 55 | # || exit 0 to the command. This also prevents the workflow from failing 56 | # if the master build is broken and we didn't generate base-sizes. 57 | - name: Size diff 58 | run: diff '${{runner.temp}}/base-sizes' '${{runner.temp}}/merge-sizes' || exit 0 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Cargo.lock 2 | /nightly/target 3 | /target 4 | /demos/*/target 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tock"] 2 | path = tock 3 | url = https://github.com/tock/tock.git 4 | -------------------------------------------------------------------------------- /.ignore: -------------------------------------------------------------------------------- 1 | # Cargo and GitHub Actions keep their configs in hidden directories. Some search 2 | # tools skip hidden directories by default. This overrides that, and makes them 3 | # search .cargo and .github. 4 | !.cargo/ 5 | !.github/ 6 | 7 | # Tell search tools to ignore the Tock submodule. Usually when someone wants to 8 | # search this repository they want to search libtock-rs' codebase, not the Tock 9 | # kernel. 10 | /tock/ 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "rust.all_targets": true, 4 | "rust.clippy_preference": "on" 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Releases 2 | 3 | ## 0.2.0 (WIP) 4 | 5 | ### Comprehensive Changes 6 | 7 | - Many functions are asynchronous 8 | - To create an `async` main function you can use the attribute `#[libtock::main]` 9 | - To retrieve the value of an asynchronous `value`, use `value.await` 10 | - This is only possible within an `async fn`, so either 11 | - Make the caller `fn` of `.await` an `async fn` 12 | - Not recommended: Use `libtock::executor::block_on(value)` to retrieve the `value` 13 | - Most API functions, including `main()`, return a `Result` 14 | - All drivers can exclusively be retrieved by `retrieve_drivers` which returns a `Drivers` singleton. Drivers can be shared between different tasks only if it is safe to do so. 15 | - The low-level functions have been moved to a new crate called `libtock-core`. This crate is intended to be less experimental and more stable. 16 | 17 | ### Changed APIs 18 | 19 | - The basic APIs have been made consistent. They are initialized via driver factories and no longer require a `WithCallback` object, s.t. the callback subscription is more intuitive. The affected APIs are: 20 | - LEDs 21 | - Buttons 22 | - GPIO 23 | - Temperature 24 | - ADC (partially) 25 | - The timer API now supports concurrent sleep operations 26 | 27 | ### Syscalls 28 | 29 | - `syscalls::subscribe` is actually usable 30 | - `syscalls::yieldk_for` is no longer available 31 | - Yielding manually is discouraged as it conflicts with Rust's safety guarantees. If you need to wait for a condition, use `futures::wait_until` and `.await`. 32 | - `syscalls::yieldk` has become `unsafe` for the same reason 33 | - `syscalls::command` is no longer `unsafe` 34 | - The low-level syscalls have been moved to `syscalls::raw` 35 | - `syscalls::subscribe_ptr` becomes `syscalls::raw::subscribe` 36 | - `syscalls::allow_ptr` becomes `syscalls::raw::allow` 37 | 38 | ### Miscellaneous 39 | 40 | - Flashing examples is no longer restricted to the nRF52 DK board 41 | - `./run_example.sh` has been deleted 42 | - Instead, use `PLATFORM= cargo r `. This will build the app for your CPU architecture and platform-specific memory layout and flash it via J-Link to your board 43 | - Targets without support for atomics can be built 44 | - The `TockAllocator` is no longer included by default and needs to to be opted-in via `--features=alloc` 45 | - `hardware_test.rs` is now called `libtock_test.rs` to make clear that the intent is to test the correctness of `libtock-rs`, not the hardware or the kernel 46 | - The panic handler can now be customized using the `custom_panic_handler` feature 47 | - The error alloc handler can now be customized using the `custom_alloc_error_handler` feature 48 | 49 | ## a8bb4fa9be504517d5533511fd8e607ea61f1750 (0.1.0) 50 | 51 | - First and highly experimental `libtock-rs` API 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | Our aim is to provide a number of tests to be safe from regression. 4 | 5 | ## Compilation 6 | 7 | `libtock-rs` currently has the following compilation targets 8 | 9 | - `riscv32imac-unknown-none-elf` 10 | - `riscv32imc-unknown-none-elf` 11 | - `thumbv7em-none-eabi` 12 | 13 | You can trigger a test build of the library and the examples using `make test`. 14 | You can run the library's test suite using `make test`. 15 | 16 | # PR Review Workflow 17 | 18 | Our code review practices are documented in our [Code Review](doc/CodeReview.md) 19 | document. 20 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 The Tock Project Developers 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. 26 | -------------------------------------------------------------------------------- /apis/interface/buttons/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_buttons" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2018" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock buttons driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/interface/buzzer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_buzzer" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock buzzer driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/interface/buzzer/src/tests.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | use libtock_platform::ErrorCode; 3 | use libtock_unittest::fake; 4 | 5 | type Buzzer = super::Buzzer; 6 | 7 | #[test] 8 | fn no_driver() { 9 | let _kernel = fake::Kernel::new(); 10 | assert_eq!(Buzzer::exists(), Err(ErrorCode::NoDevice)); 11 | } 12 | 13 | #[test] 14 | fn exists() { 15 | let kernel = fake::Kernel::new(); 16 | let driver = fake::Buzzer::new(); 17 | kernel.add_driver(&driver); 18 | 19 | assert_eq!(Buzzer::exists(), Ok(())); 20 | } 21 | 22 | #[test] 23 | fn tone() { 24 | let kernel = fake::Kernel::new(); 25 | let driver = fake::Buzzer::new(); 26 | kernel.add_driver(&driver); 27 | let duration = Duration::from_millis(100); 28 | assert_eq!(Buzzer::tone(1000, duration), Ok(())); 29 | assert!(driver.is_busy()); 30 | 31 | assert_eq!(Buzzer::tone(1000, duration), Err(ErrorCode::Busy)); 32 | } 33 | 34 | #[test] 35 | fn tone_sync() { 36 | let kernel = fake::Kernel::new(); 37 | let driver = fake::Buzzer::new(); 38 | kernel.add_driver(&driver); 39 | 40 | let duration = Duration::from_millis(100); 41 | 42 | driver.set_tone_sync(1000, 100); 43 | assert_eq!(Buzzer::tone_sync(1000, duration), Ok(())); 44 | } 45 | -------------------------------------------------------------------------------- /apis/interface/console/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_console" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "dcz ", 7 | ] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2021" 10 | repository = "https://www.github.com/tock/libtock-rs" 11 | rust-version.workspace = true 12 | description = "libtock console driver" 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | 17 | [dev-dependencies] 18 | libtock_unittest = { path = "../../../unittest" } 19 | -------------------------------------------------------------------------------- /apis/interface/leds/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_leds" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock leds driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/interface/leds/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use libtock_platform::{ErrorCode, Syscalls}; 4 | 5 | /// The LEDs driver 6 | /// 7 | /// # Example 8 | /// ```ignore 9 | /// use libtock::Leds; 10 | /// 11 | /// // Turn on led 0 12 | /// let _ = Leds::on(0); 13 | /// ``` 14 | 15 | pub struct Leds(S); 16 | 17 | impl Leds { 18 | /// Run a check against the leds capsule to ensure it is present. 19 | /// 20 | /// Returns `Ok(number_of_leds)` if the driver was present. This does not necessarily mean 21 | /// that the driver is working, as it may still fail to allocate grant 22 | /// memory. 23 | pub fn count() -> Result { 24 | S::command(DRIVER_NUM, LEDS_COUNT, 0, 0).to_result() 25 | } 26 | 27 | pub fn on(led: u32) -> Result<(), ErrorCode> { 28 | S::command(DRIVER_NUM, LED_ON, led, 0).to_result() 29 | } 30 | 31 | pub fn off(led: u32) -> Result<(), ErrorCode> { 32 | S::command(DRIVER_NUM, LED_OFF, led, 0).to_result() 33 | } 34 | 35 | pub fn toggle(led: u32) -> Result<(), ErrorCode> { 36 | S::command(DRIVER_NUM, LED_TOGGLE, led, 0).to_result() 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests; 42 | 43 | // ----------------------------------------------------------------------------- 44 | // Driver number and command IDs 45 | // ----------------------------------------------------------------------------- 46 | 47 | const DRIVER_NUM: u32 = 0x2; 48 | 49 | // Command IDs 50 | const LEDS_COUNT: u32 = 0; 51 | const LED_ON: u32 = 1; 52 | const LED_OFF: u32 = 2; 53 | const LED_TOGGLE: u32 = 3; 54 | -------------------------------------------------------------------------------- /apis/interface/leds/src/tests.rs: -------------------------------------------------------------------------------- 1 | use libtock_platform::ErrorCode; 2 | use libtock_unittest::fake; 3 | 4 | type Leds = super::Leds; 5 | 6 | #[test] 7 | fn no_driver() { 8 | let _kernel = fake::Kernel::new(); 9 | assert_eq!(Leds::count(), Err(ErrorCode::NoDevice)); 10 | } 11 | 12 | #[test] 13 | fn exists() { 14 | let kernel = fake::Kernel::new(); 15 | let driver = fake::Leds::<10>::new(); 16 | kernel.add_driver(&driver); 17 | 18 | assert_eq!(Leds::count(), Ok(10)); 19 | for led in 0..10 { 20 | assert_eq!(driver.get_led(led), Some(false)); 21 | } 22 | } 23 | 24 | #[test] 25 | fn num_leds() { 26 | let kernel = fake::Kernel::new(); 27 | let driver = fake::Leds::<10>::new(); 28 | kernel.add_driver(&driver); 29 | assert_eq!(Leds::count().unwrap_or_default(), 10); 30 | } 31 | 32 | #[test] 33 | fn on() { 34 | let kernel = fake::Kernel::new(); 35 | let driver = fake::Leds::<10>::new(); 36 | kernel.add_driver(&driver); 37 | 38 | assert_eq!(Leds::on(0), Ok(())); 39 | assert_eq!(driver.get_led(0), Some(true)); 40 | } 41 | 42 | #[test] 43 | fn off() { 44 | let kernel = fake::Kernel::new(); 45 | let driver = fake::Leds::<10>::new(); 46 | kernel.add_driver(&driver); 47 | 48 | assert_eq!(Leds::off(0), Ok(())); 49 | assert_eq!(driver.get_led(0), Some(false)); 50 | } 51 | 52 | #[test] 53 | fn toggle() { 54 | let kernel = fake::Kernel::new(); 55 | let driver = fake::Leds::<10>::new(); 56 | kernel.add_driver(&driver); 57 | 58 | assert_eq!(Leds::toggle(0), Ok(())); 59 | assert_eq!(driver.get_led(0), Some(true)); 60 | assert_eq!(Leds::toggle(0), Ok(())); 61 | assert_eq!(driver.get_led(0), Some(false)); 62 | } 63 | 64 | #[test] 65 | fn on_off() { 66 | let kernel = fake::Kernel::new(); 67 | let driver = fake::Leds::<10>::new(); 68 | kernel.add_driver(&driver); 69 | 70 | assert_eq!(Leds::on(0), Ok(())); 71 | assert_eq!(driver.get_led(0), Some(true)); 72 | assert_eq!(Leds::off(0), Ok(())); 73 | assert_eq!(driver.get_led(0), Some(false)); 74 | } 75 | 76 | #[test] 77 | fn no_led() { 78 | let kernel = fake::Kernel::new(); 79 | let driver = fake::Leds::<10>::new(); 80 | kernel.add_driver(&driver); 81 | 82 | assert_eq!(Leds::on(11), Err(ErrorCode::Invalid)); 83 | for led in 0..Leds::count().unwrap_or_default() { 84 | assert_eq!(driver.get_led(led), Some(false)); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /apis/kernel/low_level_debug/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_low_level_debug" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock low-level debug drivers" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/kernel/low_level_debug/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use libtock_platform::Syscalls; 4 | 5 | /// The low-level debug API provides tools to diagnose userspace issues that 6 | /// make normal debugging workflows (e.g. printing to the console) difficult. 7 | /// 8 | /// It allows libraries to print alert codes and apps to print numeric 9 | /// information using only the command system call. 10 | /// 11 | /// # Example 12 | /// ```ignore 13 | /// use libtock::LowLevelDebug; 14 | /// 15 | /// // Prints 0x45 and the app which called it. 16 | /// LowLevelDebug::print_1(0x45); 17 | /// ``` 18 | 19 | pub struct LowLevelDebug(S); 20 | 21 | impl LowLevelDebug { 22 | /// Run a check against the low-level debug capsule to ensure it is present. 23 | /// 24 | /// Returns `true` if the driver was present. This does not necessarily mean 25 | /// that the driver is working, as it may still fail to allocate grant 26 | /// memory. 27 | #[inline(always)] 28 | pub fn exists() -> bool { 29 | S::command(DRIVER_NUM, EXISTS, 0, 0).is_success() 30 | } 31 | 32 | /// Print one of the predefined alerts in [`AlertCode`]. 33 | #[inline(always)] 34 | pub fn print_alert_code(code: AlertCode) { 35 | let _ = S::command(DRIVER_NUM, PRINT_ALERT_CODE, code as u32, 0); 36 | } 37 | 38 | /// Print a single number. The number will be printed in hexadecimal. 39 | /// 40 | /// In general, this should only be added temporarily for debugging and 41 | /// should not be called by released library code. 42 | #[inline(always)] 43 | pub fn print_1(x: u32) { 44 | let _ = S::command(DRIVER_NUM, PRINT_1, x, 0); 45 | } 46 | 47 | /// Print two numbers. The numbers will be printed in hexadecimal. 48 | /// 49 | /// Like `print_1`, this is intended for temporary debugging and should not 50 | /// be called by released library code. If you want to print multiple 51 | /// values, it is often useful to use the first argument to indicate what 52 | /// value is being printed. 53 | #[inline(always)] 54 | pub fn print_2(x: u32, y: u32) { 55 | let _ = S::command(DRIVER_NUM, PRINT_2, x, y); 56 | } 57 | } 58 | 59 | /// A predefined alert code, for use with [`LowLevelDebug::print_alert_code`]. 60 | pub enum AlertCode { 61 | /// Application panic (e.g. `panic!()` called in Rust code). 62 | Panic = 0x01, 63 | 64 | /// A statically-linked app was not installed in the correct location in 65 | /// flash. 66 | WrongLocation = 0x02, 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests; 71 | 72 | // ----------------------------------------------------------------------------- 73 | // Driver number and command IDs 74 | // ----------------------------------------------------------------------------- 75 | 76 | const DRIVER_NUM: u32 = 0x8; 77 | 78 | // Command IDs 79 | const EXISTS: u32 = 0; 80 | const PRINT_ALERT_CODE: u32 = 1; 81 | const PRINT_1: u32 = 2; 82 | const PRINT_2: u32 = 3; 83 | -------------------------------------------------------------------------------- /apis/kernel/low_level_debug/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use libtock_platform::ErrorCode; 3 | use libtock_unittest::{command_return, fake, ExpectedSyscall}; 4 | 5 | type LowLevelDebug = super::LowLevelDebug; 6 | 7 | #[test] 8 | fn no_driver() { 9 | let _kernel = fake::Kernel::new(); 10 | assert!(!LowLevelDebug::exists()); 11 | } 12 | 13 | #[test] 14 | fn exists() { 15 | let kernel = fake::Kernel::new(); 16 | let driver = fake::LowLevelDebug::new(); 17 | kernel.add_driver(&driver); 18 | 19 | assert!(LowLevelDebug::exists()); 20 | assert_eq!(driver.take_messages(), []); 21 | } 22 | 23 | #[test] 24 | fn print_alert_code() { 25 | let kernel = fake::Kernel::new(); 26 | let driver = fake::LowLevelDebug::new(); 27 | kernel.add_driver(&driver); 28 | 29 | LowLevelDebug::print_alert_code(AlertCode::Panic); 30 | LowLevelDebug::print_alert_code(AlertCode::WrongLocation); 31 | assert_eq!( 32 | driver.take_messages(), 33 | [ 34 | fake::Message::AlertCode(0x01), 35 | fake::Message::AlertCode(0x02) 36 | ] 37 | ); 38 | } 39 | 40 | #[test] 41 | fn print_1() { 42 | let kernel = fake::Kernel::new(); 43 | let driver = fake::LowLevelDebug::new(); 44 | kernel.add_driver(&driver); 45 | 46 | LowLevelDebug::print_1(42); 47 | assert_eq!(driver.take_messages(), [fake::Message::Print1(42)]); 48 | } 49 | 50 | #[test] 51 | fn print_2() { 52 | let kernel = fake::Kernel::new(); 53 | let driver = fake::LowLevelDebug::new(); 54 | kernel.add_driver(&driver); 55 | 56 | LowLevelDebug::print_2(42, 27); 57 | LowLevelDebug::print_2(29, 43); 58 | assert_eq!( 59 | driver.take_messages(), 60 | [fake::Message::Print2(42, 27), fake::Message::Print2(29, 43)] 61 | ); 62 | } 63 | 64 | #[test] 65 | fn failed_print() { 66 | let kernel = fake::Kernel::new(); 67 | let driver = fake::LowLevelDebug::new(); 68 | kernel.add_driver(&driver); 69 | kernel.add_expected_syscall(ExpectedSyscall::Command { 70 | driver_id: DRIVER_NUM, 71 | command_id: PRINT_1, 72 | argument0: 72, 73 | argument1: 0, 74 | override_return: Some(command_return::failure(ErrorCode::Fail)), 75 | }); 76 | 77 | // The error is explicitly silenced, and cannot be detected. 78 | LowLevelDebug::print_1(72); 79 | 80 | // The fake driver still receives the command even if a fake error is injected. 81 | assert_eq!(driver.take_messages(), [fake::Message::Print1(72)]); 82 | } 83 | -------------------------------------------------------------------------------- /apis/net/ieee802154/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_ieee802154" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | ] 7 | license = "Apache-2.0 OR MIT" 8 | edition = "2021" 9 | repository = "https://www.github.com/tock/libtock-rs" 10 | rust-version.workspace = true 11 | description = "libtock raw IEEE 802.15.4 stack driver" 12 | 13 | [dependencies] 14 | libtock_platform = { path = "../../../platform" } 15 | 16 | [dev-dependencies] 17 | libtock_unittest = { path = "../../../unittest" } 18 | -------------------------------------------------------------------------------- /apis/peripherals/adc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_adc" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock adc driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/peripherals/adc/src/tests.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; 3 | use libtock_unittest::fake; 4 | 5 | type Adc = super::Adc; 6 | 7 | #[test] 8 | fn no_driver() { 9 | let _kernel = fake::Kernel::new(); 10 | assert_eq!(Adc::exists(), Err(ErrorCode::NoDevice)); 11 | } 12 | 13 | #[test] 14 | fn exists() { 15 | let kernel = fake::Kernel::new(); 16 | let driver = fake::Adc::new(); 17 | kernel.add_driver(&driver); 18 | 19 | assert_eq!(Adc::exists(), Ok(())); 20 | } 21 | 22 | #[test] 23 | fn read_single_sample() { 24 | let kernel = fake::Kernel::new(); 25 | let driver = fake::Adc::new(); 26 | kernel.add_driver(&driver); 27 | 28 | assert_eq!(Adc::read_single_sample(), Ok(())); 29 | assert!(driver.is_busy()); 30 | 31 | assert_eq!(Adc::read_single_sample(), Err(ErrorCode::Busy)); 32 | assert_eq!(Adc::read_single_sample_sync(), Err(ErrorCode::Busy)); 33 | } 34 | 35 | #[test] 36 | fn register_unregister_listener() { 37 | let kernel = fake::Kernel::new(); 38 | let driver = fake::Adc::new(); 39 | kernel.add_driver(&driver); 40 | 41 | let sample: Cell> = Cell::new(None); 42 | let listener = crate::ADCListener(|adc_val| { 43 | sample.set(Some(adc_val)); 44 | }); 45 | share::scope(|subscribe| { 46 | assert_eq!(Adc::read_single_sample(), Ok(())); 47 | driver.set_value(100); 48 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 49 | 50 | assert_eq!(Adc::register_listener(&listener, subscribe), Ok(())); 51 | assert_eq!(Adc::read_single_sample(), Ok(())); 52 | driver.set_value(100); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 54 | assert_eq!(sample.get(), Some(100)); 55 | 56 | Adc::unregister_listener(); 57 | assert_eq!(Adc::read_single_sample(), Ok(())); 58 | driver.set_value(100); 59 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 60 | }); 61 | } 62 | 63 | #[test] 64 | fn read_single_sample_sync() { 65 | let kernel = fake::Kernel::new(); 66 | let driver = fake::Adc::new(); 67 | kernel.add_driver(&driver); 68 | 69 | driver.set_value_sync(1000); 70 | assert_eq!(Adc::read_single_sample_sync(), Ok(1000)); 71 | } 72 | -------------------------------------------------------------------------------- /apis/peripherals/alarm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_alarm" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "dcz ", 7 | ] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2018" 10 | repository = "https://www.github.com/tock/libtock-rs" 11 | rust-version.workspace = true 12 | description = "libtock alarm driver" 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | 17 | [dev-dependencies] 18 | libtock_unittest = { path = "../../../unittest" } 19 | -------------------------------------------------------------------------------- /apis/peripherals/alarm/src/tests.rs: -------------------------------------------------------------------------------- 1 | use libtock_unittest::fake; 2 | 3 | use crate::{Hz, Milliseconds, Ticks}; 4 | 5 | type Alarm = crate::Alarm; 6 | 7 | #[test] 8 | fn get_freq() { 9 | let kernel = fake::Kernel::new(); 10 | let driver = fake::Alarm::new(1000); 11 | kernel.add_driver(&driver); 12 | assert_eq!(Alarm::get_frequency(), Ok(Hz(1000))); 13 | } 14 | 15 | #[test] 16 | fn sleep() { 17 | let kernel = fake::Kernel::new(); 18 | let driver = fake::Alarm::new(1000); 19 | kernel.add_driver(&driver); 20 | 21 | assert_eq!(Alarm::sleep_for(Ticks(0)), Ok(())); 22 | assert_eq!(Alarm::sleep_for(Ticks(1000)), Ok(())); 23 | assert_eq!(Alarm::sleep_for(Milliseconds(1000)), Ok(())); 24 | } 25 | -------------------------------------------------------------------------------- /apis/peripherals/gpio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_gpio" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2018" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock gpio driver" 10 | 11 | [features] 12 | rust_embedded = ["embedded-hal"] 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | embedded-hal = { version = "1.0", optional = true } 17 | 18 | [dev-dependencies] 19 | libtock_unittest = { path = "../../../unittest" } 20 | -------------------------------------------------------------------------------- /apis/peripherals/i2c_master/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_i2c_master" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "Alistair Francis ", 7 | ] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2021" 10 | repository = "https://www.github.com/tock/libtock-rs" 11 | rust-version.workspace = true 12 | description = "libtock I2C master driver" 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | 17 | -------------------------------------------------------------------------------- /apis/peripherals/i2c_master_slave/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_i2c_master_slave" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "Wilfred Mallawa ", 7 | ] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2021" 10 | repository = "https://www.github.com/tock/libtock-rs" 11 | rust-version.workspace = true 12 | description = "libtock I2C master-slave driver" 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | 17 | -------------------------------------------------------------------------------- /apis/peripherals/rng/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_rng" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "Cosmin Gabriel Georgescu ", 7 | ] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2021" 10 | repository = "https://www.github.com/tock/libtock-rs" 11 | rust-version.workspace = true 12 | description = "libtock rng driver" 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | 17 | [dev-dependencies] 18 | libtock_unittest = { path = "../../../unittest" } 19 | -------------------------------------------------------------------------------- /apis/peripherals/spi_controller/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_spi_controller" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | "Alistair Francis ", 7 | ] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2021" 10 | repository = "https://www.github.com/tock/libtock-rs" 11 | rust-version.workspace = true 12 | description = "libtock SPI controller driver" 13 | 14 | [dependencies] 15 | libtock_platform = { path = "../../../platform" } 16 | -------------------------------------------------------------------------------- /apis/sensors/air_quality/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_air_quality" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock air quality driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/sensors/ambient_light/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_ambient_light" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock ambient light driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/sensors/ambient_light/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::cell::Cell; 4 | use libtock_platform::{ 5 | share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, 6 | }; 7 | 8 | pub struct AmbientLight(S); 9 | 10 | impl AmbientLight { 11 | /// Returns Ok() if the driver was present.This does not necessarily mean 12 | /// that the driver is working. 13 | pub fn exists() -> Result<(), ErrorCode> { 14 | S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() 15 | } 16 | 17 | /// Initiate a light intensity reading. 18 | pub fn read_intensity() -> Result<(), ErrorCode> { 19 | S::command(DRIVER_NUM, READ_INTENSITY, 0, 0).to_result() 20 | } 21 | 22 | /// Register an events listener 23 | pub fn register_listener<'share, F: Fn(u32)>( 24 | listener: &'share IntensityListener, 25 | subscribe: share::Handle>, 26 | ) -> Result<(), ErrorCode> { 27 | S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) 28 | } 29 | 30 | /// Unregister the events listener 31 | pub fn unregister_listener() { 32 | S::unsubscribe(DRIVER_NUM, 0) 33 | } 34 | 35 | /// Initiate a synchronous light intensity measurement. 36 | /// Returns Ok(intensity_value) if the operation was successful 37 | /// intensity_value is returned in lux 38 | pub fn read_intensity_sync() -> Result { 39 | let intensity_cell: Cell> = Cell::new(None); 40 | let listener = IntensityListener(|intensity_val| { 41 | intensity_cell.set(Some(intensity_val)); 42 | }); 43 | 44 | share::scope(|subscribe| { 45 | Self::register_listener(&listener, subscribe)?; 46 | Self::read_intensity()?; 47 | while intensity_cell.get().is_none() { 48 | S::yield_wait(); 49 | } 50 | 51 | match intensity_cell.get() { 52 | None => Err(ErrorCode::Busy), 53 | Some(intensity_val) => Ok(intensity_val), 54 | } 55 | }) 56 | } 57 | } 58 | 59 | /// A wrapper around a closure to be registered and called when 60 | /// a luminance reading is done. 61 | /// 62 | /// ```ignore 63 | /// let listener = IntensityListener(|intensity_val| { 64 | /// // make use of the intensity value 65 | /// }); 66 | /// ``` 67 | pub struct IntensityListener(pub F); 68 | 69 | impl Upcall> for IntensityListener { 70 | fn upcall(&self, intensity: u32, _arg1: u32, _arg2: u32) { 71 | self.0(intensity) 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests; 77 | 78 | // ----------------------------------------------------------------------------- 79 | // Driver number and command IDs 80 | // ----------------------------------------------------------------------------- 81 | 82 | const DRIVER_NUM: u32 = 0x60002; 83 | 84 | // Command IDs 85 | 86 | const EXISTS: u32 = 0; 87 | const READ_INTENSITY: u32 = 1; 88 | -------------------------------------------------------------------------------- /apis/sensors/ambient_light/src/tests.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; 3 | use libtock_unittest::fake; 4 | 5 | use crate::IntensityListener; 6 | 7 | type AmbientLight = super::AmbientLight; 8 | 9 | #[test] 10 | fn no_driver() { 11 | let _kernel = fake::Kernel::new(); 12 | assert_eq!(AmbientLight::exists(), Err(ErrorCode::NoDevice)); 13 | } 14 | 15 | #[test] 16 | fn exists() { 17 | let kernel = fake::Kernel::new(); 18 | let driver = fake::AmbientLight::new(); 19 | kernel.add_driver(&driver); 20 | 21 | assert_eq!(AmbientLight::exists(), Ok(())); 22 | } 23 | 24 | #[test] 25 | fn read_temperature() { 26 | let kernel = fake::Kernel::new(); 27 | let driver = fake::AmbientLight::new(); 28 | kernel.add_driver(&driver); 29 | 30 | assert_eq!(AmbientLight::read_intensity(), Ok(())); 31 | assert!(driver.is_busy()); 32 | 33 | assert_eq!(AmbientLight::read_intensity(), Err(ErrorCode::Busy)); 34 | assert_eq!(AmbientLight::read_intensity_sync(), Err(ErrorCode::Busy)); 35 | } 36 | 37 | #[test] 38 | fn register_unregister_listener() { 39 | let kernel = fake::Kernel::new(); 40 | let driver = fake::AmbientLight::new(); 41 | kernel.add_driver(&driver); 42 | 43 | let intensity_cell: Cell> = Cell::new(None); 44 | let listener = IntensityListener(|val| { 45 | intensity_cell.set(Some(val)); 46 | }); 47 | share::scope(|subscribe| { 48 | assert_eq!(AmbientLight::read_intensity(), Ok(())); 49 | driver.set_value(100); 50 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 51 | 52 | assert_eq!( 53 | AmbientLight::register_listener(&listener, subscribe), 54 | Ok(()) 55 | ); 56 | assert_eq!(AmbientLight::read_intensity(), Ok(())); 57 | driver.set_value(100); 58 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 59 | assert_eq!(intensity_cell.get(), Some(100)); 60 | 61 | AmbientLight::unregister_listener(); 62 | assert_eq!(AmbientLight::read_intensity(), Ok(())); 63 | driver.set_value(100); 64 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 65 | }); 66 | } 67 | 68 | #[test] 69 | fn read_temperature_sync() { 70 | let kernel = fake::Kernel::new(); 71 | let driver = fake::AmbientLight::new(); 72 | kernel.add_driver(&driver); 73 | 74 | driver.set_value_sync(1000); 75 | assert_eq!(AmbientLight::read_intensity_sync(), Ok(1000)); 76 | } 77 | -------------------------------------------------------------------------------- /apis/sensors/ninedof/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_ninedof" 3 | version = "0.1.0" 4 | authors = [ "Tock Project Developers " ] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2018" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock nine degrees of freedom driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | libm = "0.2.7" 14 | 15 | [dev-dependencies] 16 | libtock_unittest = { path = "../../../unittest" } 17 | -------------------------------------------------------------------------------- /apis/sensors/proximity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_proximity" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock proximity driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | 17 | -------------------------------------------------------------------------------- /apis/sensors/proximity/src/tests.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; 3 | use libtock_unittest::fake; 4 | 5 | type Proximity = super::Proximity; 6 | 7 | #[test] 8 | fn no_driver() { 9 | let _kernel = fake::Kernel::new(); 10 | assert_eq!(Proximity::exists(), Err(ErrorCode::NoDevice)); 11 | } 12 | 13 | #[test] 14 | fn exists() { 15 | let kernel = fake::Kernel::new(); 16 | let driver = fake::Proximity::new(); 17 | kernel.add_driver(&driver); 18 | 19 | assert_eq!(Proximity::exists(), Ok(())); 20 | } 21 | 22 | #[test] 23 | fn busy_driver() { 24 | let kernel = fake::Kernel::new(); 25 | let driver = fake::Proximity::new(); 26 | kernel.add_driver(&driver); 27 | 28 | assert_eq!(Proximity::read(), Ok(())); 29 | assert_eq!(Proximity::read(), Err(ErrorCode::Busy)); 30 | assert_eq!(Proximity::read_on_interrupt(0, 0), Err(ErrorCode::Busy)); 31 | 32 | driver.set_value(100); 33 | 34 | assert_eq!(Proximity::read_on_interrupt(0, 0), Ok(())); 35 | assert_eq!(Proximity::read(), Err(ErrorCode::Busy)); 36 | } 37 | 38 | #[test] 39 | fn async_readings() { 40 | let kernel = fake::Kernel::new(); 41 | let driver = fake::Proximity::new(); 42 | kernel.add_driver(&driver); 43 | 44 | let listener = Cell::>::new(None); 45 | 46 | share::scope(|subscribe| { 47 | assert_eq!(Proximity::read(), Ok(())); 48 | driver.set_value(100); 49 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 50 | 51 | assert_eq!(Proximity::register_listener(&listener, subscribe), Ok(())); 52 | assert_eq!(Proximity::read(), Ok(())); 53 | driver.set_value(100); 54 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 55 | assert_eq!(listener.get(), Some((100,))); 56 | 57 | assert_eq!(Proximity::read_on_interrupt(100, 200), Ok(())); 58 | driver.set_value(150); 59 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 60 | 61 | driver.set_value(99); 62 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 63 | assert_eq!(listener.get(), Some((99,))); 64 | }) 65 | } 66 | 67 | #[test] 68 | fn sync_readings() { 69 | let kernel = fake::Kernel::new(); 70 | let driver = fake::Proximity::new(); 71 | kernel.add_driver(&driver); 72 | 73 | driver.set_value_sync(100); 74 | assert_eq!(Proximity::read_sync(), Ok(100)); 75 | 76 | driver.set_value_sync(250); 77 | assert_eq!(Proximity::wait_for_value_between(100, 200), Ok(250)); 78 | } 79 | 80 | #[test] 81 | fn bad_arguments() { 82 | assert_eq!( 83 | Proximity::wait_for_value_between(200, 100), 84 | Err(ErrorCode::Invalid) 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /apis/sensors/sound_pressure/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_sound_pressure" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2018" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock sound pressure driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/sensors/sound_pressure/src/tests.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; 3 | use libtock_unittest::fake; 4 | 5 | type SoundPressure = super::SoundPressure; 6 | 7 | #[test] 8 | fn no_driver() { 9 | let _kernel = fake::Kernel::new(); 10 | assert_eq!(SoundPressure::exists(), Err(ErrorCode::NoDevice)); 11 | } 12 | 13 | #[test] 14 | fn exists() { 15 | let kernel = fake::Kernel::new(); 16 | let driver = fake::SoundPressure::new(); 17 | kernel.add_driver(&driver); 18 | 19 | assert_eq!(SoundPressure::exists(), Ok(())); 20 | } 21 | 22 | #[test] 23 | fn driver_busy() { 24 | let kernel = fake::Kernel::new(); 25 | let driver = fake::SoundPressure::new(); 26 | kernel.add_driver(&driver); 27 | 28 | assert_eq!(SoundPressure::read(), Ok(())); 29 | assert!(driver.is_busy()); 30 | 31 | assert_eq!(SoundPressure::read(), Err(ErrorCode::Busy)); 32 | assert_eq!(SoundPressure::read_sync(), Err(ErrorCode::Busy)); 33 | } 34 | 35 | #[test] 36 | fn read_pressure() { 37 | let kernel = fake::Kernel::new(); 38 | let driver = fake::SoundPressure::new(); 39 | kernel.add_driver(&driver); 40 | 41 | let pressure_cell: Cell> = Cell::new(None); 42 | let listener = crate::SoundPressureListener(|pressure_val| { 43 | pressure_cell.set(Some(pressure_val)); 44 | }); 45 | 46 | share::scope(|subscribe| { 47 | assert_eq!(SoundPressure::read(), Ok(())); 48 | driver.set_value(100); 49 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 50 | 51 | assert_eq!( 52 | SoundPressure::register_listener(&listener, subscribe), 53 | Ok(()) 54 | ); 55 | assert_eq!(SoundPressure::read(), Ok(())); 56 | driver.set_value(100); 57 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 58 | assert_eq!(pressure_cell.get(), Some(100)); 59 | 60 | SoundPressure::unregister_listener(); 61 | assert_eq!(SoundPressure::read(), Ok(())); 62 | driver.set_value(100); 63 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 64 | }); 65 | } 66 | 67 | #[test] 68 | fn read_pressure_sync() { 69 | let kernel = fake::Kernel::new(); 70 | let driver = fake::SoundPressure::new(); 71 | kernel.add_driver(&driver); 72 | 73 | driver.set_value_sync(100); 74 | assert_eq!(SoundPressure::read_sync(), Ok(100)); 75 | } 76 | -------------------------------------------------------------------------------- /apis/sensors/temperature/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_temperature" 3 | version = "0.1.0" 4 | authors = ["Tock Project Developers "] 5 | license = "Apache-2.0 OR MIT" 6 | edition = "2021" 7 | repository = "https://www.github.com/tock/libtock-rs" 8 | rust-version.workspace = true 9 | description = "libtock temperature driver" 10 | 11 | [dependencies] 12 | libtock_platform = { path = "../../../platform" } 13 | 14 | [dev-dependencies] 15 | libtock_unittest = { path = "../../../unittest" } 16 | -------------------------------------------------------------------------------- /apis/sensors/temperature/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::cell::Cell; 4 | use libtock_platform::{ 5 | share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, 6 | }; 7 | 8 | pub struct Temperature(S); 9 | 10 | impl Temperature { 11 | /// Returns Ok() if the driver was present.This does not necessarily mean 12 | /// that the driver is working. 13 | pub fn exists() -> Result<(), ErrorCode> { 14 | S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() 15 | } 16 | 17 | /// Initiate a temperature measurement. 18 | /// 19 | /// This function is used both for synchronous and asynchronous readings 20 | pub fn read_temperature() -> Result<(), ErrorCode> { 21 | S::command(DRIVER_NUM, READ_TEMP, 0, 0).to_result() 22 | } 23 | 24 | /// Register an events listener 25 | pub fn register_listener<'share, F: Fn(i32)>( 26 | listener: &'share TemperatureListener, 27 | subscribe: share::Handle>, 28 | ) -> Result<(), ErrorCode> { 29 | S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) 30 | } 31 | 32 | /// Unregister the events listener 33 | pub fn unregister_listener() { 34 | S::unsubscribe(DRIVER_NUM, 0) 35 | } 36 | 37 | /// Initiate a synchronous temperature measurement. 38 | /// Returns Ok(temperature_value) if the operation was successful 39 | /// temperature_value is returned in hundreds of centigrades 40 | pub fn read_temperature_sync() -> Result { 41 | let temperature_cell: Cell> = Cell::new(None); 42 | let listener = TemperatureListener(|temp_val| { 43 | temperature_cell.set(Some(temp_val)); 44 | }); 45 | share::scope(|subscribe| { 46 | if let Ok(()) = Self::register_listener(&listener, subscribe) { 47 | if let Ok(()) = Self::read_temperature() { 48 | while temperature_cell.get().is_none() { 49 | S::yield_wait(); 50 | } 51 | } 52 | } 53 | }); 54 | 55 | match temperature_cell.get() { 56 | None => Err(ErrorCode::Busy), 57 | Some(temp_val) => Ok(temp_val), 58 | } 59 | } 60 | } 61 | 62 | pub struct TemperatureListener(pub F); 63 | impl Upcall> for TemperatureListener { 64 | fn upcall(&self, temp_val: u32, _arg1: u32, _arg2: u32) { 65 | self.0(temp_val as i32) 66 | } 67 | } 68 | 69 | #[cfg(test)] 70 | mod tests; 71 | 72 | // ----------------------------------------------------------------------------- 73 | // Driver number and command IDs 74 | // ----------------------------------------------------------------------------- 75 | 76 | const DRIVER_NUM: u32 = 0x60000; 77 | 78 | // Command IDs 79 | 80 | const EXISTS: u32 = 0; 81 | const READ_TEMP: u32 = 1; 82 | -------------------------------------------------------------------------------- /apis/sensors/temperature/src/tests.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn}; 3 | use libtock_unittest::fake; 4 | 5 | type Temperature = super::Temperature; 6 | 7 | #[test] 8 | fn no_driver() { 9 | let _kernel = fake::Kernel::new(); 10 | assert_eq!(Temperature::exists(), Err(ErrorCode::NoDevice)); 11 | } 12 | 13 | #[test] 14 | fn exists() { 15 | let kernel = fake::Kernel::new(); 16 | let driver = fake::Temperature::new(); 17 | kernel.add_driver(&driver); 18 | 19 | assert_eq!(Temperature::exists(), Ok(())); 20 | } 21 | 22 | #[test] 23 | fn read_temperature() { 24 | let kernel = fake::Kernel::new(); 25 | let driver = fake::Temperature::new(); 26 | kernel.add_driver(&driver); 27 | 28 | assert_eq!(Temperature::read_temperature(), Ok(())); 29 | assert!(driver.is_busy()); 30 | 31 | assert_eq!(Temperature::read_temperature(), Err(ErrorCode::Busy)); 32 | assert_eq!(Temperature::read_temperature_sync(), Err(ErrorCode::Busy)); 33 | } 34 | 35 | #[test] 36 | fn register_unregister_listener() { 37 | let kernel = fake::Kernel::new(); 38 | let driver = fake::Temperature::new(); 39 | kernel.add_driver(&driver); 40 | 41 | let temperature_cell: Cell> = Cell::new(None); 42 | let listener = crate::TemperatureListener(|temp_val| { 43 | temperature_cell.set(Some(temp_val)); 44 | }); 45 | share::scope(|subscribe| { 46 | assert_eq!(Temperature::read_temperature(), Ok(())); 47 | driver.set_value(100); 48 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 49 | 50 | assert_eq!(Temperature::register_listener(&listener, subscribe), Ok(())); 51 | assert_eq!(Temperature::read_temperature(), Ok(())); 52 | driver.set_value(100); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 54 | assert_eq!(temperature_cell.get(), Some(100)); 55 | 56 | Temperature::unregister_listener(); 57 | assert_eq!(Temperature::read_temperature(), Ok(())); 58 | driver.set_value(100); 59 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 60 | }); 61 | } 62 | 63 | #[test] 64 | fn read_temperature_sync() { 65 | let kernel = fake::Kernel::new(); 66 | let driver = fake::Temperature::new(); 67 | kernel.add_driver(&driver); 68 | 69 | driver.set_value_sync(1000); 70 | assert_eq!(Temperature::read_temperature_sync(), Ok(1000)); 71 | } 72 | 73 | #[test] 74 | fn negative_value() { 75 | let kernel = fake::Kernel::new(); 76 | let driver = fake::Temperature::new(); 77 | kernel.add_driver(&driver); 78 | 79 | driver.set_value_sync(-1000); 80 | assert_eq!(Temperature::read_temperature_sync(), Ok(-1000)); 81 | } 82 | -------------------------------------------------------------------------------- /apis/storage/key_value/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_key_value" 3 | version = "0.1.0" 4 | authors = [ 5 | "Tock Project Developers ", 6 | ] 7 | license = "MIT/Apache-2.0" 8 | edition = "2021" 9 | repository = "https://www.github.com/tock/libtock-rs" 10 | description = "libtock key-value driver" 11 | 12 | [dependencies] 13 | libtock_platform = { path = "../../../platform" } 14 | 15 | [dev-dependencies] 16 | libtock_unittest = { path = "../../../unittest" } 17 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | libtock_build_scripts::auto_layout(); 3 | } 4 | -------------------------------------------------------------------------------- /build_scripts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tock Project Developers "] 3 | categories = ["embedded", "no-std", "os"] 4 | description = """Helper functions for compiling libtock-rs apps.""" 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | name = "libtock_build_scripts" 8 | repository = "https://www.github.com/tock/libtock-rs" 9 | rust-version.workspace = true 10 | version = "0.1.0" 11 | -------------------------------------------------------------------------------- /build_scripts/README.md: -------------------------------------------------------------------------------- 1 | Libtock Build Scripts Support Crate 2 | =================================== 3 | 4 | This crate provides helpers for building libtock-rs apps. 5 | 6 | Usage 7 | ----- 8 | 9 | There are three general steps to use this crate. 10 | 11 | 1. This crate should be included as a build dependency in the app's Cargo.toml 12 | file: 13 | 14 | ```toml 15 | # Cargo.toml 16 | ... 17 | 18 | [build-dependencies] 19 | libtock_build_scripts = { git = "https://github.com/tock/libtock-rs"} 20 | ``` 21 | 22 | This will ensure the crate and the contained linker scripts are available for 23 | the app build. 24 | 25 | 2. This crate provides a helper function which can used from the libtock-rs 26 | app's build.rs file. In the common case, you just call the provided function 27 | from the build.rs file in your crate's root: 28 | 29 | ```rs 30 | // build.rs 31 | 32 | fn main() { 33 | libtock_build_scripts::auto_layout(); 34 | } 35 | ``` 36 | 37 | This will allow cargo to setup linker scripts and paths for the linker when 38 | your app is built. 39 | 40 | 3. When calling `cargo build` you need to instruct the build.rs on where in 41 | memory to compile your app for. This crate supports two mechanisms to do 42 | this. You can only use one. 43 | 44 | 1. Set the `LIBTOCK_PLATFORM` environment variable which specifies the name 45 | of the linker script in `/layouts` to be used. So for example, if you are 46 | using the microbit_v2 you might run: 47 | 48 | ```bash 49 | $ LIBTOCK_PLATFORM=microbit_v2 cargo build --target thumbv7em-none-eabi --release 50 | ``` 51 | 52 | 2. Set the `LIBTOCK_LINKER_FLASH` and `LIBTOCK_LINKER_RAM` environment 53 | variables which specify the starting addresses of flash and RAM memory, 54 | respectively. This allows you to customize where exactly the compiled app 55 | must be placed in flash and RAM. For example, to build for common 56 | Cortex-M4 platforms you might run: 57 | 58 | ```bash 59 | $ LIBTOCK_LINKER_FLASH=0x00040000 LIBTOCK_LINKER_RAM=0x20008000 cargo build --target thumbv7em-none-eabi --release 60 | ``` 61 | -------------------------------------------------------------------------------- /demos/st7789-slint/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "st7789-slint" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | authors = ["Alistair Francis "] 7 | description = """A demo to use the Slint GUI library with a ST7789 display via SPI using libtock-rs.""" 8 | license = "Apache-2.0 OR MIT" 9 | 10 | [dependencies] 11 | libtock = { path = "../../", features = ["rust_embedded"] } 12 | 13 | embedded-hal = "1.0" 14 | 15 | mipidsi = "0.8.0" 16 | display-interface-spi = "0.5" 17 | embedded-graphics = "0.8" 18 | 19 | # The heap allocator and portable atomics 20 | embedded-alloc = "0.5.1" 21 | critical-section = "1.0" 22 | 23 | slint = { git = "https://github.com/slint-ui/slint", default-features = false, features = ["libm", "unsafe-single-threaded"] } 24 | mcu-board-support = { git = "https://github.com/slint-ui/slint" } 25 | 26 | display-interface = "0.5" 27 | 28 | [build-dependencies] 29 | libtock_build_scripts = { path = "../../build_scripts" } 30 | 31 | slint-build = { git = "https://github.com/slint-ui/slint" } 32 | -------------------------------------------------------------------------------- /demos/st7789-slint/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | libtock_build_scripts::auto_layout(); 3 | 4 | let config = slint_build::CompilerConfiguration::new() 5 | .embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer); 6 | slint_build::compile_with_config("ui/appwindow.slint", config).unwrap(); 7 | slint_build::print_rustc_flags().unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /demos/st7789-slint/ui/appwindow.slint: -------------------------------------------------------------------------------- 1 | import {Button, AboutSlint} from "std-widgets.slint"; 2 | 3 | export component MainWindow inherits Window { 4 | width: 240px; 5 | height: 240px; 6 | VerticalLayout { 7 | AboutSlint { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /demos/st7789/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "st7789" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version.workspace = true 6 | authors = ["Alistair Francis "] 7 | description = """A demo to drive a ST7789 display via SPI using libtock-rs.""" 8 | license = "Apache-2.0 OR MIT" 9 | 10 | [dependencies] 11 | libtock = { path = "../../", features = ["rust_embedded"] } 12 | 13 | embedded-hal = "1.0" 14 | 15 | mipidsi = "0.8.0" 16 | display-interface-spi = "0.5" 17 | embedded-graphics = "0.8" 18 | 19 | [build-dependencies] 20 | libtock_build_scripts = { path = "../../build_scripts" } 21 | -------------------------------------------------------------------------------- /demos/st7789/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | libtock_build_scripts::auto_layout(); 3 | } 4 | -------------------------------------------------------------------------------- /demos/st7789/src/main.rs: -------------------------------------------------------------------------------- 1 | //! This sample demonstrates displaying text on a ST7789 display 2 | //! using a rust-embedded based crate 3 | 4 | #![no_main] 5 | #![no_std] 6 | use core::fmt::Write; 7 | use libtock::alarm::{Alarm, Milliseconds}; 8 | use libtock::console::Console; 9 | use libtock::gpio::Gpio; 10 | use libtock::platform::Syscalls; 11 | use libtock::runtime::{set_main, stack_size, TockSyscalls}; 12 | use libtock::spi_controller::EmbeddedHalSpi; 13 | 14 | use display_interface_spi::SPIInterface; 15 | use embedded_graphics::{ 16 | mono_font::{ascii::FONT_10X20, MonoTextStyle}, 17 | pixelcolor::Rgb565, 18 | prelude::*, 19 | text::Text, 20 | }; 21 | use mipidsi::{models::ST7789, options::ColorInversion, Builder}; 22 | 23 | set_main! {main} 24 | stack_size! {0x1000} 25 | 26 | // Display 27 | const W: i32 = 240; 28 | const H: i32 = 240; 29 | 30 | struct Delay; 31 | 32 | impl embedded_hal::delay::DelayNs for Delay { 33 | fn delay_ns(&mut self, ns: u32) { 34 | Alarm::sleep_for(Milliseconds(ns / (1000 * 1000))).unwrap(); 35 | } 36 | } 37 | 38 | fn main() { 39 | writeln!(Console::writer(), "st7789: example\r").unwrap(); 40 | 41 | let mut gpio_dc = Gpio::get_pin(0).unwrap(); 42 | let dc = gpio_dc.make_output().unwrap(); 43 | 44 | let mut gpio_reset = Gpio::get_pin(1).unwrap(); 45 | let reset = gpio_reset.make_output().unwrap(); 46 | 47 | let di = SPIInterface::new(EmbeddedHalSpi, dc); 48 | 49 | let mut delay = Delay; 50 | let mut display = Builder::new(ST7789, di) 51 | .display_size(W as u16, H as u16) 52 | .invert_colors(ColorInversion::Inverted) 53 | .reset_pin(reset) 54 | .init(&mut delay) 55 | .unwrap(); 56 | 57 | // Text 58 | let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); 59 | let text = "Hello World ^_^;"; 60 | let text_x = W; 61 | let text_y = H / 2; 62 | 63 | // Clear the display initially 64 | display.clear(Rgb565::BLUE).unwrap(); 65 | 66 | writeln!(Console::writer(), "Clear complete\r").unwrap(); 67 | 68 | // Draw text 69 | Text::new(text, Point::new(text_x, text_y), text_style) 70 | .draw(&mut display) 71 | .unwrap(); 72 | 73 | loop { 74 | TockSyscalls::yield_wait(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /doc/CargoFeatures.md: -------------------------------------------------------------------------------- 1 | Cargo Features 2 | ============== 3 | 4 | On the surface, `cargo`'s 5 | [features](https://doc.rust-lang.org/cargo/reference/features.html) mechanism 6 | looks great for `libtock-rs`. Process binary crates can depend on `libtock-rs`, 7 | and use `cargo` features to specify which optional `libtock-rs` functionality 8 | they want. 9 | 10 | However, `cargo` assumes that it can compile a crate with features that a binary 11 | does not need. This isn't necessarily true for embedded crates, where process 12 | binary authors care about the app size. 13 | 14 | ## Process Binaries in a Workspace 15 | 16 | When used with workspaces, `cargo` features can result in excess code being 17 | built into a repository. 18 | 19 | For example, suppose that `libtock` exposes a `malloc` feature, which adds a 20 | dynamic memory allocator. Then if a `libtock` user creates a `cargo` workspace 21 | with the following process binaries in it: 22 | 23 | 1. `no_malloc`, which depends on `libtock` and does not depend on the `malloc` 24 | feature. 25 | 2. `malloc`, which depends on `libtock` and the `malloc` feature. 26 | 27 | With this setup, `malloc` will always work correctly. Also, if you run `cargo 28 | build -p no_malloc`, then `no_malloc` will correctly build without a memory 29 | allocation. 30 | 31 | Seems optimal, right? 32 | 33 | It's not: if you build the entire workspace with `cargo build --workspace`, 34 | `libtock` will only be built once, with the `malloc` feature! That will result 35 | in a `no_malloc` process binary that contains a memory allocator, which is not 36 | what the author desired. 37 | 38 | ## Alternative to `cargo` Features 39 | 40 | In many cases, `cargo` features can be replaced by splitting a crate up into 41 | smaller crates, and letting process binaries choose what to depend on. For 42 | example, memory allocation can be in a separate crate that process binaries 43 | depend on if and only if they need memory allocation. 44 | 45 | Here are some questions to help guide the decision between using a `cargo` 46 | feature and separate crates: 47 | 48 | 1. Do you forsee a single user writing multiple process binaries, some of which 49 | use this feature? If yes, then maybe it should not be a `cargo` feature. 50 | 2. Will the compiler optimize the feature away entirely if it is included but 51 | unused? If yes, then making it a `cargo` feature is probably fine. 52 | 53 | In many cases, it will make sense to add a `cargo` feature to `libtock` but use 54 | optional crates to implement the feature internally. This way, users with 55 | multiple process binaries in a workspace can choose whether each process binary 56 | depends on the optimal crate. 57 | -------------------------------------------------------------------------------- /doc/CodeReview.md: -------------------------------------------------------------------------------- 1 | Code Review 2 | =========== 3 | 4 | ## Code Review Practices 5 | 6 | PR to `libtock-rs` can be divided into two categories: 7 | 8 | 1. **Upkeep pull requests** are self-contained changes that do not introduce 9 | significant code churn, and are unlikely to cause major merge conflicts. 10 | 1. **Significant pull requests** are pull requests that are too substantial to 11 | be considered upkeep pull requests. Significant pull requests may represent a 12 | significant change in `libtock-rs`'s design or include large refactorings 13 | that are likely to cause merge conflicts for other pull requests. 14 | 15 | The owners of `libtock-rs` (listed [below](#owners)) determine whether a PR is 16 | an upkeep PR or a significant PR. PRs should be merged by the `libtock-rs` 17 | owners rather than the PR's author. In general, PRs should be merged using a 18 | `bors r+` command rather than the GitHub UI (see the [bors 19 | documentation](https://bors.tech/documentation/) for more information on bors). 20 | 21 | A PR may only be merged when all of the following are true: 22 | 23 | 1. At least one `libtock-rs` owner (who is not the PR author) has approved the PR. 24 | 1. All outstanding review discussions have been resolved. 25 | 1. If the pull request is significant, a 7 day waiting period has passed since 26 | the PR was opened. 27 | 28 | We recommend that authors of significant PRs comment on the PR when they believe 29 | the above criteria have been satisfied (including the waiting period). This is 30 | primarily to remind the owners to merge the PR. Secondarily, it should help 31 | identify confusion about a PR review's status. 32 | 33 | ## Owners 34 | 35 | The owners of `libtock-rs` are: 36 | 37 | * The [Tock Core Working 38 | Group](https://github.com/tock/tock/tree/master/doc/wg/core#members). 39 | * Alistair Francis, [alistair23](https://github.com/alistair23), Western Digital 40 | -------------------------------------------------------------------------------- /doc/Dependencies.md: -------------------------------------------------------------------------------- 1 | Third Party Dependencies 2 | ======================== 3 | 4 | This document is about dependencies that are required to build applications 5 | using `libtock-rs`. These dependencies are not contained in the libtock-rs 6 | repository, but are used by libtock-rs when libtock-rs is used as a dependency 7 | of an application. Dependencies required to run `libtock-rs`' tests (such as 8 | `make`) are outside the scope of this document. 9 | 10 | ## Unaudited Required Dependencies 11 | 12 | `libtock-rs` has the following required build dependencies, none of which are 13 | currently audited: 14 | 15 | * The Rust toolchain, including 16 | [`cargo`](https://github.com/rust-lang/cargo), 17 | [`rustc`](https://github.com/rust-lang/rust/tree/master/src/rustc), and 18 | [`libcore`](https://github.com/rust-lang/rust/tree/master/src/libcore). The 19 | specific toolchain version used is specified by the `rust-toolchain` file at 20 | the root of the repository. 21 | * [`syn`](https://crates.io/crates/syn), pulled in by `libtock_codegen`. 22 | * [`quote`](https://crates.io/crates/quote), pulled in by `libtock_codegen`. 23 | * [`proc-macro2`](https://crates.io/crates/proc-macro2), pulled in by 24 | `libtock_codegen`. 25 | 26 | ## Avoiding Optional Dependencies 27 | 28 | To avoid pulling in optional dependencies, users should use `libtock_core` 29 | instead of `libtock`. `libtock_core` is in the `core/` directory. 30 | -------------------------------------------------------------------------------- /doc/MiriTips.md: -------------------------------------------------------------------------------- 1 | Miri Tips 2 | ========= 3 | 4 | `libtock-rs` runs most of its unit tests under 5 | [Miri](https://github.com/rust-lang/miri) to detect undefined behavior. However, 6 | Miri does not detect all undefined behavior encountered. This document lists 7 | techniques for maximizing the amount of undefined behavior identified by Miri. 8 | 9 | ## Miri flags 10 | 11 | Miri has [configuration 12 | flags](https://github.com/rust-lang/miri#miri--z-flags-and-environment-variables), 13 | which can be set using the `MIRIFLAGS` environment variable. We run the tests 14 | both with and without `-Zmiri-track-raw-pointers`, as both versions of Stacked 15 | Borrows has undefined behavior the other does not (see [this 16 | discussion](https://github.com/rust-lang/miri/pull/1748) for more information). 17 | On the run with `-Zmiri-track-raw-pointers`, we also set 18 | `-Zmiri-symbolic-alignment-check` to make Miri's alignment check more pendantic. 19 | 20 | ## Detecting invalid values 21 | 22 | Miri does not always detect undefined behavior when an invalid value is created 23 | but not used. Here are some cases that Miri currently accepts: 24 | 25 | 1. Unused reference to an invalid value 26 | (https://github.com/rust-lang/miri/issues/1638) 27 | 1. Unused uninitialized value (https://github.com/rust-lang/miri/issues/1340) 28 | 1. Slices pointing to invalid values 29 | (https://github.com/rust-lang/miri/issues/1240) 30 | 31 | Note that copying the value around (including passing it to functions) does 32 | *not* count as a use for the purpose of this check. 33 | 34 | For types that implement `core::fmt::Debug` (i.e. most types), you can check a 35 | value is valid by attempting to debug-print it: 36 | 37 | ```rust 38 | format!("{:?}", value); 39 | ``` 40 | 41 | If `value` is invalid, then Miri will identify undefined behavior in the above 42 | code. 43 | -------------------------------------------------------------------------------- /doc/Overview.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ====== 3 | 4 | This document gives an overview of the crates in this repository, and is 5 | intended to be useful to `libtock-rs` newcomers. 6 | 7 | ## `libtock` 8 | 9 | `libtock` provides the default `libtock-rs` experience. It re-exports all of the 10 | drivers `libtock-rs` provides, and provides usable defaults for panic handling 11 | and memory allocation. It should be easy to build a Tock application that only 12 | has one direct dependency: `libtock`. 13 | 14 | In order to be easy to use, `libtock` has a hard dependency on 15 | `libtock_runtime`, and therefore cannot be used in a unit test environment. If 16 | you want to unit test code that uses `libtock-rs`, you should depend on the 17 | individual libraries rather than `libtock`. 18 | 19 | ## Naming convention note 20 | 21 | Although these crates have yet to be uploaded to crates.io, they likely will be 22 | uploaded in the future. Therefore, to avoid name collisions, most crates in this 23 | repository have a name that begins with `libtock`. Crates that are only intended 24 | for internal use (e.g. `syscall_tests`, which tests code internal to 25 | `libtock_platform`) do not have the `libtock_` prefix. 26 | 27 | The directory names of `libtock_` crates do not contain the `libtock_` prefix. 28 | 29 | ## Core abstractions: `libtock_platform` 30 | 31 | In order to unit test `libtock-rs` code, we need a way to run `libtock-rs` on 32 | our development machines (and in CI). Ironically, that means most crates in 33 | `libtock-rs` are platform independent. `libtock_platform` provides the tools 34 | that allow code to run in both a unit test environment and in real Tock apps. It 35 | consists primarily of the `Syscalls` trait and supporting machinery. 36 | 37 | ## Syscall implementations: `libtock_runtime` and `libtock_unittest` 38 | 39 | In order to run `libtock-rs` code, you need a `libtock_platform::Syscalls` 40 | implementation. Multiple implementations exist that work in different 41 | environments: 42 | 43 | * `libtock_runtime` provides a syscall interface that uses a real Tock kernel. 44 | This is the crate to use in Tock process binaries. 45 | * `libtock_unittest` provides a fake kernel for use in unit tests. 46 | 47 | In addition, `libtock_runtime` provides the linker script and Rust runtime 48 | needed to start a Tock process binary. `libtock_unittest` relies on `std` to 49 | provide a runtime. 50 | 51 | ## Panic handler crates 52 | 53 | Each Rust binary must have exactly one panic handler (note that `std` provides a 54 | panic handler for binaries that depend on it). The following crates provide a 55 | `#[panic_handler]` implementation for Tock process binaries: 56 | 57 | * `libtock_panic_debug` provides useful diagnostics in the event of a panic, at 58 | the expense of code size. This is the panic handler used by `libtock`. 59 | 60 | ## Driver crates 61 | 62 | Driver crates provide interfaces to specific Tock APIs in the `/apis` directory. 63 | -------------------------------------------------------------------------------- /doc/Style.md: -------------------------------------------------------------------------------- 1 | Coding Style 2 | ============ 3 | 4 | ## List Ordering 5 | 6 | Source code tends to contain many lists whose order is unimportant, such as 7 | dependency lists in `Cargo.toml` and `mod` declarations in `.rs` files. When 8 | there isn't a better reason to prefer a particular order, these lists should be 9 | in alphabetical order. 10 | 11 | Benefits: 12 | 13 | 1. When lists become long, this makes it easier to search for a particular 14 | entry. 15 | 2. Always adding new entries at the bottom of the list results in merge 16 | conflicts whenever two PRs add entries to the same list. Putting them in 17 | alphabetical order decreases the probability of merge conflicts. 18 | 19 | ## Naming Conventions 20 | 21 | Many things in `libtock-rs` live outside Rust's namespacing system, and can 22 | therefore collide with other libraries. Examples include: 23 | 24 | 1. `cargo` package names 25 | 2. Environment variables 26 | 3. Linker script file names 27 | 28 | For example, if `libtock-rs` were to contain a `cargo` package called `adc`, 29 | that package would likely collide with another package if we were to try to 30 | upload it to crates.io. 31 | 32 | To prevent these collisions, things in `libtock-rs` that can experience external 33 | name collisions should have a `libtock` prefix (with suitable capitalization). 34 | For example: 35 | 36 | 1. The runtime crate is called `libtock_runtime` 37 | 2. The build platform environment variable is `LIBTOCK_PLATFORM` 38 | 3. The linker script is called `libtock_layout.ld` 39 | 40 | However, this prefix should be omitted when it is unnecessary, to avoid naming 41 | everything in the repository `libtock_*`. For example: 42 | 43 | 1. `libtock_` is omitted from directory names. `libtock_runtime` lives in the 44 | `/runtime/` directory. 45 | 2. Cargo packages that are not used externally, such as the `print_sizes` tool 46 | (which is only used for developing `libtock-rs` itself), do not have the 47 | `libtock_` prefix. 48 | -------------------------------------------------------------------------------- /doc/Testing.md: -------------------------------------------------------------------------------- 1 | Testing 2 | ======= 3 | 4 | This document gives an introduction to the tests in this repository, and is 5 | intended to be useful to `libtock-rs` contributors. 6 | 7 | ## Unit Tests 8 | 9 | Cargo packages in this repository that are platform-independent (i.e. that do 10 | not depend on `libtock_runtime`) have unit tests that use Rust's builtin test 11 | harness. These unit tests run on a fully-featured host OS (Linux, Mac OS, 12 | Windows, etc...) and therefore do not depend on a real Tock kernel. Instead, 13 | these unit tests use the fake kernel provided by `libtock_unittest`. As a 14 | result, most `libtock-rs` crates have `libtock_unittest` in their 15 | `[dev-dependencies]`. 16 | 17 | `libtock_platform` is a bit of an exception to this rule. If all of 18 | `libtock_platform`'s unit tests were in `libtock_platform`, the following would 19 | occur: 20 | 21 | 1. `cargo` builds `libtock_platform` with `cfg(test)` enabled to make the test 22 | binary. 23 | 2. `cargo` builds `libtock_unittest` because it is a `[dev-dependency]` of 24 | `libtock_platform` 25 | 3. `cargo` builds `libtock_platform` without `cfg(test)` because it is a 26 | dependency of `libtock_unittest` 27 | 28 | Both copies of `libtock_platform` end up in the final binary. These contain 29 | incompatible instances of the `Syscalls` trait, resulting in hard-to-understand 30 | errors. 31 | 32 | To solve this, some of `libtock_platform`'s unit tests (namely, those that 33 | require `libtock_unittest`) were moved to a `platform_test` crate. 34 | 35 | ## Integration Tests 36 | 37 | `libtock-rs`'s integration tests are Tock process binaries that can run on an 38 | emulated or real Tock system. They live in `libtock`'s `tests/` directory. 39 | 40 | TODO: Figure out a test runner strategy for automatically running all the 41 | integration tests, and document it here. 42 | -------------------------------------------------------------------------------- /examples/adc.rs: -------------------------------------------------------------------------------- 1 | //! A simple libtock-rs example. Checks for adc driver 2 | //! and samples the sensor every 2 seconds. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use core::fmt::Write; 8 | use libtock::console::Console; 9 | 10 | use libtock::adc::Adc; 11 | use libtock::alarm::{Alarm, Milliseconds}; 12 | use libtock::runtime::{set_main, stack_size}; 13 | 14 | set_main! {main} 15 | stack_size! {0x200} 16 | 17 | fn main() { 18 | if Adc::exists().is_err() { 19 | writeln!(Console::writer(), "adc driver unavailable").unwrap(); 20 | return; 21 | } 22 | 23 | loop { 24 | match Adc::read_single_sample_sync() { 25 | Ok(adc_val) => writeln!(Console::writer(), "Sample: {}\n", adc_val).unwrap(), 26 | Err(_) => writeln!(Console::writer(), "error while reading sample",).unwrap(), 27 | } 28 | 29 | Alarm::sleep_for(Milliseconds(2000)).unwrap(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/ambient_light.rs: -------------------------------------------------------------------------------- 1 | //! A simple libtock-rs example. Checks for ambient light driver 2 | //! and samples the sensor every 2 seconds. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use core::fmt::Write; 8 | use libtock::console::Console; 9 | 10 | use libtock::alarm::{Alarm, Milliseconds}; 11 | use libtock::ambient_light::AmbientLight; 12 | use libtock::runtime::{set_main, stack_size}; 13 | 14 | set_main! {main} 15 | stack_size! {0x200} 16 | 17 | fn main() { 18 | if AmbientLight::exists().is_err() { 19 | writeln!(Console::writer(), "ambient light driver unavailable").unwrap(); 20 | return; 21 | } 22 | 23 | loop { 24 | match AmbientLight::read_intensity_sync() { 25 | Ok(intensity_val) => writeln!( 26 | Console::writer(), 27 | "Light intensity: {} lux\n", 28 | intensity_val 29 | ) 30 | .unwrap(), 31 | Err(_) => writeln!(Console::writer(), "error while reading light intensity",).unwrap(), 32 | } 33 | 34 | Alarm::sleep_for(Milliseconds(2000)).unwrap(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/blink.rs: -------------------------------------------------------------------------------- 1 | //! The blink app. 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | use libtock::alarm::{Alarm, Milliseconds}; 7 | use libtock::leds::Leds; 8 | use libtock::runtime::{set_main, stack_size}; 9 | 10 | set_main! {main} 11 | stack_size! {0x200} 12 | 13 | fn main() { 14 | let mut count = 0; 15 | if let Ok(leds_count) = Leds::count() { 16 | loop { 17 | for led_index in 0..leds_count { 18 | if count & (1 << led_index) > 0 { 19 | let _ = Leds::on(led_index as u32); 20 | } else { 21 | let _ = Leds::off(led_index as u32); 22 | } 23 | } 24 | 25 | Alarm::sleep_for(Milliseconds(250)).unwrap(); 26 | 27 | count += 1; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/buttons.rs: -------------------------------------------------------------------------------- 1 | //! An extremely simple libtock-rs example. Register button events. 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | use core::fmt::Write; 7 | use libtock::buttons::{ButtonListener, Buttons}; 8 | use libtock::console::Console; 9 | use libtock::leds::Leds; 10 | use libtock::runtime::{set_main, stack_size}; 11 | use libtock_platform::{share, Syscalls}; 12 | use libtock_runtime::TockSyscalls; 13 | 14 | set_main! {main} 15 | stack_size! {0x1000} 16 | 17 | fn main() { 18 | let listener = ButtonListener(|button, state| { 19 | let _ = Leds::toggle(button); 20 | writeln!(Console::writer(), "button {:?}: {:?}", button, state).unwrap(); 21 | }); 22 | if let Ok(buttons_count) = Buttons::count() { 23 | writeln!(Console::writer(), "button count: {}", buttons_count).unwrap(); 24 | 25 | share::scope(|subscribe| { 26 | // Subscribe to the button callback. 27 | Buttons::register_listener(&listener, subscribe).unwrap(); 28 | 29 | // Enable interrupts for each button press. 30 | for i in 0..buttons_count { 31 | Buttons::enable_interrupts(i).unwrap(); 32 | } 33 | 34 | // Wait for buttons to be pressed. 35 | loop { 36 | TockSyscalls::yield_wait(); 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/console.rs: -------------------------------------------------------------------------------- 1 | //! An extremely simple libtock-rs example. Just prints out a message 2 | //! using the Console capsule, then terminates. 3 | 4 | #![no_main] 5 | #![no_std] 6 | use core::fmt::Write; 7 | use libtock::console::Console; 8 | use libtock::runtime::{set_main, stack_size}; 9 | 10 | set_main! {main} 11 | stack_size! {0x100} 12 | 13 | fn main() { 14 | writeln!(Console::writer(), "Hello world!").unwrap(); 15 | } 16 | -------------------------------------------------------------------------------- /examples/gpio.rs: -------------------------------------------------------------------------------- 1 | //! A simple GPIO example for getting GPIO interrupts. 2 | //! 3 | //! This will configure GPIO 0 to be a rising-edge triggered interrupt and print 4 | //! a message when the interrupt is triggered. 5 | 6 | #![no_main] 7 | #![no_std] 8 | 9 | use core::fmt::Write; 10 | use libtock::console::Console; 11 | use libtock::gpio; 12 | use libtock::gpio::Gpio; 13 | use libtock::runtime::{set_main, stack_size}; 14 | use libtock_platform::{share, Syscalls}; 15 | use libtock_runtime::TockSyscalls; 16 | 17 | set_main! {main} 18 | stack_size! {0x1000} 19 | 20 | fn main() { 21 | let listener = gpio::GpioInterruptListener(|gpio_index, state| { 22 | writeln!(Console::writer(), "GPIO[{}]: {:?}", gpio_index, state).unwrap(); 23 | }); 24 | 25 | if !Gpio::count().is_ok_and(|c| c > 0) { 26 | writeln!(Console::writer(), "No GPIO pins on this board.").unwrap(); 27 | return; 28 | } 29 | 30 | // Configure pin 0 as an input and enable rising interrupts 31 | let pin = Gpio::get_pin(0).unwrap(); 32 | let input_pin = pin.make_input::().unwrap(); 33 | let _ = input_pin.enable_interrupts(gpio::PinInterruptEdge::Rising); 34 | 35 | // Wait for callbacks. 36 | share::scope(|subscribe| { 37 | Gpio::register_listener(&listener, subscribe).unwrap(); 38 | 39 | loop { 40 | TockSyscalls::yield_wait(); 41 | } 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /examples/ieee802154.rs: -------------------------------------------------------------------------------- 1 | //! An example showing use of IEEE 802.15.4 networking. 2 | 3 | #![no_main] 4 | #![no_std] 5 | use libtock::console::Console; 6 | use libtock::ieee802154::{Ieee802154, RxOperator as _, RxRingBuffer, RxSingleBufferOperator}; 7 | use libtock::runtime::{set_main, stack_size}; 8 | 9 | set_main! {main} 10 | stack_size! {0x600} 11 | 12 | fn main() { 13 | // Configure the radio 14 | let pan: u16 = 0xcafe; 15 | let addr_short: u16 = 0xdead; 16 | let addr_long: u64 = 0xdead_dad; 17 | let tx_power: i8 = -3; 18 | let channel: u8 = 11; 19 | 20 | Ieee802154::set_pan(pan); 21 | Ieee802154::set_address_short(addr_short); 22 | Ieee802154::set_address_long(addr_long); 23 | Ieee802154::set_tx_power(tx_power).unwrap(); 24 | Ieee802154::set_channel(channel).unwrap(); 25 | 26 | // Don't forget to commit the config! 27 | Ieee802154::commit_config(); 28 | 29 | // Turn the radio on 30 | Ieee802154::radio_on().unwrap(); 31 | assert!(Ieee802154::is_on()); 32 | 33 | // Transmit a frame 34 | Ieee802154::transmit_frame(b"foobar").unwrap(); 35 | 36 | Console::write(b"Transmitted frame!\n").unwrap(); 37 | 38 | // Showcase receiving to a single buffer - there is a risk of losing some frames. 39 | // See [RxSingleBufferOperator] docs for more details. 40 | rx_single_buffer(); 41 | } 42 | 43 | fn rx_single_buffer() { 44 | let mut buf = RxRingBuffer::<2>::new(); 45 | let mut operator = RxSingleBufferOperator::new(&mut buf); 46 | 47 | let frame1 = operator.receive_frame().unwrap(); 48 | // Access frame1 data here: 49 | let _body_len = frame1.payload_len; 50 | let _first_body_byte = frame1.body[0]; 51 | 52 | let _frame2 = operator.receive_frame().unwrap(); 53 | // Access frame2 data here 54 | } 55 | -------------------------------------------------------------------------------- /examples/ieee802154_rx.rs: -------------------------------------------------------------------------------- 1 | //! An example showing use of IEEE 802.15.4 networking. 2 | //! It infinitely received a frame and prints its content to Console. 3 | 4 | #![no_main] 5 | #![no_std] 6 | use core::fmt::Write as _; 7 | use libtock::console::Console; 8 | use libtock::ieee802154::{Ieee802154, RxOperator as _, RxRingBuffer, RxSingleBufferOperator}; 9 | use libtock::runtime::{set_main, stack_size}; 10 | 11 | set_main! {main} 12 | stack_size! {0x600} 13 | 14 | fn main() { 15 | // Configure the radio 16 | let pan: u16 = 0xcafe; 17 | let addr_short: u16 = 0xdead; 18 | let addr_long: u64 = 0xdead_dad; 19 | let tx_power: i8 = 5; 20 | let channel: u8 = 11; 21 | 22 | Ieee802154::set_pan(pan); 23 | Ieee802154::set_address_short(addr_short); 24 | Ieee802154::set_address_long(addr_long); 25 | Ieee802154::set_tx_power(tx_power).unwrap(); 26 | Ieee802154::set_channel(channel).unwrap(); 27 | 28 | // Don't forget to commit the config! 29 | Ieee802154::commit_config(); 30 | 31 | // Turn the radio on 32 | Ieee802154::radio_on().unwrap(); 33 | assert!(Ieee802154::is_on()); 34 | 35 | let mut buf = RxRingBuffer::<2>::new(); 36 | let mut operator = RxSingleBufferOperator::new(&mut buf); 37 | loop { 38 | let frame = operator.receive_frame().unwrap(); 39 | 40 | let body_len = frame.payload_len; 41 | writeln!( 42 | Console::writer(), 43 | "Received frame with body of len {}: {} {:?}!\n", 44 | body_len, 45 | core::str::from_utf8(&frame.body).unwrap(), 46 | &frame.body[..frame.body.len() - core::mem::size_of::()] 47 | ) 48 | .unwrap(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/ieee802154_rx_tx.rs: -------------------------------------------------------------------------------- 1 | //! An example showing use of IEEE 802.15.4 networking. 2 | //! It infinitely sends a frame with a constantly incremented counter, 3 | //! and after each send receives a frame and prints it to Console. 4 | 5 | #![no_main] 6 | #![no_std] 7 | use core::fmt::Write as _; 8 | use libtock::alarm::{Alarm, Milliseconds}; 9 | use libtock::console::Console; 10 | use libtock::ieee802154::{Ieee802154, RxOperator as _, RxRingBuffer, RxSingleBufferOperator}; 11 | use libtock::runtime::{set_main, stack_size}; 12 | 13 | set_main! {main} 14 | stack_size! {0x600} 15 | 16 | fn main() { 17 | // Configure the radio 18 | let pan: u16 = 0xcafe; 19 | let addr_short: u16 = 0xdead; 20 | let addr_long: u64 = 0xdead_dad; 21 | let tx_power: i8 = 5; 22 | let channel: u8 = 11; 23 | 24 | Ieee802154::set_pan(pan); 25 | Ieee802154::set_address_short(addr_short); 26 | Ieee802154::set_address_long(addr_long); 27 | Ieee802154::set_tx_power(tx_power).unwrap(); 28 | Ieee802154::set_channel(channel).unwrap(); 29 | 30 | // Don't forget to commit the config! 31 | Ieee802154::commit_config(); 32 | 33 | // Turn the radio on 34 | Ieee802154::radio_on().unwrap(); 35 | assert!(Ieee802154::is_on()); 36 | 37 | let mut buf = RxRingBuffer::<2>::new(); 38 | let mut operator = RxSingleBufferOperator::new(&mut buf); 39 | 40 | let mut counter = 0_usize; 41 | let mut buf = [ 42 | b'f', b'r', b'a', b'm', b'e', b' ', b'n', b'.', b'o', b'.', b' ', b'\0', b'\0', b'\0', 43 | b'\0', 44 | ]; 45 | fn set_buf_cnt(buf: &mut [u8], counter: &mut usize) { 46 | let buf_len = buf.len(); 47 | let buf_cnt = &mut buf[buf_len - core::mem::size_of_val(&counter)..]; 48 | buf_cnt.copy_from_slice(&counter.to_be_bytes()); 49 | } 50 | 51 | loop { 52 | Alarm::sleep_for(Milliseconds(1000)).unwrap(); 53 | 54 | set_buf_cnt(&mut buf, &mut counter); 55 | 56 | // Transmit a frame 57 | Ieee802154::transmit_frame(&buf).unwrap(); 58 | 59 | writeln!(Console::writer(), "Transmitted frame {}!\n", counter).unwrap(); 60 | 61 | let frame = operator.receive_frame().unwrap(); 62 | 63 | let body_len = frame.payload_len; 64 | writeln!( 65 | Console::writer(), 66 | "Received frame with body of len {}: {}-{} {:?}!\n", 67 | body_len, 68 | core::str::from_utf8(&frame.body[..frame.body.len() - core::mem::size_of::()]) 69 | .unwrap_or(""), 70 | usize::from_le_bytes( 71 | frame.body[frame.body.len() - core::mem::size_of::()..] 72 | .try_into() 73 | .unwrap() 74 | ), 75 | frame.body 76 | ) 77 | .unwrap(); 78 | 79 | counter += 1; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/ieee802154_tx.rs: -------------------------------------------------------------------------------- 1 | //! An example showing use of IEEE 802.15.4 networking. 2 | //! It infinitely sends a frame with a constantly incremented counter. 3 | 4 | #![no_main] 5 | #![no_std] 6 | use core::fmt::Write as _; 7 | 8 | use libtock::alarm::{Alarm, Milliseconds}; 9 | use libtock::console::Console; 10 | use libtock::ieee802154::Ieee802154; 11 | use libtock::runtime::{set_main, stack_size}; 12 | 13 | set_main! {main} 14 | stack_size! {0x600} 15 | 16 | fn main() { 17 | // Configure the radio 18 | let pan: u16 = 0xcafe; 19 | let addr_short: u16 = 0xdead; 20 | let addr_long: u64 = 0xdead_dad; 21 | let tx_power: i8 = 5; 22 | let channel: u8 = 11; 23 | 24 | Ieee802154::set_pan(pan); 25 | Ieee802154::set_address_short(addr_short); 26 | Ieee802154::set_address_long(addr_long); 27 | Ieee802154::set_tx_power(tx_power).unwrap(); 28 | Ieee802154::set_channel(channel).unwrap(); 29 | 30 | // Don't forget to commit the config! 31 | Ieee802154::commit_config(); 32 | 33 | // Turn the radio on 34 | Ieee802154::radio_on().unwrap(); 35 | assert!(Ieee802154::is_on()); 36 | 37 | let mut counter = 0_usize; 38 | let mut buf = [ 39 | b'f', b'r', b'a', b'm', b'e', b' ', b'n', b'.', b'o', b'.', b' ', b'\0', b'\0', b'\0', 40 | b'\0', 41 | ]; 42 | fn set_buf_cnt(buf: &mut [u8], counter: &mut usize) { 43 | let buf_len = buf.len(); 44 | let buf_cnt = &mut buf[buf_len - core::mem::size_of_val(&counter)..]; 45 | buf_cnt.copy_from_slice(&counter.to_be_bytes()); 46 | } 47 | 48 | loop { 49 | Alarm::sleep_for(Milliseconds(1000)).unwrap(); 50 | 51 | set_buf_cnt(&mut buf, &mut counter); 52 | 53 | // Transmit a frame 54 | Ieee802154::transmit_frame(&buf).unwrap(); 55 | 56 | writeln!(Console::writer(), "Transmitted frame {}!\n", counter).unwrap(); 57 | 58 | counter += 1; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/kv.rs: -------------------------------------------------------------------------------- 1 | //! A key-value store example. Gets and sets key-value objects. 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | use core::fmt::Write; 7 | use core::str; 8 | use libtock::console::Console; 9 | 10 | use libtock::key_value::KeyValue; 11 | use libtock::runtime::{set_main, stack_size}; 12 | 13 | set_main! {main} 14 | stack_size! {0x200} 15 | 16 | /// Retrieve a key and insert the value into `value`. Then print the value 17 | /// contents as a UTF-8 string or as hex values. 18 | fn get_and_print(key: &[u8], value: &mut [u8]) { 19 | match KeyValue::get(key, value) { 20 | Ok(val_length) => { 21 | let val_length: usize = val_length as usize; 22 | writeln!(Console::writer(), "Got value len: {}", val_length).unwrap(); 23 | 24 | match str::from_utf8(&value[0..val_length]) { 25 | Ok(val_str) => writeln!(Console::writer(), "Value: {}", val_str).unwrap(), 26 | Err(_) => { 27 | write!(Console::writer(), "Value: ").unwrap(); 28 | for i in 0..val_length { 29 | write!(Console::writer(), "{:02x}", value[i]).unwrap(); 30 | } 31 | write!(Console::writer(), "\n").unwrap(); 32 | } 33 | } 34 | } 35 | Err(_) => writeln!(Console::writer(), "error KV::get()",).unwrap(), 36 | } 37 | } 38 | 39 | fn main() { 40 | // Check if there is key-value support on this board. 41 | if KeyValue::exists() { 42 | writeln!(Console::writer(), "KV available!").unwrap() 43 | } else { 44 | writeln!(Console::writer(), "ERR! KV unavailable").unwrap(); 45 | } 46 | 47 | // Do a test query for key: a. 48 | let key = "a"; 49 | let mut value: [u8; 64] = [0; 64]; 50 | get_and_print(key.as_bytes(), &mut value); 51 | 52 | // Now set, get, delete, then get the key: libtock-rs. 53 | let set_key = "libtock-rs"; 54 | let set_val = "kv test app"; 55 | 56 | match KeyValue::set(set_key.as_bytes(), set_val.as_bytes()) { 57 | Ok(()) => { 58 | writeln!(Console::writer(), "Successfully set the key-value").unwrap(); 59 | } 60 | Err(_) => writeln!(Console::writer(), "error KV::set()",).unwrap(), 61 | } 62 | 63 | get_and_print(set_key.as_bytes(), &mut value); 64 | 65 | match KeyValue::delete(set_key.as_bytes()) { 66 | Ok(()) => { 67 | writeln!(Console::writer(), "Successfully deleted the key-value").unwrap(); 68 | } 69 | Err(_) => writeln!(Console::writer(), "error KV::delete()",).unwrap(), 70 | } 71 | 72 | get_and_print(set_key.as_bytes(), &mut value); 73 | } 74 | -------------------------------------------------------------------------------- /examples/leds.rs: -------------------------------------------------------------------------------- 1 | //! A simple libtock-rs example. Just blinks all the LEDs. 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | use libtock::alarm::{Alarm, Milliseconds}; 7 | use libtock::leds::Leds; 8 | use libtock::runtime::{set_main, stack_size}; 9 | 10 | set_main! {main} 11 | stack_size! {0x200} 12 | 13 | fn main() { 14 | if let Ok(leds_count) = Leds::count() { 15 | loop { 16 | for led_index in 0..leds_count { 17 | let _ = Leds::toggle(led_index as u32); 18 | } 19 | Alarm::sleep_for(Milliseconds(250)).unwrap(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/low_level_debug.rs: -------------------------------------------------------------------------------- 1 | //! An extremely simple libtock-rs example. Just prints out a few numbers using 2 | //! the LowLevelDebug capsule then terminates. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use libtock::low_level_debug::LowLevelDebug; 8 | use libtock::runtime::{set_main, stack_size}; 9 | 10 | set_main! {main} 11 | stack_size! {0x100} 12 | 13 | fn main() { 14 | LowLevelDebug::print_1(1); 15 | LowLevelDebug::print_2(2, 3); 16 | } 17 | -------------------------------------------------------------------------------- /examples/music.rs: -------------------------------------------------------------------------------- 1 | //! Implementation done by : https://github.com/teodorobert 2 | //! A simple libtock-rs example. Plays Ode of Joy using the buzzer. 3 | #![no_main] 4 | #![no_std] 5 | 6 | use core::fmt::Write; 7 | use core::time::Duration; 8 | use libtock::buzzer::{Buzzer, Note}; 9 | use libtock::console::Console; 10 | use libtock::runtime::{set_main, stack_size}; 11 | 12 | set_main! {main} 13 | stack_size! {0x800} 14 | 15 | // Adapted from https://github.com/robsoncouto/arduino-songs 16 | 17 | // Notes in the form of (note_frequency, note_delay in musical terms) 18 | const MELODY: [(Note, i32); 62] = [ 19 | (Note::E4, 4), 20 | (Note::E4, 4), 21 | (Note::F4, 4), 22 | (Note::G4, 4), 23 | (Note::G4, 4), 24 | (Note::F4, 4), 25 | (Note::E4, 4), 26 | (Note::D4, 4), 27 | (Note::C4, 4), 28 | (Note::C4, 4), 29 | (Note::D4, 4), 30 | (Note::E4, 4), 31 | (Note::E4, -4), 32 | (Note::D4, 8), 33 | (Note::D4, 2), 34 | (Note::E4, 4), 35 | (Note::E4, 4), 36 | (Note::F4, 4), 37 | (Note::G4, 4), 38 | (Note::G4, 4), 39 | (Note::F4, 4), 40 | (Note::E4, 4), 41 | (Note::D4, 4), 42 | (Note::C4, 4), 43 | (Note::C4, 4), 44 | (Note::D4, 4), 45 | (Note::E4, 4), 46 | (Note::D4, -4), 47 | (Note::C4, 8), 48 | (Note::C4, 2), 49 | (Note::D4, 4), 50 | (Note::D4, 4), 51 | (Note::E4, 4), 52 | (Note::C4, 4), 53 | (Note::D4, 4), 54 | (Note::E4, 8), 55 | (Note::F4, 8), 56 | (Note::E4, 4), 57 | (Note::C4, 4), 58 | (Note::D4, 4), 59 | (Note::E4, 8), 60 | (Note::F4, 8), 61 | (Note::E4, 4), 62 | (Note::D4, 4), 63 | (Note::C4, 4), 64 | (Note::D4, 4), 65 | (Note::G3, 2), 66 | (Note::E4, 4), 67 | (Note::E4, 4), 68 | (Note::F4, 4), 69 | (Note::G4, 4), 70 | (Note::G4, 4), 71 | (Note::F4, 4), 72 | (Note::E4, 4), 73 | (Note::D4, 4), 74 | (Note::C4, 4), 75 | (Note::C4, 4), 76 | (Note::D4, 4), 77 | (Note::E4, 4), 78 | (Note::D4, -4), 79 | (Note::C4, 8), 80 | (Note::C4, 2), 81 | ]; 82 | 83 | const TEMPO: u32 = 114; 84 | const WHOLE_NOTE: u32 = (60000 * 4) / TEMPO; 85 | 86 | fn main() { 87 | if let Err(_) = Buzzer::exists() { 88 | writeln!(Console::writer(), "There is no available buzzer").unwrap(); 89 | return; 90 | } 91 | 92 | writeln!(Console::writer(), "Ode to Joy").unwrap(); 93 | 94 | for (frequency, duration) in MELODY.iter() { 95 | let mut note_duration: Duration = 96 | Duration::from_millis((WHOLE_NOTE / duration.unsigned_abs()) as u64); 97 | // let mut note_duration = WHOLE_NOTE / duration.unsigned_abs(); 98 | if duration < &0 { 99 | note_duration = note_duration * 15 / 10; 100 | } 101 | 102 | let note_duration = note_duration * 9 / 10; 103 | Buzzer::tone_sync(*frequency as u32 * 3, note_duration).unwrap(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /examples/ninedof.rs: -------------------------------------------------------------------------------- 1 | //! Libtock-rs example for the ninedof sensor. 2 | 3 | #![no_main] 4 | #![no_std] 5 | 6 | use core::fmt::Write; 7 | use libtock::console::Console; 8 | 9 | use libtock::alarm::{Alarm, Milliseconds}; 10 | use libtock::ninedof::NineDof; 11 | use libtock::runtime::{set_main, stack_size}; 12 | 13 | set_main! {main} 14 | stack_size! {0x2000} 15 | 16 | fn main() { 17 | if NineDof::exists().is_err() { 18 | writeln!(Console::writer(), "NineDof driver unavailable").unwrap(); 19 | return; 20 | } 21 | 22 | writeln!(Console::writer(), "NineDof driver available").unwrap(); 23 | loop { 24 | let accelerometer_data = NineDof::read_accelerometer_sync(); 25 | let magnetomer_data = NineDof::read_magnetometer_sync(); 26 | let gyroscope_data = NineDof::read_gyroscope_sync(); 27 | 28 | match accelerometer_data { 29 | Ok(data) => { 30 | writeln!( 31 | Console::writer(), 32 | "Accelerometer: x: {}, y: {}, z: {}", 33 | data.x, 34 | data.y, 35 | data.z 36 | ) 37 | .unwrap(); 38 | } 39 | Err(_) => writeln!(Console::writer(), "error while reading accelerometer").unwrap(), 40 | } 41 | 42 | match magnetomer_data { 43 | Ok(data) => { 44 | writeln!( 45 | Console::writer(), 46 | "Magnetometer: x: {}, y: {}, z: {}", 47 | data.x, 48 | data.y, 49 | data.z 50 | ) 51 | .unwrap(); 52 | } 53 | Err(_) => writeln!(Console::writer(), "error while reading magnetometer").unwrap(), 54 | } 55 | 56 | match gyroscope_data { 57 | Ok(data) => { 58 | writeln!( 59 | Console::writer(), 60 | "Gyroscope: x: {}, y: {}, z: {}", 61 | data.x, 62 | data.y, 63 | data.z 64 | ) 65 | .unwrap(); 66 | } 67 | Err(_) => writeln!(Console::writer(), "error while reading gyroscope").unwrap(), 68 | } 69 | Alarm::sleep_for(Milliseconds(700)).unwrap(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/proximity.rs: -------------------------------------------------------------------------------- 1 | //! A simple libtock-rs example. Checks for proximity driver 2 | //! and samples the sensor every 2 seconds. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use core::fmt::Write; 8 | use libtock::console::Console; 9 | 10 | use libtock::alarm::{Alarm, Milliseconds}; 11 | use libtock::proximity::Proximity; 12 | use libtock::runtime::{set_main, stack_size}; 13 | 14 | set_main! {main} 15 | stack_size! {0x200} 16 | 17 | fn main() { 18 | if Proximity::exists().is_err() { 19 | writeln!(Console::writer(), "proximity driver unavailable").unwrap(); 20 | return; 21 | } 22 | writeln!(Console::writer(), "proximity driver available").unwrap(); 23 | loop { 24 | match Proximity::read_sync() { 25 | Ok(prox_val) => writeln!(Console::writer(), "Proximity: {}\n", prox_val).unwrap(), 26 | Err(_) => writeln!(Console::writer(), "error while reading proximity",).unwrap(), 27 | } 28 | 29 | Alarm::sleep_for(Milliseconds(2000)).unwrap(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/rng.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use libtock::alarm::{Alarm, Milliseconds}; 6 | use libtock::console::Console; 7 | use libtock::rng::Rng; 8 | use libtock::runtime::{set_main, stack_size}; 9 | 10 | stack_size! {0x300} 11 | set_main! {main} 12 | 13 | struct Randomness<'a, const N: usize>(&'a [u8; N]); 14 | 15 | impl<'a, const N: usize> core::fmt::Display for Randomness<'a, N> { 16 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 17 | let mut bytes = self.0.iter(); 18 | while let Some(&byte) = bytes.next() { 19 | write!(f, "{byte:02x}")?; 20 | } 21 | Ok(()) 22 | } 23 | } 24 | 25 | fn main() { 26 | if let Err(e) = Rng::exists() { 27 | writeln!(Console::writer(), "RNG DRIVER ERROR: {e:?}").unwrap(); 28 | return; 29 | } 30 | 31 | let mut console_writer = Console::writer(); 32 | let mut buffer: [u8; 32] = Default::default(); 33 | let n: u32 = 32; 34 | 35 | loop { 36 | match Rng::get_bytes_sync(&mut buffer, n) { 37 | Ok(()) => { 38 | let _ = writeln!(console_writer, "Randomness: {}", Randomness(&buffer)); 39 | } 40 | Err(e) => { 41 | let _ = writeln!(console_writer, "Error while getting bytes {e:?}"); 42 | } 43 | } 44 | let _ = Alarm::sleep_for(Milliseconds(2000)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/rng_async.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use core::fmt::Write; 5 | use libtock::alarm::{Alarm, Milliseconds}; 6 | use libtock::rng::RngListener; 7 | use libtock::{console::Console, rng::Rng}; 8 | use libtock_platform::{share, Syscalls}; 9 | use libtock_runtime::{set_main, stack_size, TockSyscalls}; 10 | 11 | stack_size! {0x300} 12 | set_main! {main} 13 | 14 | fn main() { 15 | if let Err(e) = Rng::exists() { 16 | writeln!(Console::writer(), "RNG DRIVER ERROR: {e:?}").unwrap(); 17 | return; 18 | } 19 | 20 | let mut console_writer = Console::writer(); 21 | let rng_listener = RngListener(|_| write!(Console::writer(), "Randomness: ").unwrap()); 22 | let mut buffer: [u8; 32] = Default::default(); 23 | let n: u32 = 32; 24 | 25 | loop { 26 | share::scope(|allow_rw| { 27 | Rng::allow_buffer(&mut buffer, allow_rw).unwrap(); 28 | 29 | share::scope(|subscribe| { 30 | Rng::register_listener(&rng_listener, subscribe).unwrap(); 31 | 32 | Rng::get_bytes_async(n).unwrap(); 33 | TockSyscalls::yield_wait(); 34 | }); 35 | }); 36 | 37 | buffer.iter().for_each(|&byte| { 38 | let _ = write!(console_writer, "{byte:02x}"); 39 | }); 40 | let _ = writeln!(console_writer, ""); 41 | 42 | let _ = Alarm::sleep_for(Milliseconds(2000)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/sound_pressure.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how to use the sound pressure driver. 2 | //! It checks for the sound pressure driver and samples the sensor every second. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use core::fmt::Write; 8 | use libtock::console::Console; 9 | 10 | use libtock::alarm::{Alarm, Milliseconds}; 11 | use libtock::runtime::{set_main, stack_size}; 12 | use libtock::sound_pressure::SoundPressure; 13 | 14 | set_main! {main} 15 | stack_size! {0x200} 16 | 17 | fn main() { 18 | if SoundPressure::exists().is_err() { 19 | writeln!(Console::writer(), "Sound pressure driver not found").unwrap(); 20 | return; 21 | } 22 | 23 | writeln!(Console::writer(), "Sound pressure driver found").unwrap(); 24 | let enable = SoundPressure::enable(); 25 | match enable { 26 | Ok(()) => { 27 | writeln!(Console::writer(), "Sound pressure driver enabled").unwrap(); 28 | loop { 29 | match SoundPressure::read_sync() { 30 | Ok(sound_pressure_val) => writeln!( 31 | Console::writer(), 32 | "Sound Pressure: {}\n", 33 | sound_pressure_val 34 | ) 35 | .unwrap(), 36 | Err(_) => { 37 | writeln!(Console::writer(), "error while reading sound pressure",).unwrap() 38 | } 39 | } 40 | Alarm::sleep_for(Milliseconds(1000)).unwrap(); 41 | } 42 | } 43 | Err(_e) => writeln!(Console::writer(), "Sound pressure driver enable failed",).unwrap(), 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/spi_controller_write_read.rs: -------------------------------------------------------------------------------- 1 | //! This sample demonstrates setting up the SPI controller (assuming board has support) 2 | 3 | #![no_main] 4 | #![no_std] 5 | use core::fmt::Write; 6 | use libtock::console::Console; 7 | use libtock::runtime::{set_main, stack_size}; 8 | use libtock::spi_controller::SpiController; 9 | 10 | set_main! {main} 11 | stack_size! {0x400} 12 | 13 | const OPERATION_LEN: usize = 0x08; 14 | 15 | fn main() { 16 | let tx_buf: [u8; OPERATION_LEN] = [0x12; OPERATION_LEN]; 17 | let mut rx_buf: [u8; OPERATION_LEN] = [0; OPERATION_LEN]; 18 | 19 | writeln!(Console::writer(), "spi-controller: write-read\r").unwrap(); 20 | if let Err(why) = 21 | SpiController::spi_controller_write_read_sync(&tx_buf, &mut rx_buf, OPERATION_LEN as u32) 22 | { 23 | writeln!( 24 | Console::writer(), 25 | "spi-controller: write-read operation failed {:?}\r", 26 | why 27 | ) 28 | .unwrap(); 29 | } else { 30 | writeln!( 31 | Console::writer(), 32 | "spi-controller: write-read: wrote {:x?}: read {:x?}\r", 33 | tx_buf, 34 | rx_buf 35 | ) 36 | .unwrap(); 37 | } 38 | 39 | writeln!(Console::writer(), "spi-controller: write\r").unwrap(); 40 | if let Err(why) = SpiController::spi_controller_write_sync(&tx_buf, OPERATION_LEN as u32) { 41 | writeln!( 42 | Console::writer(), 43 | "spi-controller: write operation failed {:?}\r", 44 | why 45 | ) 46 | .unwrap(); 47 | } else { 48 | writeln!(Console::writer(), "spi-controller: wrote {:x?}\r", tx_buf).unwrap(); 49 | } 50 | 51 | writeln!(Console::writer(), "spi-controller: read\r").unwrap(); 52 | if let Err(why) = SpiController::spi_controller_read_sync(&mut rx_buf, OPERATION_LEN as u32) { 53 | writeln!( 54 | Console::writer(), 55 | "spi-controller: read operation failed {:?}\r", 56 | why 57 | ) 58 | .unwrap(); 59 | } else { 60 | writeln!(Console::writer(), "spi-controller: read {:x?}\r", rx_buf).unwrap(); 61 | } 62 | 63 | writeln!(Console::writer(), "spi-controller: inplace write-read\r").unwrap(); 64 | if let Err(why) = 65 | SpiController::spi_controller_inplace_write_read_sync(&mut rx_buf, OPERATION_LEN as u32) 66 | { 67 | writeln!( 68 | Console::writer(), 69 | "spi-controller: inplace write-read operation failed {:?}\r", 70 | why 71 | ) 72 | .unwrap(); 73 | } else { 74 | writeln!( 75 | Console::writer(), 76 | "spi-controller: inplace write-read: wrote {:x?}: read {:x?}\r", 77 | tx_buf, 78 | rx_buf 79 | ) 80 | .unwrap(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /examples/temperature.rs: -------------------------------------------------------------------------------- 1 | //! A simple libtock-rs example. Checks for temperature driver 2 | //! and samples the sensor every 2 seconds. 3 | 4 | #![no_main] 5 | #![no_std] 6 | 7 | use core::fmt::Write; 8 | use libtock::console::Console; 9 | 10 | use libtock::alarm::{Alarm, Milliseconds}; 11 | use libtock::runtime::{set_main, stack_size}; 12 | use libtock::temperature::Temperature; 13 | 14 | set_main! {main} 15 | stack_size! {0x200} 16 | 17 | fn main() { 18 | match Temperature::exists() { 19 | Ok(()) => writeln!(Console::writer(), "temperature driver available").unwrap(), 20 | Err(_) => { 21 | writeln!(Console::writer(), "temperature driver unavailable").unwrap(); 22 | return; 23 | } 24 | } 25 | 26 | loop { 27 | match Temperature::read_temperature_sync() { 28 | Ok(temp_val) => writeln!( 29 | Console::writer(), 30 | "Temperature: {}{}.{}*C\n", 31 | if temp_val > 0 { "" } else { "-" }, 32 | i32::abs(temp_val) / 100, 33 | i32::abs(temp_val) % 100 34 | ) 35 | .unwrap(), 36 | Err(_) => writeln!(Console::writer(), "error while reading temperature",).unwrap(), 37 | } 38 | 39 | Alarm::sleep_for(Milliseconds(2000)).unwrap(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /nightly/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | # This is the nightly Rust toolchain used by `make test`. 2 | [toolchain] 3 | channel = "nightly-2024-11-11" 4 | components = ["miri", "rust-src"] 5 | -------------------------------------------------------------------------------- /panic_handlers/debug_panic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_debug_panic" 3 | authors = ["Tock Project Developers "] 4 | version = "0.1.0" 5 | description = """Debug panic handler for libtock. Prints panic info to console and terminates.""" 6 | edition = "2021" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://www.github.com/tock/libtock-rs" 9 | rust-version.workspace = true 10 | 11 | [dependencies] 12 | libtock_console = { path = "../../apis/interface/console" } 13 | libtock_low_level_debug = { path = "../../apis/kernel/low_level_debug" } 14 | libtock_platform = { path = "../../platform" } 15 | libtock_runtime = { path = "../../runtime" } 16 | -------------------------------------------------------------------------------- /panic_handlers/debug_panic/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use core::fmt::Write; 3 | use libtock_console::Console; 4 | use libtock_low_level_debug::{AlertCode, LowLevelDebug}; 5 | use libtock_platform::{ErrorCode, Syscalls}; 6 | use libtock_runtime::TockSyscalls; 7 | 8 | /// This handler requires some 0x400 bytes of stack 9 | 10 | #[panic_handler] 11 | fn panic_handler(info: &core::panic::PanicInfo) -> ! { 12 | // Signal a panic using the LowLevelDebug capsule (if available). 13 | LowLevelDebug::::print_alert_code(AlertCode::Panic); 14 | 15 | let mut writer = Console::::writer(); 16 | // If this printing fails, we can't panic harder, and we can't print it either. 17 | let _ = writeln!(writer, "{}", info); 18 | // Exit with a non-zero exit code to indicate failure. 19 | TockSyscalls::exit_terminate(ErrorCode::Fail as u32); 20 | } 21 | -------------------------------------------------------------------------------- /panic_handlers/small_panic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libtock_small_panic" 3 | authors = ["Tock Project Developers "] 4 | version = "0.1.0" 5 | description = """Small and basic panic handler for libtock. Calls low-level debug on panic and terminates.""" 6 | edition = "2021" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://www.github.com/tock/libtock-rs" 9 | rust-version.workspace = true 10 | 11 | [dependencies] 12 | libtock_low_level_debug = { path = "../../apis/kernel/low_level_debug" } 13 | libtock_platform = { path = "../../platform" } 14 | libtock_runtime = { path = "../../runtime" } 15 | -------------------------------------------------------------------------------- /panic_handlers/small_panic/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use libtock_low_level_debug::{AlertCode, LowLevelDebug}; 4 | use libtock_platform::{ErrorCode, Syscalls}; 5 | use libtock_runtime::TockSyscalls; 6 | 7 | #[panic_handler] 8 | fn panic_handler(_info: &core::panic::PanicInfo) -> ! { 9 | // Signal a panic using the LowLevelDebug capsule (if available). 10 | LowLevelDebug::::print_alert_code(AlertCode::Panic); 11 | 12 | // Exit with a non-zero exit code to indicate failure. 13 | // TODO(kupiakos@google.com): Make this logic consistent with tock/tock#2914 14 | // when it is merged. 15 | TockSyscalls::exit_terminate(ErrorCode::Fail as u32); 16 | } 17 | -------------------------------------------------------------------------------- /platform/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tock Project Developers "] 3 | categories = ["embedded", "no-std", "os"] 4 | description = """libtock-rs platform layer. Provides the Platform abstraction, 5 | an abstraction that extends Tock's system calls to form the 6 | basis for libtock-rs' asynchronous APIs. libtock_platform is 7 | intended for use in both TBF binaries and unit tests that run 8 | on Linux.""" 9 | edition = "2021" 10 | license = "Apache-2.0 OR MIT" 11 | name = "libtock_platform" 12 | repository = "https://www.github.com/tock/libtock/rs" 13 | rust-version.workspace = true 14 | version = "0.1.0" 15 | 16 | [features] 17 | rust_embedded = ["embedded-hal"] 18 | 19 | [dependencies] 20 | embedded-hal = { version = "1.0", optional = true } 21 | -------------------------------------------------------------------------------- /platform/src/constants.rs: -------------------------------------------------------------------------------- 1 | //! Defines constants shared between multiple `libtock-rs` crates. 2 | 3 | pub mod exit_id { 4 | pub const TERMINATE: u32 = 0; 5 | pub const RESTART: u32 = 1; 6 | } 7 | 8 | pub mod syscall_class { 9 | pub const SUBSCRIBE: usize = 1; 10 | pub const COMMAND: usize = 2; 11 | pub const ALLOW_RW: usize = 3; 12 | pub const ALLOW_RO: usize = 4; 13 | pub const MEMOP: usize = 5; 14 | pub const EXIT: usize = 6; 15 | } 16 | 17 | pub mod yield_id { 18 | pub const NO_WAIT: u32 = 0; 19 | pub const WAIT: u32 = 1; 20 | } 21 | -------------------------------------------------------------------------------- /platform/src/default_config.rs: -------------------------------------------------------------------------------- 1 | /// A general purpose syscall configuration, which drivers should use as their 2 | /// default syscall config. 3 | pub struct DefaultConfig; 4 | 5 | impl crate::allow_ro::Config for DefaultConfig {} 6 | impl crate::allow_rw::Config for DefaultConfig {} 7 | impl crate::subscribe::Config for DefaultConfig {} 8 | -------------------------------------------------------------------------------- /platform/src/error_code_tests.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryInto; 2 | 3 | use crate::{error_code::NotAnErrorCode, ErrorCode}; 4 | 5 | // Verifies that `ErrorCode` represents every valid value in the range 6 | // [1, 1024]. 7 | #[cfg(miri)] 8 | #[test] 9 | fn error_code_range() { 10 | for value in 1..=1024u32 { 11 | let _ = unsafe { *(&value as *const u32 as *const ErrorCode) }; 12 | } 13 | } 14 | 15 | #[test] 16 | fn error_code_try_into() { 17 | assert_eq!(TryInto::::try_into(0u32), Err(NotAnErrorCode)); 18 | for value in 1..=1024u32 { 19 | assert_eq!(value.try_into().map(|e: ErrorCode| e as u32), Ok(value)); 20 | } 21 | assert_eq!(TryInto::::try_into(1025u32), Err(NotAnErrorCode)); 22 | } 23 | -------------------------------------------------------------------------------- /platform/src/exit_on_drop.rs: -------------------------------------------------------------------------------- 1 | /// Calls the Exit system call when dropped. Used to catch panic unwinding from 2 | /// a `#![no_std]` context. The caller should `core::mem::forget` the 3 | /// `ExitOnDrop` when it no longer needs to catch unwinding. 4 | /// 5 | /// 6 | /// # Example 7 | /// ``` 8 | /// use libtock_platform::exit_on_drop::ExitOnDrop; 9 | /// fn function_that_must_not_unwind() { 10 | /// let exit_on_drop: ExitOnDrop:: = Default::default(); 11 | /// /* Do something that might unwind here. */ 12 | /// core::mem::forget(exit_on_drop); 13 | /// } 14 | /// ``` 15 | pub struct ExitOnDrop(core::marker::PhantomData); 16 | 17 | impl Default for ExitOnDrop { 18 | fn default() -> ExitOnDrop { 19 | ExitOnDrop(core::marker::PhantomData) 20 | } 21 | } 22 | 23 | impl Drop for ExitOnDrop { 24 | fn drop(&mut self) { 25 | S::exit_terminate(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /platform/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![warn(unsafe_op_in_unsafe_fn)] 3 | 4 | pub mod allow_ro; 5 | pub mod allow_rw; 6 | pub mod command_return; 7 | mod constants; 8 | mod default_config; 9 | mod error_code; 10 | pub mod exit_on_drop; 11 | mod raw_syscalls; 12 | mod register; 13 | pub mod return_variant; 14 | pub mod share; 15 | pub mod subscribe; 16 | mod syscalls; 17 | mod syscalls_impl; 18 | mod termination; 19 | mod yield_types; 20 | 21 | pub use allow_ro::AllowRo; 22 | pub use allow_rw::AllowRw; 23 | pub use command_return::CommandReturn; 24 | pub use constants::{exit_id, syscall_class, yield_id}; 25 | pub use default_config::DefaultConfig; 26 | pub use error_code::ErrorCode; 27 | pub use raw_syscalls::RawSyscalls; 28 | pub use register::Register; 29 | pub use return_variant::ReturnVariant; 30 | pub use subscribe::{Subscribe, Upcall}; 31 | pub use syscalls::Syscalls; 32 | pub use termination::Termination; 33 | pub use yield_types::YieldNoWaitReturn; 34 | 35 | #[cfg(test)] 36 | mod command_return_tests; 37 | 38 | #[cfg(test)] 39 | mod error_code_tests; 40 | -------------------------------------------------------------------------------- /platform/src/return_variant.rs: -------------------------------------------------------------------------------- 1 | // TODO: Re-evaluate whether u32 is the correct type to wrap. Maybe it should 2 | // wrap a Register instead? Also, double-check that ReturnVariant is providing 3 | // useful type-safety. 4 | 5 | /// `ReturnVariant` describes what value type the kernel has returned. 6 | // ReturnVariant is not an enum so that it can be converted from a u32 for free. 7 | // TODO: Add a ufmt debug implementation for use by process binaries. 8 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 9 | pub struct ReturnVariant(u32); 10 | 11 | impl From for ReturnVariant { 12 | fn from(value: u32) -> ReturnVariant { 13 | ReturnVariant(value) 14 | } 15 | } 16 | 17 | impl From for crate::Register { 18 | fn from(return_variant: ReturnVariant) -> crate::Register { 19 | return_variant.0.into() 20 | } 21 | } 22 | 23 | impl From for u32 { 24 | fn from(return_variant: ReturnVariant) -> u32 { 25 | return_variant.0 26 | } 27 | } 28 | 29 | pub const FAILURE: ReturnVariant = ReturnVariant(0); 30 | pub const FAILURE_U32: ReturnVariant = ReturnVariant(1); 31 | pub const FAILURE_2_U32: ReturnVariant = ReturnVariant(2); 32 | pub const FAILURE_U64: ReturnVariant = ReturnVariant(3); 33 | pub const SUCCESS: ReturnVariant = ReturnVariant(128); 34 | pub const SUCCESS_U32: ReturnVariant = ReturnVariant(129); 35 | pub const SUCCESS_2_U32: ReturnVariant = ReturnVariant(130); 36 | pub const SUCCESS_U64: ReturnVariant = ReturnVariant(131); 37 | pub const SUCCESS_3_U32: ReturnVariant = ReturnVariant(132); 38 | pub const SUCCESS_U32_U64: ReturnVariant = ReturnVariant(133); 39 | -------------------------------------------------------------------------------- /platform/src/share/mod.rs: -------------------------------------------------------------------------------- 1 | //! `share` contains tools for safely sharing objects (such as buffers and 2 | //! upcalls) with the Tock kernel. 3 | 4 | mod handle; 5 | mod tuple_impls; 6 | 7 | pub use handle::{Handle, SplittableHandle}; 8 | 9 | /// Creates a scope in which objects may safely be shared with the kernel. 10 | pub fn scope) -> Output>(fcn: F) -> Output { 11 | let list = Default::default(); 12 | // Safety: We do not move the L out of the `list` variable. The `list` 13 | // variable will be dropped at the end of the scope, immediately before the 14 | // L becomes invalid. 15 | fcn(unsafe { Handle::new(&list) }) 16 | } 17 | 18 | /// A list of objects that may be shared with the kernel. `List` is implemented 19 | /// for system call types such as `Subscribe`, as well as (potentially-nested) 20 | /// tuples of such types. 21 | pub trait List: Default {} 22 | 23 | #[cfg(test)] 24 | mod tests; 25 | -------------------------------------------------------------------------------- /platform/src/share/tuple_impls.rs: -------------------------------------------------------------------------------- 1 | /// Implements `share::List` and `share::SplittableHandle` on tuples of various 2 | /// sizes. 3 | use crate::share::{Handle, List, SplittableHandle}; 4 | 5 | // Implement List and SplittableHandle on empty tuples, because tuple_impl only 6 | // works for tuples of size 1 or greater. Empty lists may be useful in generic 7 | // and/or macro contexts. 8 | impl List for () {} 9 | 10 | impl<'handle> SplittableHandle<'handle> for () { 11 | type SplitHandles = (); 12 | 13 | fn split(_handle: Handle<'handle, ()>) {} 14 | } 15 | 16 | // Provides `share::List` and `share::SplittableHandle` impls for tuples of a 17 | // specific size. tuple_impls! must be provided with a list of names, and will 18 | // generate impls for tuples of the given length. 19 | macro_rules! tuple_impls { 20 | ($($name:ident),*) => { 21 | impl<$($name: List),*> List for ($($name),*,) {} 22 | 23 | impl<'handle, $($name: List + 'handle),*> SplittableHandle<'handle> for ($($name),*,) { 24 | type SplitHandles = ($(Handle<'handle, $name>),*,); 25 | 26 | fn split(handle: Handle<'handle, Self>) -> Self::SplitHandles { 27 | // Safety: handle guarantees that an instance of Self exists and 28 | // will be cleaned up before it becomes invalid. Self is a 29 | // tuple, and the types we are changing handle into are elements 30 | // of that tuple, so when the tuple is cleaned up they will be 31 | // cleaned up as well. 32 | ($(unsafe { handle.change_type::<$name>() }),*,) 33 | } 34 | } 35 | } 36 | } 37 | 38 | // Recursively calls tuple_impls for all tuples of a given length or shorter 39 | // (except the empty tuple, which tuple_impls doesn't support). 40 | macro_rules! impl_recursive { 41 | // Base case: if provided no names, do nothing. 42 | () => {}; 43 | 44 | // Recursive case. Calls tuple_impls, then call ourselves with one less 45 | // name. 46 | ($head_name:ident$(, $($tail_names:ident),*)?) => { 47 | tuple_impls!($head_name$(, $($tail_names),*)?); 48 | impl_recursive!($($($tail_names),*)?); 49 | }; 50 | } 51 | 52 | // Because List depends on Default, which is only implemented for tuples of <= 53 | // 12 elements, we can only implement List for tuples of up to length 12. 54 | impl_recursive!(A, B, C, D, E, F, G, H, I, J, K, L); 55 | -------------------------------------------------------------------------------- /platform/src/termination.rs: -------------------------------------------------------------------------------- 1 | //! Definition of the Termination trait. The main() function (set using set_main!()) 2 | //! must return a type that implements Termination. 3 | 4 | use crate::{ErrorCode, Syscalls}; 5 | 6 | pub trait Termination { 7 | fn complete(self) -> !; 8 | } 9 | 10 | impl Termination for () { 11 | fn complete(self) -> ! { 12 | S::exit_terminate(0) 13 | } 14 | } 15 | 16 | impl Termination for Result<(), ErrorCode> { 17 | fn complete(self) -> ! { 18 | let exit_code = match self { 19 | Ok(()) => 0, 20 | Err(ec) => ec as u32, 21 | }; 22 | S::exit_terminate(exit_code); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /platform/src/yield_types.rs: -------------------------------------------------------------------------------- 1 | /// The return value from a yield_no_wait call. 2 | // Calling yield-no-wait passes a *mut YieldNoWaitReturn to the kernel, which 3 | // the kernel writes to. We cannot safely pass a `*mut bool` to the kernel, 4 | // because the representation of `bool` in Rust is undefined (although it is 5 | // likely false == 0, true == 1, based on `bool`'s conversions). Using *mut 6 | // YieldNoWaitReturn rather than a *mut u8 allows the compiler to assume the 7 | // kernel will never write a value other than 0 or 1 into the pointee. Assuming 8 | // the likely representation of `bool`, this makes the conversion into `bool` 9 | // free. 10 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 11 | #[repr(u8)] 12 | pub enum YieldNoWaitReturn { 13 | NoUpcall = 0, 14 | Upcall = 1, 15 | } 16 | -------------------------------------------------------------------------------- /runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tock Project Developers "] 3 | description = """Tool used to run libtock-rs process binaries.""" 4 | edition = "2021" 5 | license = "Apache-2.0 OR MIT" 6 | name = "runner" 7 | publish = false 8 | repository = "https://www.github.com/tock/libtock-rs" 9 | rust-version.workspace = true 10 | version = "0.1.0" 11 | 12 | [dependencies] 13 | clap = { features = ["derive"], version = "3.2.6" } 14 | elf = "0.0.10" 15 | libc = "0.2.113" 16 | termion = "1.5.6" 17 | -------------------------------------------------------------------------------- /runner/src/main.rs: -------------------------------------------------------------------------------- 1 | mod elf2tab; 2 | mod output_processor; 3 | mod qemu; 4 | mod tockloader; 5 | 6 | use clap::{Parser, ValueEnum}; 7 | use std::env::{var, VarError}; 8 | use std::path::PathBuf; 9 | 10 | /// Converts ELF binaries into Tock Binary Format binaries and runs them on a 11 | /// Tock system. 12 | #[derive(Debug, Parser)] 13 | pub struct Cli { 14 | /// Where to deploy the process binary. If not specified, runner will only 15 | /// make a TBF file and not attempt to run it. 16 | #[clap(action, long, short, value_enum)] 17 | deploy: Option, 18 | 19 | /// The executable to convert into Tock Binary Format and run. 20 | #[clap(action)] 21 | elf: PathBuf, 22 | 23 | /// Whether to output verbose debugging information to the console. 24 | #[clap(long, short, action)] 25 | verbose: bool, 26 | } 27 | 28 | #[derive(ValueEnum, Clone, Copy, Debug)] 29 | pub enum Deploy { 30 | Qemu, 31 | Tockloader, 32 | } 33 | 34 | fn main() { 35 | let cli = Cli::parse(); 36 | let platform = match var("LIBTOCK_PLATFORM") { 37 | Err(VarError::NotPresent) => { 38 | panic!("LIBTOCK_PLATFORM must be specified to deploy") 39 | } 40 | Err(VarError::NotUnicode(platform)) => { 41 | panic!("Non-UTF-8 LIBTOCK_PLATFORM value: {:?}", platform) 42 | } 43 | Ok(platform) => platform, 44 | }; 45 | if cli.verbose { 46 | println!("Detected platform {}", platform); 47 | } 48 | let paths = elf2tab::convert_elf(&cli, &platform); 49 | let deploy = match cli.deploy { 50 | None => return, 51 | Some(deploy) => deploy, 52 | }; 53 | let child = match deploy { 54 | Deploy::Qemu => qemu::deploy(&cli, platform, paths.tbf_path), 55 | Deploy::Tockloader => tockloader::deploy(&cli, platform, paths.tab_path), 56 | }; 57 | output_processor::process(&cli, child); 58 | } 59 | -------------------------------------------------------------------------------- /runner/src/qemu.rs: -------------------------------------------------------------------------------- 1 | use super::Cli; 2 | use std::path::PathBuf; 3 | use std::process::{Child, Command, Stdio}; 4 | 5 | // Spawns a QEMU VM with a simulated Tock system and the process binary. Returns 6 | // the handle for the spawned QEMU process. 7 | pub fn deploy(cli: &Cli, platform: String, tbf_path: PathBuf) -> Child { 8 | let platform_args = get_platform_args(platform); 9 | let device = format!( 10 | "loader,file={},addr={}", 11 | tbf_path 12 | .into_os_string() 13 | .into_string() 14 | .expect("Non-UTF-8 path"), 15 | platform_args.process_binary_load_address, 16 | ); 17 | let mut qemu = Command::new("tock/tools/qemu/build/qemu-system-riscv32"); 18 | qemu.args(["-device", &device, "-nographic", "-serial", "mon:stdio"]); 19 | qemu.args(platform_args.fixed_args); 20 | // If we let QEMU inherit its stdin from us, it will set it to raw mode, 21 | // which prevents Ctrl+C from generating SIGINT. QEMU will not exit when 22 | // Ctrl+C is entered, making our runner hard to close. Instead, we forward 23 | // stdin to QEMU ourselves -- see output_processor.rs for more details. 24 | qemu.stdin(Stdio::piped()); 25 | qemu.stdout(Stdio::piped()); 26 | // Because we set the terminal to raw mode while running QEMU, but QEMU's 27 | // stdin is not connected to a terminal, QEMU does not know it needs to use 28 | // CRLF line endings when printing to stderr. To convert, we also pipe 29 | // QEMU's stderr through us and output_processor converts the line endings. 30 | qemu.stderr(Stdio::piped()); 31 | if cli.verbose { 32 | println!("QEMU command: {:?}", qemu); 33 | println!("Spawning QEMU") 34 | } 35 | qemu.spawn().expect("failed to spawn QEMU") 36 | } 37 | 38 | // Returns the command line arguments for the given platform to qemu. Panics if 39 | // an unknown platform is passed. 40 | fn get_platform_args(platform: String) -> PlatformConfig { 41 | match platform.as_str() { 42 | "hifive1" => PlatformConfig { 43 | #[rustfmt::skip] 44 | fixed_args: &[ 45 | "-kernel", "tock/target/riscv32imac-unknown-none-elf/release/hifive1", 46 | "-M", "sifive_e,revb=true", 47 | ], 48 | process_binary_load_address: "0x20040000", 49 | }, 50 | "opentitan" => PlatformConfig { 51 | #[rustfmt::skip] 52 | fixed_args: &[ 53 | "-bios", "tock/tools/qemu-runner/opentitan-boot-rom.elf", 54 | "-kernel", "tock/target/riscv32imc-unknown-none-elf/release/earlgrey-cw310", 55 | "-M", "opentitan", 56 | ], 57 | process_binary_load_address: "0x20030000", 58 | }, 59 | _ => panic!("Cannot deploy to platform {} via QEMU.", platform), 60 | } 61 | } 62 | 63 | // QEMU configuration information that is specific to each platform. 64 | struct PlatformConfig { 65 | fixed_args: &'static [&'static str], 66 | process_binary_load_address: &'static str, 67 | } 68 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tock Project Developers "] 3 | categories = ["embedded", "no-std", "os"] 4 | description = """libtock-rs runtime. Provides raw system call implementations \ 5 | and language items necessary for Tock apps.""" 6 | edition = "2021" 7 | license = "Apache-2.0 OR MIT" 8 | name = "libtock_runtime" 9 | repository = "https://www.github.com/tock/libtock-rs" 10 | rust-version.workspace = true 11 | version = "0.1.0" 12 | 13 | [dependencies] 14 | libtock_platform = { path = "../platform" } 15 | 16 | [features] 17 | 18 | # By default, libtock_runtime calls Memop to tell the Tock kernel where the 19 | # stack and heap begin. The kernel uses those addresses to specify the stack and 20 | # heap address ranges if the process faults. Those calls cost 22 bytes on ARM 21 | # and 28 bytes on RISC-V. To remove them (for the purpose of minimizing code 22 | # size), enable the no_debug_memop feature. 23 | no_debug_memop = [] 24 | -------------------------------------------------------------------------------- /runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `libtock_runtime` provides the runtime for Tock process binaries written in 2 | //! Rust as well as interfaces to Tock's system calls. 3 | //! 4 | //! `libtock_runtime` is designed for statically-compiled binaries, and needs to 5 | //! know the location (in non-volatile memory and RAM) at which the process will 6 | //! execute. It reads the `LIBTOCK_PLATFORM` variable to determine what location 7 | //! to build for (see the `layouts/` directory to see what platforms are 8 | //! available). It expects the following cargo config options to be set (e.g. in 9 | //! `.cargo/config.toml`): 10 | //! ``` 11 | //! [build] 12 | //! rustflags = [ 13 | //! "-C", "relocation-model=static", 14 | //! "-C", "link-arg=-Tlayout.ld", 15 | //! ] 16 | //! ``` 17 | //! If a process binary wants to support another platform, it can set the 18 | //! `no_auto_layout` feature on `libtock_runtime` to disable this functionality 19 | //! and provide its own layout file. 20 | 21 | #![no_std] 22 | #![warn(unsafe_op_in_unsafe_fn)] 23 | 24 | pub mod startup; 25 | 26 | /// TockSyscalls implements `libtock_platform::Syscalls`. 27 | pub struct TockSyscalls; 28 | 29 | #[cfg(target_arch = "arm")] 30 | mod syscalls_impl_arm; 31 | #[cfg(target_arch = "riscv32")] 32 | mod syscalls_impl_riscv; 33 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | # This is libtock-rs' Minimum Supported Rust Version (MSRV). 3 | # 4 | # Update policy: Update this if doing so allows you to use a Rust feature that 5 | # you'd like to use. When you do so, update this to the first Rust version that 6 | # includes that feature. Whenever this value is updated, the rust-version field 7 | # in Cargo.toml must be updated as well. 8 | channel = "1.77" 9 | components = ["clippy", "rustfmt"] 10 | targets = [ 11 | "thumbv6m-none-eabi", 12 | "thumbv7em-none-eabi", 13 | "thumbv8m.main-none-eabi", 14 | "riscv32imac-unknown-none-elf", 15 | "riscv32imc-unknown-none-elf", 16 | ] 17 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_field_init_shorthand = true 2 | -------------------------------------------------------------------------------- /src/spi_controller.rs: -------------------------------------------------------------------------------- 1 | use crate::alarm::{Alarm, Milliseconds}; 2 | use crate::platform::ErrorCode; 3 | use libtock_spi_controller as spi_controller; 4 | 5 | pub type SpiController = spi_controller::SpiController; 6 | 7 | pub struct EmbeddedHalSpi; 8 | 9 | impl embedded_hal::spi::ErrorType for EmbeddedHalSpi { 10 | type Error = ErrorCode; 11 | } 12 | 13 | impl embedded_hal::spi::SpiDevice for EmbeddedHalSpi { 14 | fn transaction( 15 | &mut self, 16 | operations: &mut [embedded_hal::spi::Operation<'_, u8>], 17 | ) -> Result<(), Self::Error> { 18 | for operation in operations { 19 | match operation { 20 | embedded_hal::spi::Operation::Read(read_buf) => { 21 | SpiController::spi_controller_read_sync(read_buf, read_buf.len() as u32)? 22 | } 23 | embedded_hal::spi::Operation::Write(write_buf) => { 24 | // writeln!(Console::writer(), "Write: write_buf: {:x?}\r", write_buf).unwrap(); 25 | SpiController::spi_controller_write_sync(write_buf, write_buf.len() as u32) 26 | .unwrap(); 27 | } 28 | embedded_hal::spi::Operation::Transfer(read_buf, write_buf) => { 29 | // writeln!(Console::writer(), "Transfer: write_buf: {:x?}\r", write_buf).unwrap(); 30 | SpiController::spi_controller_write_read_sync( 31 | write_buf, 32 | read_buf, 33 | write_buf.len() as u32, 34 | )? 35 | } 36 | embedded_hal::spi::Operation::TransferInPlace(read_write_buf) => { 37 | // writeln!(Console::writer(), "TransferInPlace: read_write_buf: {:x?}\r", read_write_buf).unwrap(); 38 | SpiController::spi_controller_inplace_write_read_sync( 39 | read_write_buf, 40 | read_write_buf.len() as u32, 41 | )? 42 | } 43 | embedded_hal::spi::Operation::DelayNs(time) => { 44 | Alarm::sleep_for(Milliseconds(*time / 1000)).unwrap(); 45 | } 46 | } 47 | } 48 | 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /syscalls_tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "syscalls_tests" 4 | rust-version.workspace = true 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | libtock_platform = { path = "../platform" } 9 | libtock_unittest = { path = "../unittest" } 10 | -------------------------------------------------------------------------------- /syscalls_tests/src/command_tests.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the Command system call implementation in 2 | //! `libtock_platform::Syscalls`. 3 | 4 | use libtock_platform::Syscalls; 5 | use libtock_unittest::{command_return, fake, ExpectedSyscall, SyscallLogEntry}; 6 | 7 | #[test] 8 | fn command() { 9 | let kernel = fake::Kernel::new(); 10 | kernel.add_expected_syscall(ExpectedSyscall::Command { 11 | driver_id: 1, 12 | command_id: 2, 13 | argument0: 3, 14 | argument1: 4, 15 | override_return: Some(command_return::success_3_u32(1, 2, 3)), 16 | }); 17 | assert_eq!( 18 | fake::Syscalls::command(1, 2, 3, 4).get_success_3_u32(), 19 | Some((1, 2, 3)) 20 | ); 21 | assert_eq!( 22 | kernel.take_syscall_log(), 23 | [SyscallLogEntry::Command { 24 | driver_id: 1, 25 | command_id: 2, 26 | argument0: 3, 27 | argument1: 4, 28 | }] 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /syscalls_tests/src/exit_on_drop.rs: -------------------------------------------------------------------------------- 1 | use libtock_platform::exit_on_drop::ExitOnDrop; 2 | use libtock_unittest::fake; 3 | 4 | // Unwinds if `unwind` is true, otherwise just returns. 5 | fn maybe_unwind(unwind: bool) { 6 | if unwind { 7 | panic!("Triggering stack unwinding."); 8 | } 9 | } 10 | 11 | #[cfg(not(miri))] 12 | #[test] 13 | fn exit() { 14 | let exit = libtock_unittest::exit_test("exit_on_drop::exit", || { 15 | let exit_on_drop: ExitOnDrop = Default::default(); 16 | maybe_unwind(true); 17 | core::mem::forget(exit_on_drop); 18 | }); 19 | assert_eq!(exit, libtock_unittest::ExitCall::Terminate(0)); 20 | } 21 | 22 | #[test] 23 | fn no_exit() { 24 | let exit_on_drop: ExitOnDrop = Default::default(); 25 | maybe_unwind(false); 26 | core::mem::forget(exit_on_drop); 27 | } 28 | -------------------------------------------------------------------------------- /syscalls_tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate contains tests for `libtock_platform` functionality that use the 2 | //! `Syscalls` implementation. 3 | //! 4 | //! These tests are not in `libtock_platform` because adding them to 5 | //! `libtock_platform causes two incompatible copies of `libtock_platform` to be 6 | //! compiled: 7 | //! 1. The `libtock_platform` with `cfg(test)` enabled 8 | //! 2. The `libtock_platform` that `libtock_unittest` depends on, which has 9 | //! `cfg(test)` disabled. 10 | //! Mixing types from the two `libtock_platform` instantiations in tests results 11 | //! in confusing error messages, so instead those tests live in this crate. 12 | 13 | #[cfg(test)] 14 | mod allow_ro; 15 | 16 | #[cfg(test)] 17 | mod allow_rw; 18 | 19 | #[cfg(test)] 20 | mod command_tests; 21 | 22 | #[cfg(test)] 23 | mod exit_on_drop; 24 | 25 | // TODO: Add Exit. 26 | 27 | #[cfg(test)] 28 | mod memop_tests; 29 | 30 | #[cfg(test)] 31 | mod subscribe_tests; 32 | 33 | #[cfg(test)] 34 | mod yield_tests; 35 | -------------------------------------------------------------------------------- /syscalls_tests/src/yield_tests.rs: -------------------------------------------------------------------------------- 1 | //! Tests for implementations of Yield system calls in 2 | //! `libtock_platform::Syscalls`. 3 | 4 | use libtock_platform::{Syscalls, YieldNoWaitReturn}; 5 | use libtock_unittest::{fake, ExpectedSyscall, SyscallLogEntry}; 6 | 7 | // Tests yield_no_wait with an upcall executed. 8 | #[test] 9 | fn no_wait_upcall() { 10 | use YieldNoWaitReturn::Upcall; 11 | let kernel = fake::Kernel::new(); 12 | kernel.add_expected_syscall(ExpectedSyscall::YieldNoWait { 13 | override_return: Some(Upcall), 14 | }); 15 | assert_eq!(fake::Syscalls::yield_no_wait(), Upcall); 16 | assert_eq!(kernel.take_syscall_log(), [SyscallLogEntry::YieldNoWait]); 17 | } 18 | 19 | // Tests yield_no_wait with no upcall executed. 20 | #[test] 21 | fn no_wait_no_upcall() { 22 | use YieldNoWaitReturn::NoUpcall; 23 | let kernel = fake::Kernel::new(); 24 | kernel.add_expected_syscall(ExpectedSyscall::YieldNoWait { 25 | override_return: Some(NoUpcall), 26 | }); 27 | assert_eq!(fake::Syscalls::yield_no_wait(), NoUpcall); 28 | assert_eq!(kernel.take_syscall_log(), [SyscallLogEntry::YieldNoWait]); 29 | } 30 | 31 | // Tests yield_wait. 32 | #[test] 33 | fn wait() { 34 | let kernel = fake::Kernel::new(); 35 | kernel.add_expected_syscall(ExpectedSyscall::YieldWait { skip_upcall: true }); 36 | fake::Syscalls::yield_wait(); 37 | assert_eq!(kernel.take_syscall_log(), [SyscallLogEntry::YieldWait]); 38 | } 39 | -------------------------------------------------------------------------------- /tools/print_sizes/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Finds all the libtock_core and libtock examples and prints the sizes of 2 | # several of their sections. Searches the target/$ARCH/release directory. Note 3 | # that print_sizes will not build the examples; that is done by the 4 | # `print-sizes` Makefile action. 5 | 6 | [package] 7 | authors = ["Tock Project Developers "] 8 | edition = "2018" 9 | name = "print_sizes" 10 | rust-version.workspace = true 11 | version = "0.1.0" 12 | 13 | [dependencies] 14 | elf = "0.0.10" 15 | -------------------------------------------------------------------------------- /ufmt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | categories = ["embedded", "no-std"] 4 | description = "A (6-40x) smaller, (2-9x) faster and panic-free alternative to `core::fmt`" 5 | documentation = "https://docs.rs/ufmt" 6 | edition = "2021" 7 | keywords = ["Debug", "Display", "Write", "format"] 8 | license = "MIT OR Apache-2.0" 9 | name = "ufmt" 10 | readme = "README.md" 11 | repository = "https://github.com/japaric/ufmt" 12 | version = "0.1.0" 13 | 14 | [dependencies] 15 | proc-macro-hack = "0.5.11" 16 | ufmt-macros = { path = "macros", version = "0.1.0" } 17 | ufmt-write = { path = "write", version = "0.1.0" } 18 | 19 | # NOTE do NOT add an `alloc` feature before the alloc crate can be used in 20 | # no-std BINARIES 21 | [features] 22 | # NOTE do NOT turn `std` into a default feature; this is a no-std first crate 23 | std = ["ufmt-write/std"] 24 | 25 | [[test]] 26 | name = "vs-std-write" 27 | required-features = ["std"] 28 | -------------------------------------------------------------------------------- /ufmt/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Jorge Aparicio 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. 26 | -------------------------------------------------------------------------------- /ufmt/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | categories = ["embedded", "no-std"] 4 | description = "`μfmt` macros" 5 | edition = "2021" 6 | keywords = ["Debug", "Display", "Write", "format"] 7 | license = "MIT OR Apache-2.0" 8 | name = "ufmt-macros" 9 | repository = "https://github.com/japaric/ufmt" 10 | version = "0.1.1" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | proc-macro-hack = "0.5.11" 17 | proc-macro2 = "1" 18 | quote = "1" 19 | regex = "1.5.4" 20 | lazy_static = "1.4.0" 21 | 22 | [dependencies.syn] 23 | features = ["full"] 24 | version = "1" 25 | -------------------------------------------------------------------------------- /ufmt/src/impls.rs: -------------------------------------------------------------------------------- 1 | mod array; 2 | mod core; 3 | mod ixx; 4 | mod nz; 5 | mod ptr; 6 | #[cfg(feature = "std")] 7 | mod std; 8 | mod tuple; 9 | mod uxx; 10 | -------------------------------------------------------------------------------- /ufmt/src/impls/array.rs: -------------------------------------------------------------------------------- 1 | use crate::{uDebug, uWrite, Formatter}; 2 | 3 | macro_rules! array { 4 | ($($N:expr),+) => { 5 | $( 6 | impl uDebug for [T; $N] 7 | where 8 | T: uDebug, 9 | { 10 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 11 | where 12 | W: uWrite + ?Sized, 13 | { 14 | <[T] as uDebug>::fmt(self, f) 15 | } 16 | } 17 | )+ 18 | } 19 | } 20 | 21 | array!( 22 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 23 | 26, 27, 28, 29, 30, 31, 32 24 | ); 25 | -------------------------------------------------------------------------------- /ufmt/src/impls/nz.rs: -------------------------------------------------------------------------------- 1 | use core::num::{ 2 | NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32, 3 | NonZeroU64, NonZeroU8, NonZeroUsize, 4 | }; 5 | 6 | use crate::{uDebug, uDisplay, uWrite, Formatter}; 7 | 8 | macro_rules! nz { 9 | ($($NZ:ident : $inner:ident,)*) => { 10 | $( 11 | impl uDebug for $NZ { 12 | #[inline(always)] 13 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 14 | where 15 | W: uWrite + ?Sized, 16 | { 17 | <$inner as uDebug>::fmt(&self.get(), f) 18 | } 19 | } 20 | 21 | impl uDisplay for $NZ { 22 | #[inline(always)] 23 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 24 | where 25 | W: uWrite + ?Sized, 26 | { 27 | <$inner as uDisplay>::fmt(&self.get(), f) 28 | } 29 | } 30 | )* 31 | } 32 | } 33 | 34 | nz!( 35 | NonZeroI16: i16, 36 | NonZeroI32: i32, 37 | NonZeroI64: i64, 38 | NonZeroI8: i8, 39 | NonZeroIsize: isize, 40 | NonZeroU16: u16, 41 | NonZeroU32: u32, 42 | NonZeroU64: u64, 43 | NonZeroU8: u8, 44 | NonZeroUsize: usize, 45 | ); 46 | -------------------------------------------------------------------------------- /ufmt/src/impls/ptr.rs: -------------------------------------------------------------------------------- 1 | use core::str; 2 | 3 | use crate::{uDebug, uWrite, Formatter}; 4 | 5 | macro_rules! hex { 6 | ($self:expr, $f:expr, $N:expr) => {{ 7 | let mut buf: [u8; $N] = [0; $N]; 8 | 9 | let i = hex(*$self as usize, &mut buf); 10 | 11 | unsafe { 12 | $f.write_str(str::from_utf8_unchecked( 13 | buf.get(i..).unwrap_or_else(|| assume_unreachable!()), 14 | )) 15 | } 16 | }}; 17 | } 18 | 19 | fn hex(mut n: usize, buf: &mut [u8]) -> usize { 20 | let mut i = buf.len() - 1; 21 | 22 | loop { 23 | let d = (n % 16) as u8; 24 | *buf.get_mut(i) 25 | .unwrap_or_else(|| unsafe { assume_unreachable!() }) = 26 | if d < 10 { d + b'0' } else { (d - 10) + b'a' }; 27 | n = n / 16; 28 | 29 | i -= 1; 30 | if n == 0 { 31 | break; 32 | } 33 | } 34 | 35 | *buf.get_mut(i) 36 | .unwrap_or_else(|| unsafe { assume_unreachable!() }) = b'x'; 37 | i -= 1; 38 | 39 | *buf.get_mut(i) 40 | .unwrap_or_else(|| unsafe { assume_unreachable!() }) = b'0'; 41 | 42 | i 43 | } 44 | 45 | impl uDebug for *const T { 46 | #[cfg(target_pointer_width = "16")] 47 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 48 | where 49 | W: uWrite + ?Sized, 50 | { 51 | hex!(self, f, 6) 52 | } 53 | 54 | #[cfg(target_pointer_width = "32")] 55 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 56 | where 57 | W: uWrite + ?Sized, 58 | { 59 | hex!(self, f, 10) 60 | } 61 | 62 | #[cfg(target_pointer_width = "64")] 63 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 64 | where 65 | W: uWrite + ?Sized, 66 | { 67 | hex!(self, f, 18) 68 | } 69 | } 70 | 71 | impl uDebug for *mut T { 72 | #[inline(always)] 73 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 74 | where 75 | W: uWrite + ?Sized, 76 | { 77 | (*self as *const T).fmt(f) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ufmt/src/impls/std.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; 2 | 3 | use crate::{uDebug, uDisplay, uWrite, Formatter}; 4 | 5 | impl uDebug for Box 6 | where 7 | T: uDebug, 8 | { 9 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 10 | where 11 | W: uWrite + ?Sized, 12 | { 13 | ::fmt(self, f) 14 | } 15 | } 16 | 17 | impl uDisplay for Box 18 | where 19 | T: uDisplay, 20 | { 21 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 22 | where 23 | W: uWrite + ?Sized, 24 | { 25 | ::fmt(self, f) 26 | } 27 | } 28 | 29 | impl uDebug for BTreeMap 30 | where 31 | K: uDebug, 32 | V: uDebug, 33 | { 34 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 35 | where 36 | W: uWrite + ?Sized, 37 | { 38 | f.debug_map()?.entries(self)?.finish() 39 | } 40 | } 41 | 42 | impl uDebug for BTreeSet 43 | where 44 | T: uDebug, 45 | { 46 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 47 | where 48 | W: uWrite + ?Sized, 49 | { 50 | f.debug_set()?.entries(self)?.finish() 51 | } 52 | } 53 | 54 | impl uDebug for HashMap 55 | where 56 | K: uDebug, 57 | V: uDebug, 58 | { 59 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 60 | where 61 | W: uWrite + ?Sized, 62 | { 63 | f.debug_map()?.entries(self)?.finish() 64 | } 65 | } 66 | 67 | impl uDebug for HashSet 68 | where 69 | T: uDebug, 70 | { 71 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 72 | where 73 | W: uWrite + ?Sized, 74 | { 75 | f.debug_set()?.entries(self)?.finish() 76 | } 77 | } 78 | 79 | impl uDebug for String { 80 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 81 | where 82 | W: uWrite + ?Sized, 83 | { 84 | ::fmt(self, f) 85 | } 86 | } 87 | 88 | impl uDisplay for String { 89 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 90 | where 91 | W: uWrite + ?Sized, 92 | { 93 | ::fmt(self, f) 94 | } 95 | } 96 | 97 | impl uDebug for Vec 98 | where 99 | T: uDebug, 100 | { 101 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 102 | where 103 | W: uWrite + ?Sized, 104 | { 105 | <[T] as uDebug>::fmt(self, f) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /ufmt/src/impls/tuple.rs: -------------------------------------------------------------------------------- 1 | use crate::{uDebug, uWrite, Formatter}; 2 | 3 | macro_rules! tuple { 4 | ($($T:ident),*; $($i:tt),*) => { 5 | impl<$($T,)*> uDebug for ($($T,)*) 6 | where 7 | $($T: uDebug,)* 8 | { 9 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 10 | where 11 | W: uWrite + ?Sized, 12 | { 13 | f.debug_tuple("")?$(.field(&self.$i)?)*.finish() 14 | } 15 | } 16 | 17 | } 18 | } 19 | 20 | impl uDebug for () { 21 | fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> 22 | where 23 | W: uWrite + ?Sized, 24 | { 25 | f.write_str("()") 26 | } 27 | } 28 | 29 | tuple!(A; 0); 30 | tuple!(A, B; 0, 1); 31 | tuple!(A, B, C; 0, 1, 2); 32 | tuple!(A, B, C, D; 0, 1, 2, 3); 33 | tuple!(A, B, C, D, E; 0, 1, 2, 3, 4); 34 | tuple!(A, B, C, D, E, F; 0, 1, 2, 3, 4, 5); 35 | tuple!(A, B, C, D, E, F, G; 0, 1, 2, 3, 4, 5, 6); 36 | tuple!(A, B, C, D, E, F, G, H; 0, 1, 2, 3, 4, 5, 6, 7); 37 | tuple!(A, B, C, D, E, F, G, H, I; 0, 1, 2, 3, 4, 5, 6, 7, 8); 38 | tuple!(A, B, C, D, E, F, G, H, I, J; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 39 | tuple!(A, B, C, D, E, F, G, H, I, J, K; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 40 | tuple!(A, B, C, D, E, F, G, H, I, J, K, L; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); 41 | -------------------------------------------------------------------------------- /ufmt/src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! assume_unreachable { 2 | () => { 3 | if cfg!(debug_assertions) { 4 | unreachable!() 5 | } else { 6 | core::hint::unreachable_unchecked() 7 | } 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /ufmt/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | categories = ["embedded", "no-std"] 4 | description = "`μfmt` utilities" 5 | documentation = "https://docs.rs/ufmt-utils" 6 | edition = "2021" 7 | keywords = ["Debug", "Display", "Write", "format"] 8 | license = "MIT OR Apache-2.0" 9 | name = "ufmt-utils" 10 | repository = "https://github.com/japaric/ufmt" 11 | version = "0.1.1" 12 | 13 | [dependencies] 14 | heapless = "0.5.0" 15 | ufmt-write = { version = "0.1.0", path = "../write" } 16 | 17 | [dev-dependencies] 18 | ufmt = { version = "0.1.0", path = ".." } 19 | -------------------------------------------------------------------------------- /ufmt/write/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Jorge Aparicio "] 3 | categories = ["embedded", "no-std"] 4 | description = "`μfmt`'s `uWrite` trait" 5 | edition = "2021" 6 | keywords = ["Debug", "Display", "Write", "format"] 7 | license = "MIT OR Apache-2.0" 8 | name = "ufmt-write" 9 | repository = "https://github.com/japaric/ufmt" 10 | version = "0.1.0" 11 | 12 | # NOTE do NOT add an `alloc` feature before the alloc crate can be used in 13 | # no-std BINARIES 14 | [features] 15 | # NOTE do NOT turn `std` into a default feature; this is a no-std first crate 16 | std = [] 17 | -------------------------------------------------------------------------------- /ufmt/write/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `μfmt`'s `uWrite` trait 2 | 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | #![deny(missing_docs)] 5 | #![deny(rust_2018_compatibility)] 6 | #![deny(rust_2018_idioms)] 7 | #![deny(warnings)] 8 | 9 | #[cfg(feature = "std")] 10 | use core::convert::Infallible; 11 | 12 | /// A collection of methods that are required / used to format a message into a stream. 13 | #[allow(non_camel_case_types)] 14 | pub trait uWrite { 15 | /// The error associated to this writer 16 | type Error; 17 | 18 | /// Writes a string slice into this writer, returning whether the write succeeded. 19 | /// 20 | /// This method can only succeed if the entire string slice was successfully written, and this 21 | /// method will not return until all data has been written or an error occurs. 22 | fn write_str(&mut self, s: &str) -> Result<(), Self::Error>; 23 | 24 | /// Writes a [`char`] into this writer, returning whether the write succeeded. 25 | /// 26 | /// A single [`char`] may be encoded as more than one byte. This method can only succeed if the 27 | /// entire byte sequence was successfully written, and this method will not return until all 28 | /// data has been written or an error occurs. 29 | fn write_char(&mut self, c: char) -> Result<(), Self::Error> { 30 | let mut buf: [u8; 4] = [0; 4]; 31 | self.write_str(c.encode_utf8(&mut buf)) 32 | } 33 | } 34 | 35 | #[cfg(feature = "std")] 36 | impl uWrite for String { 37 | type Error = Infallible; 38 | 39 | fn write_str(&mut self, s: &str) -> Result<(), Infallible> { 40 | self.push_str(s); 41 | Ok(()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /unittest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tock Project Developers "] 3 | description = """libtock-rs unit test support. Provides a fake Tock kernel as \ 4 | well as the test_component! macro.""" 5 | edition = "2021" 6 | license = "Apache-2.0 OR MIT" 7 | name = "libtock_unittest" 8 | repository = "https://www.github.com/tock/libtock-rs" 9 | rust-version.workspace = true 10 | version = "0.1.0" 11 | 12 | [dependencies] 13 | libtock_platform = { path = "../platform" } 14 | thiserror = "1.0.44" 15 | -------------------------------------------------------------------------------- /unittest/src/driver_info.rs: -------------------------------------------------------------------------------- 1 | /// Information that a `fake::SyscallDriver` provides to the `fake::Kernel` 2 | /// during registration. This may be expanded over time as new features are 3 | /// added to Tock. 4 | #[non_exhaustive] 5 | pub struct DriverInfo { 6 | // All constructors of DriverInfo require the driver to specify 7 | // `driver_num`. 8 | pub(crate) driver_num: u32, 9 | 10 | /// The maximum number of subscriptions to support. The maximum subscribe 11 | /// number supported will be one less than `upcall_count`. 12 | pub upcall_count: u32, 13 | } 14 | 15 | impl DriverInfo { 16 | /// Creates a new `DriverInfo` with the given driver number. `upcall_count` 17 | /// will be initialized to zero. 18 | pub fn new(driver_num: u32) -> Self { 19 | Self { 20 | driver_num, 21 | upcall_count: 0, 22 | } 23 | } 24 | 25 | /// Sets `upcall_count` and returns `self`. Used similar to a builder. 26 | /// 27 | /// # Example 28 | /// ``` 29 | /// use libtock_platform::CommandReturn; 30 | /// use libtock_unittest::{DriverInfo, fake}; 31 | /// struct FooDriver; 32 | /// impl fake::SyscallDriver for FooDriver { 33 | /// fn info(&self) -> DriverInfo { 34 | /// DriverInfo::new(3).upcall_count(2) 35 | /// } 36 | /// fn command(&self, _: u32, _: u32, _: u32) -> CommandReturn { 37 | /// unimplemented!("Example code"); 38 | /// } 39 | /// } 40 | /// ``` 41 | pub fn upcall_count(mut self, upcall_count: u32) -> Self { 42 | self.upcall_count = upcall_count; 43 | self 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /unittest/src/exit_test/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn exitcall_display() { 5 | assert_eq!(format!("{}", ExitCall::Terminate(3)), "exit-terminate(3)"); 6 | assert_eq!(format!("{}", ExitCall::Restart(14)), "exit-restart(14)"); 7 | } 8 | 9 | #[test] 10 | fn exitcall_parse() { 11 | assert_eq!("exit-terminate(3)".parse(), Ok(ExitCall::Terminate(3))); 12 | assert_eq!("exit-restart(14)".parse(), Ok(ExitCall::Restart(14))); 13 | assert_eq!("exit-unknown(3)".parse::(), Err(ParseExitError)); 14 | assert_eq!( 15 | "exit-restart(not-an-int)".parse::(), 16 | Err(ParseExitError) 17 | ); 18 | assert_eq!("no-parens".parse::(), Err(ParseExitError)); 19 | assert_eq!("".parse::(), Err(ParseExitError)); 20 | } 21 | 22 | #[test] 23 | fn exitmessage_display() { 24 | assert_eq!( 25 | format!("{}", ExitMessage::ExitCall(ExitCall::Restart(1))), 26 | "ExitCall(exit-restart(1))" 27 | ); 28 | assert_eq!(format!("{}", ExitMessage::WrongCase), "WrongCase"); 29 | assert_eq!(format!("{}", ExitMessage::DidNotExit), "DidNotExit"); 30 | } 31 | 32 | #[test] 33 | fn exitmessage_parse() { 34 | assert_eq!("".parse::(), Err(ParseExitError)); 35 | assert_eq!("ExitCall()".parse::(), Err(ParseExitError)); 36 | assert_eq!( 37 | "ExitCall(error)".parse::(), 38 | Err(ParseExitError) 39 | ); 40 | assert_eq!( 41 | "ExitCall(exit-restart(5))".parse::(), 42 | Ok(ExitMessage::ExitCall(ExitCall::Restart(5))) 43 | ); 44 | assert_eq!( 45 | "WrongCase".parse::(), 46 | Ok(ExitMessage::WrongCase) 47 | ); 48 | assert_eq!( 49 | "DidNotExit".parse::(), 50 | Ok(ExitMessage::DidNotExit) 51 | ); 52 | } 53 | 54 | #[should_panic(expected = "did not call Exit")] 55 | #[test] 56 | fn exit_test_did_not_exit() { 57 | exit_test("exit_test::tests::exit_test_did_not_exit", || {}); 58 | } 59 | 60 | #[should_panic(expected = "did not indicate why it exited")] 61 | #[test] 62 | fn exit_test_did_not_signal() { 63 | exit_test("exit_test::tests::exit_test_did_not_signal", || { 64 | std::process::exit(1) 65 | }); 66 | } 67 | 68 | #[test] 69 | fn exit_test_signal_terminate() { 70 | let result = exit_test("exit_test::tests::exit_test_signal_terminate", || { 71 | signal_exit(ExitCall::Terminate(159)); 72 | std::process::exit(1); 73 | }); 74 | assert_eq!(result, ExitCall::Terminate(159)); 75 | } 76 | 77 | #[test] 78 | fn exit_test_signal_restart() { 79 | let result = exit_test("exit_test::tests::exit_test_signal_restart", || { 80 | signal_exit(ExitCall::Restart(0)); 81 | std::process::exit(1); 82 | }); 83 | assert_eq!(result, ExitCall::Restart(0)); 84 | } 85 | 86 | #[should_panic(expected = "executed the wrong test case")] 87 | #[test] 88 | fn exit_test_wrong_case() { 89 | // Intentionally-incorrect test case name. 90 | exit_test("exit_test::tests::exit_test_signal_restart", || { 91 | signal_exit(ExitCall::Restart(0)); 92 | std::process::exit(1); 93 | }); 94 | } 95 | -------------------------------------------------------------------------------- /unittest/src/fake/adc/mod.rs: -------------------------------------------------------------------------------- 1 | //! Fake implementation of the Adc API, documented here: 2 | //! 3 | //! Like the real API, `Adc` controls a fake Adc sensor. It provides 4 | //! a function `set_value` used to immediately call an upcall with a Adc value read by the sensor 5 | //! and a function 'set_value_sync' used to call the upcall when the read command is received. 6 | 7 | use crate::{DriverInfo, DriverShareRef}; 8 | use libtock_platform::{CommandReturn, ErrorCode}; 9 | use std::cell::Cell; 10 | 11 | // The `upcall_on_command` field is set to Some(value) if an upcall(with value as its argument) should be called when read command is received, 12 | // or None otherwise. It was needed for testing `read_sync` library function which simulates a synchronous Adc read, 13 | // because it was impossible to schedule an upcall during the `synchronous` read in other ways. 14 | pub struct Adc { 15 | busy: Cell, 16 | upcall_on_command: Cell>, 17 | share_ref: DriverShareRef, 18 | } 19 | 20 | impl Adc { 21 | pub fn new() -> std::rc::Rc { 22 | std::rc::Rc::new(Adc { 23 | busy: Cell::new(false), 24 | upcall_on_command: Cell::new(None), 25 | share_ref: Default::default(), 26 | }) 27 | } 28 | 29 | pub fn is_busy(&self) -> bool { 30 | self.busy.get() 31 | } 32 | pub fn set_value(&self, value: i32) { 33 | if self.busy.get() { 34 | self.share_ref 35 | .schedule_upcall(0, (value as u32, 0, 0)) 36 | .expect("Unable to schedule upcall"); 37 | self.busy.set(false); 38 | } 39 | } 40 | pub fn set_value_sync(&self, value: i32) { 41 | self.upcall_on_command.set(Some(value)); 42 | } 43 | } 44 | 45 | impl crate::fake::SyscallDriver for Adc { 46 | fn info(&self) -> DriverInfo { 47 | DriverInfo::new(DRIVER_NUM).upcall_count(1) 48 | } 49 | 50 | fn register(&self, share_ref: DriverShareRef) { 51 | self.share_ref.replace(share_ref); 52 | } 53 | 54 | fn command(&self, command_id: u32, _argument0: u32, _argument1: u32) -> CommandReturn { 55 | match command_id { 56 | EXISTS => crate::command_return::success_u32(1), 57 | 58 | SINGLE_SAMPLE => { 59 | if self.busy.get() { 60 | return crate::command_return::failure(ErrorCode::Busy); 61 | } 62 | self.busy.set(true); 63 | if let Some(val) = self.upcall_on_command.take() { 64 | self.set_value(val); 65 | } 66 | crate::command_return::success() 67 | } 68 | _ => crate::command_return::failure(ErrorCode::NoSupport), 69 | } 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests; 75 | // ----------------------------------------------------------------------------- 76 | // Driver number and command IDs 77 | // ----------------------------------------------------------------------------- 78 | 79 | const DRIVER_NUM: u32 = 0x5; 80 | 81 | // Command IDs 82 | 83 | const EXISTS: u32 = 0; 84 | const SINGLE_SAMPLE: u32 = 1; 85 | -------------------------------------------------------------------------------- /unittest/src/fake/adc/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake::{self, SyscallDriver}; 2 | use fake::adc::*; 3 | use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; 4 | 5 | //Test the command implementation 6 | #[test] 7 | fn command() { 8 | let adc = Adc::new(); 9 | 10 | assert!(adc.command(EXISTS, 1, 2).is_success_u32()); 11 | 12 | assert!(adc.command(SINGLE_SAMPLE, 0, 0).is_success()); 13 | 14 | assert_eq!( 15 | adc.command(SINGLE_SAMPLE, 0, 0).get_failure(), 16 | Some(ErrorCode::Busy) 17 | ); 18 | 19 | adc.set_value(100); 20 | assert!(adc.command(SINGLE_SAMPLE, 0, 1).is_success()); 21 | adc.set_value(100); 22 | 23 | adc.set_value_sync(100); 24 | assert!(adc.command(SINGLE_SAMPLE, 0, 1).is_success()); 25 | assert!(adc.command(SINGLE_SAMPLE, 0, 1).is_success()); 26 | } 27 | 28 | // Integration test that verifies Adc works with fake::Kernel and 29 | // libtock_platform::Syscalls. 30 | #[test] 31 | fn kernel_integration() { 32 | use libtock_platform::Syscalls; 33 | let kernel = fake::Kernel::new(); 34 | let adc = Adc::new(); 35 | kernel.add_driver(&adc); 36 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success_u32()); 37 | assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 0).is_success()); 38 | assert_eq!( 39 | fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 0).get_failure(), 40 | Some(ErrorCode::Busy) 41 | ); 42 | adc.set_value(100); 43 | assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 1).is_success()); 44 | 45 | let listener = Cell::>::new(None); 46 | share::scope(|subscribe| { 47 | assert_eq!( 48 | fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), 49 | Ok(()) 50 | ); 51 | 52 | adc.set_value(100); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 54 | assert_eq!(listener.get(), Some((100,))); 55 | 56 | adc.set_value(200); 57 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 58 | 59 | assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 1).is_success()); 60 | adc.set_value(200); 61 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 62 | 63 | adc.set_value_sync(200); 64 | assert!(fake::Syscalls::command(DRIVER_NUM, SINGLE_SAMPLE, 0, 1).is_success()); 65 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /unittest/src/fake/alarm/mod.rs: -------------------------------------------------------------------------------- 1 | //! Fake implementation of the Alarm API. 2 | //! 3 | //! Supports frequency and set_relative. 4 | //! Will schedule the upcall immediately. 5 | 6 | use core::cell::Cell; 7 | use core::num::Wrapping; 8 | use libtock_platform::{CommandReturn, ErrorCode}; 9 | 10 | use crate::{DriverInfo, DriverShareRef}; 11 | 12 | pub struct Alarm { 13 | frequency_hz: u32, 14 | now: Cell>, 15 | share_ref: DriverShareRef, 16 | } 17 | 18 | impl Alarm { 19 | pub fn new(frequency_hz: u32) -> std::rc::Rc { 20 | std::rc::Rc::new(Alarm { 21 | frequency_hz, 22 | now: Cell::new(Wrapping(0)), 23 | share_ref: Default::default(), 24 | }) 25 | } 26 | } 27 | 28 | impl crate::fake::SyscallDriver for Alarm { 29 | fn info(&self) -> DriverInfo { 30 | DriverInfo::new(DRIVER_NUM).upcall_count(1) 31 | } 32 | 33 | fn register(&self, share_ref: DriverShareRef) { 34 | self.share_ref.replace(share_ref); 35 | } 36 | 37 | fn command(&self, command_number: u32, argument0: u32, _argument1: u32) -> CommandReturn { 38 | match command_number { 39 | command::FREQUENCY => crate::command_return::success_u32(self.frequency_hz), 40 | command::SET_RELATIVE => { 41 | // We're not actually sleeping, just ticking the timer. 42 | // The semantics of sleeping aren't clear, 43 | // so we're assuming that all future times are equal, 44 | // and waking immediately. 45 | let relative = argument0; 46 | let wake = self.now.get() + Wrapping(relative); 47 | self.share_ref 48 | .schedule_upcall(subscribe::CALLBACK, (wake.0, 0, 0)) 49 | .expect("schedule_upcall failed"); 50 | self.now.set(wake); 51 | crate::command_return::success_u32(wake.0) 52 | } 53 | _ => crate::command_return::failure(ErrorCode::NoSupport), 54 | } 55 | } 56 | } 57 | 58 | #[cfg(test)] 59 | mod tests; 60 | 61 | // ----------------------------------------------------------------------------- 62 | // Driver number and command IDs 63 | // ----------------------------------------------------------------------------- 64 | 65 | const DRIVER_NUM: u32 = 0x0; 66 | 67 | // Command IDs 68 | #[allow(unused)] 69 | pub mod command { 70 | pub const EXISTS: u32 = 0; 71 | pub const FREQUENCY: u32 = 1; 72 | pub const TIME: u32 = 2; 73 | pub const STOP: u32 = 3; 74 | 75 | pub const SET_RELATIVE: u32 = 5; 76 | pub const SET_ABSOLUTE: u32 = 6; 77 | } 78 | 79 | #[allow(unused)] 80 | pub mod subscribe { 81 | pub const CALLBACK: u32 = 0; 82 | } 83 | -------------------------------------------------------------------------------- /unittest/src/fake/alarm/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake; 2 | use fake::alarm::*; 3 | 4 | // Tests the command implementation. 5 | #[test] 6 | fn command() { 7 | use fake::SyscallDriver; 8 | let alarm = Alarm::new(10); 9 | 10 | assert_eq!( 11 | alarm.command(command::FREQUENCY, 1, 2).get_success_u32(), 12 | Some(10) 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /unittest/src/fake/ambient_light/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake::{self, SyscallDriver}; 2 | use fake::ambient_light::*; 3 | use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; 4 | 5 | //Test the command implementation 6 | #[test] 7 | fn command() { 8 | let amb = AmbientLight::new(); 9 | 10 | assert!(amb.command(EXISTS, 1, 2).is_success()); 11 | 12 | assert!(amb.command(READ_INTENSITY, 0, 0).is_success()); 13 | 14 | assert_eq!( 15 | amb.command(READ_INTENSITY, 0, 0).get_failure(), 16 | Some(ErrorCode::Busy) 17 | ); 18 | 19 | amb.set_value(100); 20 | assert!(amb.command(READ_INTENSITY, 0, 1).is_success()); 21 | amb.set_value(100); 22 | 23 | amb.set_value_sync(100); 24 | assert!(amb.command(READ_INTENSITY, 0, 1).is_success()); 25 | assert!(amb.command(READ_INTENSITY, 0, 1).is_success()); 26 | } 27 | 28 | // Integration test that verifies AmbientLight works with fake::Kernel and 29 | // libtock_platform::Syscalls. 30 | #[test] 31 | fn kernel_integration() { 32 | use libtock_platform::Syscalls; 33 | let kernel = fake::Kernel::new(); 34 | let ambient_light = AmbientLight::new(); 35 | kernel.add_driver(&ambient_light); 36 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); 37 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 0).is_success()); 38 | assert_eq!( 39 | fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 0).get_failure(), 40 | Some(ErrorCode::Busy) 41 | ); 42 | ambient_light.set_value(100); 43 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 1).is_success()); 44 | 45 | let listener = Cell::>::new(None); 46 | share::scope(|subscribe| { 47 | assert_eq!( 48 | fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), 49 | Ok(()) 50 | ); 51 | 52 | ambient_light.set_value(100); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 54 | assert_eq!(listener.get(), Some((100,))); 55 | 56 | ambient_light.set_value(200); 57 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 58 | 59 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 1).is_success()); 60 | ambient_light.set_value(200); 61 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 62 | 63 | ambient_light.set_value_sync(200); 64 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_INTENSITY, 0, 1).is_success()); 65 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /unittest/src/fake/buzzer/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake::{self, SyscallDriver}; 2 | use fake::buzzer::*; 3 | use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; 4 | 5 | #[test] 6 | fn command() { 7 | let buzzer = Buzzer::new(); 8 | assert!(buzzer.command(EXISTS, 1, 2).is_success()); 9 | assert!(buzzer.command(TONE, 0, 0).is_success()); 10 | 11 | assert_eq!( 12 | buzzer.command(TONE, 0, 0).get_failure(), 13 | Some(ErrorCode::Busy) 14 | ); 15 | 16 | buzzer.set_tone(100, Duration::from_millis(100)); 17 | assert!(buzzer.command(TONE, 0, 1).is_success()); 18 | buzzer.set_tone(100, Duration::from_millis(100)); 19 | 20 | buzzer.set_tone_sync(100, 100); 21 | assert!(buzzer.command(TONE, 0, 1).is_success()); 22 | assert!(buzzer.command(TONE, 0, 1).is_success()); 23 | } 24 | 25 | #[test] 26 | fn kernel_integration() { 27 | use libtock_platform::Syscalls; 28 | let kernel = fake::Kernel::new(); 29 | let buzzer = Buzzer::new(); 30 | kernel.add_driver(&buzzer); 31 | 32 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); 33 | assert!(fake::Syscalls::command(DRIVER_NUM, TONE, 0, 0).is_success()); 34 | assert_eq!( 35 | fake::Syscalls::command(DRIVER_NUM, TONE, 0, 0).get_failure(), 36 | Some(ErrorCode::Busy) 37 | ); 38 | buzzer.set_tone(100, Duration::from_millis(100)); 39 | assert!(fake::Syscalls::command(DRIVER_NUM, TONE, 0, 1).is_success()); 40 | 41 | let listener = Cell::>::new(None); 42 | share::scope(|subscribe| { 43 | assert_eq!( 44 | fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), 45 | Ok(()) 46 | ); 47 | 48 | buzzer.set_tone(100, Duration::from_millis(100)); 49 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 50 | assert_eq!(listener.get(), Some((100,))); 51 | 52 | buzzer.set_tone(200, Duration::from_millis(100)); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 54 | 55 | assert!(fake::Syscalls::command(DRIVER_NUM, TONE, 0, 1).is_success()); 56 | buzzer.set_tone(200, Duration::from_millis(100)); 57 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 58 | assert_eq!(listener.get(), Some((200,))); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /unittest/src/fake/console/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake; 2 | use crate::{RoAllowBuffer, RwAllowBuffer}; 3 | use libtock_platform::share; 4 | use libtock_platform::DefaultConfig; 5 | 6 | // Tests the command implementation. 7 | #[test] 8 | fn command() { 9 | use fake::SyscallDriver; 10 | let console = fake::Console::new(); 11 | assert!(console.command(fake::console::EXISTS, 1, 2).is_success()); 12 | assert!(console.allow_readonly(1, RoAllowBuffer::default()).is_ok()); 13 | assert!(console.allow_readonly(2, RoAllowBuffer::default()).is_err()); 14 | 15 | assert!(console.allow_readwrite(1, RwAllowBuffer::default()).is_ok()); 16 | assert!(console 17 | .allow_readwrite(2, RwAllowBuffer::default()) 18 | .is_err()); 19 | } 20 | 21 | // Integration test that verifies Console works with fake::Kernel and 22 | // libtock_platform::Syscalls. 23 | #[test] 24 | fn kernel_integration() { 25 | use libtock_platform::Syscalls; 26 | let kernel = fake::Kernel::new(); 27 | let console = fake::Console::new(); 28 | kernel.add_driver(&console); 29 | assert!( 30 | fake::Syscalls::command(fake::console::DRIVER_NUM, fake::console::EXISTS, 1, 2) 31 | .is_success() 32 | ); 33 | share::scope(|allow_ro| { 34 | fake::Syscalls::allow_ro::< 35 | DefaultConfig, 36 | { fake::console::DRIVER_NUM }, 37 | { fake::console::ALLOW_WRITE }, 38 | >(allow_ro, b"abcd") 39 | .unwrap(); 40 | assert!( 41 | fake::Syscalls::command(fake::console::DRIVER_NUM, fake::console::WRITE, 3, 0) 42 | .is_success() 43 | ); 44 | }); 45 | assert_eq!(console.take_bytes(), b"abc"); 46 | assert_eq!(console.take_bytes(), b""); 47 | 48 | let mut buf = [0; 4]; 49 | 50 | share::scope(|allow_rw| { 51 | fake::Syscalls::allow_rw::< 52 | DefaultConfig, 53 | { fake::console::DRIVER_NUM }, 54 | { fake::console::ALLOW_READ }, 55 | >(allow_rw, &mut buf) 56 | .unwrap(); 57 | assert!( 58 | fake::Syscalls::command(fake::console::DRIVER_NUM, fake::console::READ, 3, 0) 59 | .is_success() 60 | ); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /unittest/src/fake/kernel_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::kernel_data::with_kernel_data; 2 | use crate::{fake, ExpectedSyscall, SyscallLogEntry}; 3 | 4 | #[test] 5 | fn expected_syscall_queue() { 6 | use libtock_platform::YieldNoWaitReturn::Upcall; 7 | use std::matches; 8 | use ExpectedSyscall::YieldNoWait; 9 | let kernel = fake::Kernel::new(); 10 | with_kernel_data(|kernel_data| assert!(kernel_data.unwrap().expected_syscalls.is_empty())); 11 | kernel.add_expected_syscall(YieldNoWait { 12 | override_return: None, 13 | }); 14 | kernel.add_expected_syscall(YieldNoWait { 15 | override_return: Some(Upcall), 16 | }); 17 | with_kernel_data(|kernel_data| { 18 | let expected_syscalls = &mut kernel_data.unwrap().expected_syscalls; 19 | assert!(matches!( 20 | expected_syscalls.pop_front(), 21 | Some(YieldNoWait { 22 | override_return: None 23 | }) 24 | )); 25 | assert!(matches!( 26 | expected_syscalls.pop_front(), 27 | Some(YieldNoWait { 28 | override_return: Some(Upcall) 29 | }) 30 | )); 31 | assert!(expected_syscalls.is_empty()); 32 | }); 33 | } 34 | 35 | #[test] 36 | fn syscall_log() { 37 | use SyscallLogEntry::{YieldNoWait, YieldWait}; 38 | let kernel = fake::Kernel::new(); 39 | assert_eq!(kernel.take_syscall_log(), []); 40 | with_kernel_data(|kernel_data| { 41 | let syscall_log = &mut kernel_data.unwrap().syscall_log; 42 | syscall_log.push(YieldNoWait); 43 | syscall_log.push(YieldWait); 44 | }); 45 | assert_eq!(kernel.take_syscall_log(), [YieldNoWait, YieldWait]); 46 | assert_eq!(kernel.take_syscall_log(), []); 47 | } 48 | -------------------------------------------------------------------------------- /unittest/src/fake/leds/mod.rs: -------------------------------------------------------------------------------- 1 | //! Fake implementation of the LEDs API, documented here: 2 | //! https://github.com/tock/tock/blob/master/doc/syscalls/00002_leds.md 3 | //! 4 | //! Like the real API, `Leds` controls a set of fake LEDs. It provides 5 | //! a function `get_led` used to retrieve the state of an LED. 6 | 7 | use crate::DriverInfo; 8 | use core::cell::Cell; 9 | use libtock_platform::{CommandReturn, ErrorCode}; 10 | 11 | pub struct Leds { 12 | leds: [Cell; LEDS_COUNT], 13 | } 14 | 15 | impl Leds { 16 | pub fn new() -> std::rc::Rc> { 17 | #[allow(clippy::declare_interior_mutable_const)] 18 | const OFF: Cell = Cell::new(false); 19 | std::rc::Rc::new(Leds { 20 | leds: [OFF; LEDS_COUNT], 21 | }) 22 | } 23 | 24 | pub fn get_led(&self, led: u32) -> Option { 25 | self.leds.get(led as usize).map(|led| led.get()) 26 | } 27 | } 28 | 29 | impl crate::fake::SyscallDriver for Leds { 30 | fn info(&self) -> DriverInfo { 31 | DriverInfo::new(DRIVER_NUM) 32 | } 33 | 34 | fn command(&self, command_num: u32, argument0: u32, _argument1: u32) -> CommandReturn { 35 | match command_num { 36 | EXISTS => crate::command_return::success_u32(LEDS_COUNT as u32), 37 | LED_ON => { 38 | if argument0 < LEDS_COUNT as u32 { 39 | self.leds[argument0 as usize].set(true); 40 | crate::command_return::success() 41 | } else { 42 | crate::command_return::failure(ErrorCode::Invalid) 43 | } 44 | } 45 | LED_OFF => { 46 | if argument0 < LEDS_COUNT as u32 { 47 | self.leds[argument0 as usize].set(false); 48 | crate::command_return::success() 49 | } else { 50 | crate::command_return::failure(ErrorCode::Invalid) 51 | } 52 | } 53 | LED_TOGGLE => { 54 | if argument0 < LEDS_COUNT as u32 { 55 | self.leds[argument0 as usize].set(!self.leds[argument0 as usize].get()); 56 | crate::command_return::success() 57 | } else { 58 | crate::command_return::failure(ErrorCode::Invalid) 59 | } 60 | } 61 | _ => crate::command_return::failure(ErrorCode::NoSupport), 62 | } 63 | } 64 | } 65 | 66 | #[cfg(test)] 67 | mod tests; 68 | 69 | // ----------------------------------------------------------------------------- 70 | // Implementation details below 71 | // ----------------------------------------------------------------------------- 72 | 73 | const DRIVER_NUM: u32 = 0x2; 74 | 75 | // Command numbers 76 | const EXISTS: u32 = 0; 77 | const LED_ON: u32 = 1; 78 | const LED_OFF: u32 = 2; 79 | const LED_TOGGLE: u32 = 3; 80 | -------------------------------------------------------------------------------- /unittest/src/fake/leds/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake; 2 | use fake::leds::*; 3 | use libtock_platform::ErrorCode; 4 | 5 | // Tests the command implementation. 6 | #[test] 7 | fn command() { 8 | use fake::SyscallDriver; 9 | let leds = Leds::<10>::new(); 10 | let value = leds.command(EXISTS, 1, 2); 11 | assert_eq!(value.get_success_u32(), Some(10)); 12 | assert_eq!( 13 | leds.command(LED_ON, 11, 0).get_failure(), 14 | Some(ErrorCode::Invalid) 15 | ); 16 | assert_eq!(leds.get_led(0), Some(false)); 17 | assert!(leds.command(LED_ON, 0, 0).is_success()); 18 | assert_eq!(leds.get_led(0), Some(true)); 19 | assert!(leds.command(LED_OFF, 0, 0).is_success()); 20 | assert_eq!(leds.get_led(0), Some(false)); 21 | assert!(leds.command(LED_TOGGLE, 0, 0).is_success()); 22 | assert_eq!(leds.get_led(0), Some(true)); 23 | assert!(leds.command(LED_TOGGLE, 0, 0).is_success()); 24 | assert_eq!(leds.get_led(0), Some(false)); 25 | } 26 | 27 | // Integration test that verifies Leds works with fake::Kernel and 28 | // libtock_platform::Syscalls. 29 | #[test] 30 | fn kernel_integration() { 31 | use libtock_platform::Syscalls; 32 | let kernel = fake::Kernel::new(); 33 | let leds = Leds::<10>::new(); 34 | kernel.add_driver(&leds); 35 | let value = fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2); 36 | assert!(value.is_success_u32()); 37 | assert_eq!(value.get_success_u32(), Some(10)); 38 | assert_eq!( 39 | fake::Syscalls::command(DRIVER_NUM, LED_ON, 11, 0).get_failure(), 40 | Some(ErrorCode::Invalid) 41 | ); 42 | assert_eq!(leds.get_led(0), Some(false)); 43 | assert!(fake::Syscalls::command(DRIVER_NUM, LED_ON, 0, 0).is_success()); 44 | assert_eq!(leds.get_led(0), Some(true)); 45 | assert!(fake::Syscalls::command(DRIVER_NUM, LED_OFF, 0, 0).is_success()); 46 | assert_eq!(leds.get_led(0), Some(false)); 47 | assert!(fake::Syscalls::command(DRIVER_NUM, LED_TOGGLE, 0, 0).is_success()); 48 | assert_eq!(leds.get_led(0), Some(true)); 49 | assert!(fake::Syscalls::command(DRIVER_NUM, LED_TOGGLE, 0, 0).is_success()); 50 | assert_eq!(leds.get_led(0), Some(false)); 51 | } 52 | -------------------------------------------------------------------------------- /unittest/src/fake/low_level_debug/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake; 2 | use fake::low_level_debug::*; 3 | 4 | // Tests the command implementation. 5 | #[test] 6 | fn command() { 7 | use fake::SyscallDriver; 8 | let low_level_debug = LowLevelDebug::new(); 9 | assert!(low_level_debug.command(EXISTS, 1, 2).is_success()); 10 | assert!(low_level_debug.command(PRINT_ALERT_CODE, 3, 4).is_success()); 11 | assert_eq!(low_level_debug.take_messages(), [Message::AlertCode(3)]); 12 | assert!(low_level_debug.command(PRINT_1, 5, 6).is_success()); 13 | assert!(low_level_debug.command(PRINT_2, 7, 8).is_success()); 14 | assert_eq!( 15 | low_level_debug.take_messages(), 16 | [Message::Print1(5), Message::Print2(7, 8)] 17 | ); 18 | } 19 | 20 | // Integration test that verifies LowLevelDebug works with fake::Kernel and 21 | // libtock_platform::Syscalls. 22 | #[test] 23 | fn kernel_integration() { 24 | use libtock_platform::Syscalls; 25 | let kernel = fake::Kernel::new(); 26 | let low_level_debug = LowLevelDebug::new(); 27 | kernel.add_driver(&low_level_debug); 28 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); 29 | assert!(fake::Syscalls::command(DRIVER_NUM, PRINT_ALERT_CODE, 3, 4).is_success()); 30 | assert_eq!(low_level_debug.take_messages(), [Message::AlertCode(3)]); 31 | assert!(fake::Syscalls::command(DRIVER_NUM, PRINT_1, 5, 6).is_success()); 32 | assert!(fake::Syscalls::command(DRIVER_NUM, PRINT_2, 7, 8).is_success()); 33 | assert_eq!( 34 | low_level_debug.take_messages(), 35 | [Message::Print1(5), Message::Print2(7, 8)] 36 | ); 37 | } 38 | 39 | // Tests the Display implementation on Message. 40 | #[test] 41 | fn message_display() { 42 | use Message::*; 43 | assert_eq!(format!("{}", AlertCode(0x0)), "alert code 0x0 (unknown)"); 44 | assert_eq!(format!("{}", AlertCode(0x1)), "alert code 0x1 (panic)"); 45 | assert_eq!( 46 | format!("{}", AlertCode(0x2)), 47 | "alert code 0x2 (wrong location)" 48 | ); 49 | assert_eq!(format!("{}", AlertCode(0x3)), "alert code 0x3 (unknown)"); 50 | assert_eq!(format!("{}", Print1(0x31)), "prints 0x31"); 51 | assert_eq!(format!("{}", Print2(0x41, 0x59)), "prints 0x41 0x59"); 52 | } 53 | -------------------------------------------------------------------------------- /unittest/src/fake/mod.rs: -------------------------------------------------------------------------------- 1 | //! `fake` contains fake implementations of Tock kernel components. Fake 2 | //! components emulate the behavior of the real Tock kernel components, but in 3 | //! the unit test environment. They generally have additional testing features, 4 | //! such as error injection functionality. 5 | //! 6 | //! These components are exposed under the `fake` module because otherwise their 7 | //! names would collide with the corresponding drivers (e.g. the fake Console 8 | //! would collide with the Console driver in unit tests). Tests should generally 9 | //! `use libtock_unittest::fake` and refer to the type with the `fake::` prefix 10 | //! (e.g. `fake::Console`). 11 | 12 | mod adc; 13 | mod air_quality; 14 | mod alarm; 15 | mod ambient_light; 16 | mod buttons; 17 | mod buzzer; 18 | mod console; 19 | mod gpio; 20 | pub mod ieee802154; 21 | mod kernel; 22 | mod key_value; 23 | mod leds; 24 | mod low_level_debug; 25 | mod ninedof; 26 | mod proximity; 27 | mod sound_pressure; 28 | mod syscall_driver; 29 | mod syscalls; 30 | mod temperature; 31 | 32 | pub use adc::Adc; 33 | pub use air_quality::AirQuality; 34 | pub use alarm::Alarm; 35 | pub use ambient_light::AmbientLight; 36 | pub use buttons::Buttons; 37 | pub use buzzer::Buzzer; 38 | pub use console::Console; 39 | pub use gpio::{Gpio, GpioMode, InterruptEdge, PullMode}; 40 | pub use ieee802154::Ieee802154Phy; 41 | pub use kernel::Kernel; 42 | pub use key_value::KeyValue; 43 | pub use leds::Leds; 44 | pub use low_level_debug::{LowLevelDebug, Message}; 45 | pub use ninedof::{NineDof, NineDofData}; 46 | pub use proximity::Proximity; 47 | pub use sound_pressure::SoundPressure; 48 | pub use syscall_driver::SyscallDriver; 49 | pub use syscalls::Syscalls; 50 | pub use temperature::Temperature; 51 | 52 | #[cfg(test)] 53 | mod kernel_tests; 54 | -------------------------------------------------------------------------------- /unittest/src/fake/ninedof/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake::{self, SyscallDriver}; 2 | use fake::ninedof::*; 3 | use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; 4 | 5 | //Test the command implementation 6 | #[test] 7 | fn command() { 8 | let ninedof = NineDof::new(); 9 | 10 | assert!(ninedof.command(EXISTS, 1, 2).is_success()); 11 | 12 | assert!(ninedof.command(READ_ACCELEROMETER, 0, 0).is_success()); 13 | 14 | assert_eq!( 15 | ninedof.command(READ_ACCELEROMETER, 0, 0).get_failure(), 16 | Some(ErrorCode::Busy) 17 | ); 18 | 19 | let payload: NineDofData = NineDofData { x: 1, y: 2, z: 3 }; 20 | 21 | ninedof.set_value(payload); 22 | assert!(ninedof.command(READ_ACCELEROMETER, 0, 1).is_success()); 23 | ninedof.set_value(payload); 24 | 25 | ninedof.set_value_sync(payload); 26 | assert!(ninedof.command(READ_ACCELEROMETER, 0, 1).is_success()); 27 | assert!(ninedof.command(READ_ACCELEROMETER, 0, 1).is_success()); 28 | } 29 | 30 | // Integration test that verifies NineDof works with fake::Kernel and 31 | // libtock_platform::Syscalls. 32 | #[test] 33 | fn kernel_integration() { 34 | use libtock_platform::Syscalls; 35 | let kernel = fake::Kernel::new(); 36 | let ninedof = NineDof::new(); 37 | kernel.add_driver(&ninedof); 38 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); 39 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 0).is_success()); 40 | assert_eq!( 41 | fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 0).get_failure(), 42 | Some(ErrorCode::Busy) 43 | ); 44 | 45 | let payload: NineDofData = NineDofData { x: 1, y: 2, z: 3 }; 46 | 47 | ninedof.set_value(payload); 48 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 1).is_success()); 49 | 50 | let listener = Cell::>::new(None); 51 | share::scope(|subscribe| { 52 | assert_eq!( 53 | fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), 54 | Ok(()) 55 | ); 56 | 57 | ninedof.set_value(payload); 58 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 59 | assert_eq!(listener.get(), Some((1, 2, 3))); 60 | 61 | ninedof.set_value(payload); 62 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 63 | 64 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_ACCELEROMETER, 0, 1).is_success()); 65 | ninedof.set_value(payload); 66 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 67 | assert_eq!(listener.get(), Some((1, 2, 3))); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /unittest/src/fake/sound_pressure/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake::{self, SyscallDriver}; 2 | use fake::sound_pressure::*; 3 | use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; 4 | 5 | //Test the command implementation 6 | #[test] 7 | fn command() { 8 | let sound_pressure = SoundPressure::new(); 9 | 10 | assert!(sound_pressure.command(EXISTS, 1, 2).is_success()); 11 | 12 | assert!(sound_pressure.command(READ_PRESSURE, 0, 0).is_success()); 13 | 14 | assert_eq!( 15 | sound_pressure.command(READ_PRESSURE, 0, 0).get_failure(), 16 | Some(ErrorCode::Busy) 17 | ); 18 | 19 | sound_pressure.set_value(100); 20 | assert!(sound_pressure.command(READ_PRESSURE, 0, 1).is_success()); 21 | sound_pressure.set_value(100); 22 | 23 | sound_pressure.set_value_sync(100); 24 | assert!(sound_pressure.command(READ_PRESSURE, 0, 1).is_success()); 25 | assert!(sound_pressure.command(READ_PRESSURE, 0, 1).is_success()); 26 | } 27 | 28 | // Integration test that verifies SoundPressure works with fake::Kernel and 29 | // libtock_platform::Syscalls. 30 | #[test] 31 | fn kernel_integration() { 32 | use libtock_platform::Syscalls; 33 | let kernel = fake::Kernel::new(); 34 | let sound_pressure = SoundPressure::new(); 35 | kernel.add_driver(&sound_pressure); 36 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); 37 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 0).is_success()); 38 | assert_eq!( 39 | fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 0).get_failure(), 40 | Some(ErrorCode::Busy) 41 | ); 42 | sound_pressure.set_value(100); 43 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 1).is_success()); 44 | 45 | let listener = Cell::>::new(None); 46 | share::scope(|subscribe| { 47 | assert_eq!( 48 | fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), 49 | Ok(()) 50 | ); 51 | 52 | sound_pressure.set_value(100); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 54 | assert_eq!(listener.get(), Some((100,))); 55 | 56 | sound_pressure.set_value(200); 57 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 58 | 59 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_PRESSURE, 0, 1).is_success()); 60 | sound_pressure.set_value(200); 61 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 62 | assert_eq!(listener.get(), Some((200,))); 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /unittest/src/fake/syscall_driver.rs: -------------------------------------------------------------------------------- 1 | use crate::{DriverInfo, DriverShareRef, RoAllowBuffer, RwAllowBuffer}; 2 | use libtock_platform::{CommandReturn, ErrorCode}; 3 | 4 | /// The `fake::SyscallDriver` trait is implemented by fake versions of Tock's 5 | /// kernel APIs. It is used by `fake::Kernel` to route system calls to the fake 6 | /// kernel APIs. 7 | pub trait SyscallDriver: 'static { 8 | // ------------------------------------------------------------------------- 9 | // Functions called by `fake::Kernel` during driver registration. 10 | // ------------------------------------------------------------------------- 11 | 12 | /// Returns information about this driver, including its driver number. 13 | fn info(&self) -> DriverInfo; 14 | 15 | /// Called by `fake::Kernel` to link this driver to the `fake::Kernel`. 16 | /// Passes a reference to data shared with the kernel (e.g. registered 17 | /// upcalls). 18 | fn register(&self, share_ref: DriverShareRef) { 19 | let _ = share_ref; // Silence the unused variable warning. 20 | } 21 | 22 | // ------------------------------------------------------------------------- 23 | // Command 24 | // ------------------------------------------------------------------------- 25 | 26 | /// Process a Command system call. Fake drivers should use the methods in 27 | /// `libtock_unittest::command_return` to construct the return value. 28 | fn command(&self, command_id: u32, argument0: u32, argument1: u32) -> CommandReturn; 29 | 30 | // ------------------------------------------------------------------------- 31 | // Allow 32 | // ------------------------------------------------------------------------- 33 | 34 | /// Process a Read-Only Allow call. Because not all `SyscallDriver` 35 | /// implementations need to support Read-Only Allow, a default 36 | /// implementation is provided that rejects all Read-Only Allow calls. 37 | fn allow_readonly( 38 | &self, 39 | buffer_num: u32, 40 | buffer: RoAllowBuffer, 41 | ) -> Result { 42 | let _ = buffer_num; // Silences the unused variable warning. 43 | Err((buffer, ErrorCode::NoSupport)) 44 | } 45 | 46 | /// Process a Read-Write Allow call. Because not all SyscallDriver 47 | /// implementations need to support Read-Write Allow, a default 48 | /// implementation is provided that rejects all Read-Write Allow calls. 49 | fn allow_readwrite( 50 | &self, 51 | buffer_num: u32, 52 | buffer: RwAllowBuffer, 53 | ) -> Result { 54 | let _ = buffer_num; // Silences the unused variable warning. 55 | Err((buffer, ErrorCode::NoSupport)) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /unittest/src/fake/syscalls/exit_impl.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryInto; 2 | 3 | pub(super) fn exit(r0: libtock_platform::Register, r1: libtock_platform::Register) -> ! { 4 | let exit_num: u32 = r0.try_into().expect("Too large exit number"); 5 | let completion_code: u32 = r1.try_into().expect("Too large completion code"); 6 | match exit_num { 7 | libtock_platform::exit_id::TERMINATE => { 8 | println!("exit-terminate called with code {}", completion_code); 9 | 10 | #[cfg(not(miri))] 11 | crate::exit_test::signal_exit(crate::ExitCall::Terminate(completion_code)); 12 | 13 | std::process::exit(1); 14 | } 15 | libtock_platform::exit_id::RESTART => { 16 | println!("exit-restart called with code {}", completion_code); 17 | 18 | #[cfg(not(miri))] 19 | crate::exit_test::signal_exit(crate::ExitCall::Restart(completion_code)); 20 | 21 | std::process::exit(1); 22 | } 23 | _ => panic!("Unknown exit number {} invoked.", exit_num), 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /unittest/src/fake/syscalls/exit_impl_tests.rs: -------------------------------------------------------------------------------- 1 | use super::exit_impl::*; 2 | use crate::{exit_test, ExitCall}; 3 | 4 | #[test] 5 | fn exit_restart() { 6 | let exit_call = exit_test("fake::syscalls::exit_impl_tests::exit_restart", || { 7 | exit(libtock_platform::exit_id::RESTART.into(), 31415u32.into()) 8 | }); 9 | assert_eq!(exit_call, ExitCall::Restart(31415)); 10 | } 11 | 12 | #[test] 13 | fn exit_terminate() { 14 | let exit_call = exit_test("fake::syscalls::exit_impl_tests::exit_terminate", || { 15 | exit(libtock_platform::exit_id::TERMINATE.into(), 9265u32.into()) 16 | }); 17 | assert_eq!(exit_call, ExitCall::Terminate(9265)); 18 | } 19 | -------------------------------------------------------------------------------- /unittest/src/fake/syscalls/mod.rs: -------------------------------------------------------------------------------- 1 | mod allow_ro_impl; 2 | mod allow_rw_impl; 3 | mod command_impl; 4 | mod exit_impl; 5 | mod memop_impl; 6 | mod raw_syscalls_impl; 7 | mod subscribe_impl; 8 | mod yield_impl; 9 | 10 | /// `fake::Syscalls` implements `libtock_platform::Syscalls` by forwarding the 11 | /// system calls to the thread's `fake::Kernel` instance. It is used by unit 12 | /// tests to provide the code under test access to Tock's system calls. 13 | pub struct Syscalls; 14 | 15 | #[cfg(test)] 16 | mod allow_ro_impl_tests; 17 | #[cfg(test)] 18 | mod allow_rw_impl_tests; 19 | #[cfg(test)] 20 | mod command_impl_tests; 21 | #[cfg(all(not(miri), test))] 22 | mod exit_impl_tests; 23 | #[cfg(test)] 24 | mod memop_impl_tests; 25 | #[cfg(test)] 26 | mod raw_syscalls_impl_tests; 27 | #[cfg(test)] 28 | mod subscribe_impl_tests; 29 | #[cfg(test)] 30 | mod yield_impl_tests; 31 | 32 | // Miri does not always check that values are valid (see `doc/MiriTips.md` in 33 | // the root of this repository). This function uses a hack to verify a value is 34 | // valid. If the value is invalid, Miri will detect undefined behavior when it 35 | // executes this. It is used by submodules of fake::syscalls. 36 | fn assert_valid(_value: T) { 37 | #[cfg(miri)] 38 | let _ = format!("{:?}", _value); 39 | } 40 | -------------------------------------------------------------------------------- /unittest/src/fake/syscalls/raw_syscalls_impl.rs: -------------------------------------------------------------------------------- 1 | use libtock_platform::{syscall_class, yield_id, RawSyscalls, Register}; 2 | use std::convert::TryInto; 3 | 4 | unsafe impl RawSyscalls for crate::fake::Syscalls { 5 | unsafe fn yield1([r0]: [Register; 1]) { 6 | crate::fake::syscalls::assert_valid(r0); 7 | match r0.try_into().expect("too-large Yield ID passed") { 8 | yield_id::NO_WAIT => panic!("yield-no-wait called without an argument"), 9 | yield_id::WAIT => super::yield_impl::yield_wait(), 10 | id => panic!("unknown yield ID {}", id), 11 | } 12 | } 13 | 14 | unsafe fn yield2([r0, r1]: [Register; 2]) { 15 | crate::fake::syscalls::assert_valid((r0, r1)); 16 | match r0.try_into().expect("too-large Yield ID passed") { 17 | yield_id::NO_WAIT => unsafe { super::yield_impl::yield_no_wait(r1.into()) }, 18 | yield_id::WAIT => { 19 | // Technically it is acceptable to call yield_wait with an 20 | // argument, but it shouldn't be done because it's wasteful so 21 | // we fail the test case regardless. 22 | panic!("yield-wait called with an argument"); 23 | } 24 | id => panic!("unknown yield ID {}", id), 25 | } 26 | } 27 | 28 | unsafe fn syscall1([r0]: [Register; 1]) -> [Register; 2] { 29 | match CLASS { 30 | syscall_class::MEMOP => super::memop_impl::memop(r0, 0u32.into()), 31 | _ => panic!("Unknown syscall1 call. Class: {}", CLASS), 32 | } 33 | } 34 | 35 | unsafe fn syscall2([r0, r1]: [Register; 2]) -> [Register; 2] { 36 | crate::fake::syscalls::assert_valid((r0, r1)); 37 | match CLASS { 38 | syscall_class::MEMOP => super::memop_impl::memop(r0, r1), 39 | syscall_class::EXIT => super::exit_impl::exit(r0, r1), 40 | _ => panic!("Unknown syscall2 call. Class: {}", CLASS), 41 | } 42 | } 43 | 44 | unsafe fn syscall4([r0, r1, r2, r3]: [Register; 4]) -> [Register; 4] { 45 | crate::fake::syscalls::assert_valid((r0, r1, r2, r3)); 46 | match CLASS { 47 | syscall_class::SUBSCRIBE => unsafe { super::subscribe_impl::subscribe(r0, r1, r2, r3) }, 48 | syscall_class::COMMAND => super::command_impl::command(r0, r1, r2, r3), 49 | syscall_class::ALLOW_RW => unsafe { super::allow_rw_impl::allow_rw(r0, r1, r2, r3) }, 50 | syscall_class::ALLOW_RO => unsafe { super::allow_ro_impl::allow_ro(r0, r1, r2, r3) }, 51 | _ => panic!("Unknown syscall4 call. Class: {}", CLASS), 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /unittest/src/fake/syscalls/raw_syscalls_impl_tests.rs: -------------------------------------------------------------------------------- 1 | // These tests verify the RawSyscalls implementation routes system calls to the 2 | // fake implementations (e.g. command(), yield_wait, etc) correctly. It does not 3 | // test the fake syscall implementations themselves, as they have their own unit 4 | // tests. 5 | 6 | use crate::{fake, SyscallLogEntry}; 7 | use libtock_platform::{syscall_class, RawSyscalls}; 8 | 9 | #[test] 10 | fn allow_ro() { 11 | let kernel = fake::Kernel::new(); 12 | unsafe { 13 | fake::Syscalls::syscall4::<{ syscall_class::ALLOW_RO }>([ 14 | 1u32.into(), 15 | 2u32.into(), 16 | 0u32.into(), 17 | 0u32.into(), 18 | ]); 19 | } 20 | assert_eq!( 21 | kernel.take_syscall_log(), 22 | [SyscallLogEntry::AllowRo { 23 | driver_num: 1, 24 | buffer_num: 2, 25 | len: 0, 26 | }] 27 | ); 28 | } 29 | 30 | #[test] 31 | fn allow_rw() { 32 | let kernel = fake::Kernel::new(); 33 | unsafe { 34 | fake::Syscalls::syscall4::<{ syscall_class::ALLOW_RW }>([ 35 | 1u32.into(), 36 | 2u32.into(), 37 | 0u32.into(), 38 | 0u32.into(), 39 | ]); 40 | } 41 | assert_eq!( 42 | kernel.take_syscall_log(), 43 | [SyscallLogEntry::AllowRw { 44 | driver_num: 1, 45 | buffer_num: 2, 46 | len: 0, 47 | }] 48 | ); 49 | } 50 | 51 | // TODO: Move the syscall4 Command test here. 52 | 53 | // TODO: Implement Exit. 54 | 55 | #[test] 56 | fn memop() { 57 | let kernel = fake::Kernel::new(); 58 | unsafe { 59 | fake::Syscalls::syscall2::<{ syscall_class::MEMOP }>([1u32.into(), 2u32.into()]); 60 | fake::Syscalls::syscall1::<{ syscall_class::MEMOP }>([2u32.into()]); 61 | } 62 | assert_eq!( 63 | kernel.take_syscall_log(), 64 | [ 65 | SyscallLogEntry::Memop { 66 | memop_num: 1, 67 | argument0: 2.into(), 68 | }, 69 | SyscallLogEntry::Memop { 70 | memop_num: 2, 71 | argument0: 0.into(), 72 | } 73 | ] 74 | ); 75 | } 76 | 77 | // TODO: Implement Subscribe. 78 | 79 | // TODO: Move the yield1 and yield2 tests here. 80 | -------------------------------------------------------------------------------- /unittest/src/fake/temperature/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::fake::{self, SyscallDriver}; 2 | use fake::temperature::*; 3 | use libtock_platform::{share, DefaultConfig, YieldNoWaitReturn}; 4 | 5 | //Test the command implementation 6 | #[test] 7 | fn command() { 8 | let temp = Temperature::new(); 9 | 10 | assert!(temp.command(EXISTS, 1, 2).is_success()); 11 | 12 | assert!(temp.command(READ_TEMP, 0, 0).is_success()); 13 | 14 | assert_eq!( 15 | temp.command(READ_TEMP, 0, 0).get_failure(), 16 | Some(ErrorCode::Busy) 17 | ); 18 | 19 | temp.set_value(100); 20 | assert!(temp.command(READ_TEMP, 0, 1).is_success()); 21 | temp.set_value(100); 22 | 23 | temp.set_value_sync(100); 24 | assert!(temp.command(READ_TEMP, 0, 1).is_success()); 25 | assert!(temp.command(READ_TEMP, 0, 1).is_success()); 26 | } 27 | 28 | // Integration test that verifies Temperature works with fake::Kernel and 29 | // libtock_platform::Syscalls. 30 | #[test] 31 | fn kernel_integration() { 32 | use libtock_platform::Syscalls; 33 | let kernel = fake::Kernel::new(); 34 | let temp = Temperature::new(); 35 | kernel.add_driver(&temp); 36 | assert!(fake::Syscalls::command(DRIVER_NUM, EXISTS, 1, 2).is_success()); 37 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_TEMP, 0, 0).is_success()); 38 | assert_eq!( 39 | fake::Syscalls::command(DRIVER_NUM, READ_TEMP, 0, 0).get_failure(), 40 | Some(ErrorCode::Busy) 41 | ); 42 | temp.set_value(100); 43 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_TEMP, 0, 1).is_success()); 44 | 45 | let listener = Cell::>::new(None); 46 | share::scope(|subscribe| { 47 | assert_eq!( 48 | fake::Syscalls::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, &listener), 49 | Ok(()) 50 | ); 51 | 52 | temp.set_value(100); 53 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 54 | assert_eq!(listener.get(), Some((100,))); 55 | 56 | temp.set_value(200); 57 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); 58 | 59 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_TEMP, 0, 1).is_success()); 60 | temp.set_value(200); 61 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 62 | 63 | temp.set_value_sync(200); 64 | assert!(fake::Syscalls::command(DRIVER_NUM, READ_TEMP, 0, 1).is_success()); 65 | assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /unittest/src/kernel_data.rs: -------------------------------------------------------------------------------- 1 | //! `KernelData` contains the data corresponding to a `fake::Kernel`. It is 2 | //! stored in the thread-local variable `KERNEL_DATA`. 3 | //! 4 | //! The data is stored separately from the `fake::Kernel` because in addition to 5 | //! being accessed through the `fake::Kernel`, it is also accessed by 6 | //! `fake::Syscalls` and `upcall::schedule`. `fake::Syscalls` is reentrant (a 7 | //! Yield invocation can run a callback that executes another system call), 8 | //! which easily results in messy code. To keep things understandable, code that 9 | //! uses `KERNEL_DATA` should avoid calling user-supplied functions (such as 10 | //! upcalls) while holding a reference to `KERNEL_DATA`. 11 | 12 | use std::cell::RefCell; 13 | 14 | pub(crate) struct KernelData { 15 | pub allow_db: crate::allow_db::AllowDb, 16 | 17 | // The location of the call to `fake::Kernel::new`. Used in the event a 18 | // duplicate `fake::Kernel` is created to tell the user which kernel they 19 | // did not clean up in a unit test. 20 | pub create_location: &'static std::panic::Location<'static>, 21 | 22 | pub drivers: std::collections::HashMap, 23 | pub expected_syscalls: std::collections::VecDeque, 24 | pub syscall_log: Vec, 25 | pub upcall_queue: crate::upcall::UpcallQueue, 26 | pub memory_break: *const u8, 27 | } 28 | 29 | // KERNEL_DATA is set to Some in `fake::Kernel::new` and set to None when the 30 | // `fake::Kernel` is dropped. 31 | thread_local!(pub(crate) static KERNEL_DATA: RefCell> = const { RefCell::new(None) }); 32 | 33 | // Convenience function to get mutable access to KERNEL_DATA. 34 | pub(crate) fn with_kernel_data) -> R, R>(f: F) -> R { 35 | KERNEL_DATA.with(|refcell| f(refcell.borrow_mut().as_mut())) 36 | } 37 | 38 | // Per-driver data stored in KernelData. 39 | pub struct DriverData { 40 | pub driver: std::rc::Rc, 41 | pub num_upcalls: u32, 42 | 43 | // Currently-valid upcalls passed to Subscribe. The key is the subscribe 44 | // number. 45 | pub upcalls: std::collections::HashMap, 46 | } 47 | -------------------------------------------------------------------------------- /unittest/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! `libtock_unittest` provides testing tools needed by `libtock-rs`'s own unit 2 | //! tests as well as unit tests of code that uses `libtock-rs`. 3 | 4 | #![deny(unsafe_op_in_unsafe_fn)] 5 | 6 | mod allow_db; 7 | pub mod command_return; 8 | mod driver_info; 9 | #[cfg(not(miri))] 10 | mod exit_test; 11 | mod expected_syscall; 12 | pub mod fake; 13 | mod kernel_data; 14 | mod share_data; 15 | mod syscall_log; 16 | pub mod upcall; 17 | 18 | pub use allow_db::{RoAllowBuffer, RwAllowBuffer}; 19 | pub use driver_info::DriverInfo; 20 | #[cfg(not(miri))] 21 | pub use exit_test::{exit_test, ExitCall}; 22 | pub use expected_syscall::ExpectedSyscall; 23 | pub use share_data::DriverShareRef; 24 | pub use syscall_log::SyscallLogEntry; 25 | 26 | #[cfg(test)] 27 | mod allow_db_test; 28 | -------------------------------------------------------------------------------- /unittest/src/syscall_log.rs: -------------------------------------------------------------------------------- 1 | use libtock_platform::Register; 2 | 3 | /// SyscallLogEntry represents a system call made during test execution. 4 | #[derive(Debug, Eq, PartialEq)] 5 | pub enum SyscallLogEntry { 6 | // ------------------------------------------------------------------------- 7 | // Yield 8 | // ------------------------------------------------------------------------- 9 | YieldNoWait, 10 | 11 | YieldWait, 12 | 13 | // ------------------------------------------------------------------------- 14 | // Subscribe 15 | // ------------------------------------------------------------------------- 16 | Subscribe { 17 | driver_num: u32, 18 | subscribe_num: u32, 19 | }, 20 | 21 | // ------------------------------------------------------------------------- 22 | // Command 23 | // ------------------------------------------------------------------------- 24 | Command { 25 | driver_id: u32, 26 | command_id: u32, 27 | argument0: u32, 28 | argument1: u32, 29 | }, 30 | 31 | // ------------------------------------------------------------------------- 32 | // Read-Only Allow 33 | // ------------------------------------------------------------------------- 34 | AllowRo { 35 | driver_num: u32, 36 | buffer_num: u32, 37 | len: usize, 38 | }, 39 | 40 | // ------------------------------------------------------------------------- 41 | // Read-Write Allow 42 | // ------------------------------------------------------------------------- 43 | AllowRw { 44 | driver_num: u32, 45 | buffer_num: u32, 46 | len: usize, 47 | }, 48 | 49 | // ------------------------------------------------------------------------- 50 | // Memop 51 | // ------------------------------------------------------------------------- 52 | Memop { 53 | memop_num: u32, 54 | argument0: Register, // Necessary for Miri ptr provenance of brk() 55 | }, 56 | // TODO: Add Exit. 57 | } 58 | -------------------------------------------------------------------------------- /unittest/src/upcall.rs: -------------------------------------------------------------------------------- 1 | /// Raw upcall data, as it was passed to Subscribe. This upcall is not 2 | /// guaranteed to still be valid. 3 | #[derive(Clone, Copy)] 4 | pub struct Upcall { 5 | pub fn_pointer: Option, 6 | pub data: libtock_platform::Register, 7 | } 8 | 9 | impl Upcall { 10 | /// Returns true if this is a null callback, false otherwise. 11 | pub fn is_null(&self) -> bool { 12 | self.fn_pointer.is_none() 13 | } 14 | 15 | /// # Safety 16 | /// An upcall may only be invoked if it is still active. As described in TRD 17 | /// 104, an upcall is still active if it has not been replaced by another 18 | /// Subscribe call with the same upcall ID. All upcalls in the upcall queue 19 | /// in KernelData are active. 20 | pub unsafe fn invoke(&self, args: (u32, u32, u32)) { 21 | if let Some(fn_pointer) = self.fn_pointer { 22 | unsafe { 23 | fn_pointer(args.0, args.1, args.2, self.data); 24 | } 25 | } 26 | } 27 | } 28 | 29 | // The type of the upcall queue in KernelData. Contains queued upcalls, which 30 | // are waiting to be invoked during a Yield call. 31 | // 32 | // Based on this discussion: 33 | // https://mailman.stanford.edu/pipermail/tock-version2/2020-November/000025.html 34 | // this queue is a FIFO queue. New entries should be pushed to the back of the 35 | // queue, and Yield should invoke upcalls starting with the front of the queue. 36 | // 37 | // A note on performance: When an upcall is replaced via Subscribe and becomes 38 | // invalid, Subscribe has to iterate through this queue and remove all instances 39 | // of that upcall. That takes linear time in the length of the queue, so it can 40 | // be slow if the queue is long. A long queue is unrealistic, so we shouldn't 41 | // need a long queue in test cases, so that is acceptable. There are alternative 42 | // data structures that avoid that slowdown, but they are more complex and 43 | // likely slower in the common case. 44 | pub(crate) type UpcallQueue = std::collections::VecDeque; 45 | 46 | // An entry in the fake kernel's upcall queue. 47 | pub(crate) struct UpcallQueueEntry { 48 | pub args: (u32, u32, u32), 49 | pub id: UpcallId, 50 | pub upcall: Upcall, 51 | } 52 | 53 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 54 | pub(crate) struct UpcallId { 55 | pub driver_num: u32, 56 | pub subscribe_num: u32, 57 | } 58 | --------------------------------------------------------------------------------