diff --git a/Cargo.lock b/Cargo.lock index 6d7affc1b..2a1cc17d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -407,6 +407,21 @@ version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +[[package]] +name = "button-api" +version = "0.1.0" +dependencies = [ + "counters", + "derive-idol-err", + "hubpack", + "idol", + "idol-runtime", + "num-traits", + "serde", + "userlib", + "zerocopy 0.6.6", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -4820,6 +4835,29 @@ dependencies = [ "zerocopy 0.6.6", ] +[[package]] +name = "task-button" +version = "0.1.0" +dependencies = [ + "anyhow", + "arrayvec", + "build-lpc55pins", + "build-util", + "button-api", + "drv-lpc55-gpio-api", + "hubpack", + "idol", + "idol-runtime", + "num-traits", + "quote", + "serde", + "serde_with 3.6.1", + "static-cell", + "unwrap-lite", + "userlib", + "zerocopy 0.6.6", +] + [[package]] name = "task-caboose-reader" version = "0.1.0" diff --git a/app/lpc55xpresso/app-button.toml b/app/lpc55xpresso/app-button.toml new file mode 100644 index 000000000..03096b44a --- /dev/null +++ b/app/lpc55xpresso/app-button.toml @@ -0,0 +1,160 @@ +name = "lpc55xpresso-button" +target = "thumbv8m.main-none-eabihf" +board = "lpcxpresso55s69" +chip = "../../chips/lpc55" +stacksize = 1024 +image-names = ["a", "b"] +fwid = true + +[kernel] +name = "lpc55xpresso" +features = ["dump", "dice-self"] +requires = {flash = 55040, ram = 4096} + +[caboose] +region = "flash" +size = 256 +default = true + +[tasks.jefe] +name = "task-jefe" +priority = 0 +max-sizes = {flash = 16384, ram = 2048} +start = true +features = ["dump"] +stacksize = 1536 +notifications = ["fault", "timer"] +extern-regions = ["sram2"] + +[tasks.jefe.config.allowed-callers] +request_reset = ["update_server"] + +[tasks.hiffy] +name = "task-hiffy" +priority = 5 +features = ["lpc55", "gpio"] +max-sizes = {flash = 32768, ram = 16384 } +stacksize = 2048 +start = true +task-slots = ["gpio_driver", "update_server"] + +[tasks.idle] +name = "task-idle" +priority = 7 +max-sizes = {flash = 256, ram = 256} +stacksize = 256 +start = true + +[tasks.update_server] +name = "lpc55-update-server" +priority = 3 +max-sizes = {flash = 26720, ram = 16704} +stacksize = 8192 +start = true +sections = {bootstate = "usbsram"} +uses = ["flash_controller", "hash_crypt"] +notifications = ["flash-irq", "hashcrypt-irq"] +interrupts = {"flash_controller.irq" = "flash-irq", "hash_crypt.irq" = "hashcrypt-irq"} +task-slots = [{"syscon" = "syscon_driver"}, "jefe"] + +[tasks.syscon_driver] +name = "drv-lpc55-syscon" +priority = 2 +max-sizes = {flash = 8192, ram = 2048} +uses = ["syscon", "anactrl", "pmc"] +start = true +stacksize = 1000 +task-slots = ["jefe"] + +[tasks.gpio_driver] +name = "drv-lpc55-gpio" +priority = 3 +max-sizes = {flash = 8192, ram = 2048} +uses = ["gpio", "iocon", "pint", "inputmux"] +start = true +stacksize = 1000 +task-slots = ["syscon_driver"] + +[tasks.user_leds] +name = "drv-user-leds" +features = ["lpc55"] +priority = 4 +max-sizes = {flash = 8192, ram = 2048} +start = true +stacksize = 1000 +task-slots = ["gpio_driver"] +notifications = ["timer"] + +[tasks.button] +name = "task-button" +priority = 6 +start = true +notifications = ["timer", "button-irq"] +interrupts = { "pint.irq0" = "button-irq"} +stacksize = 4096 +task-slots = ["gpio_driver", {"syscon" = "syscon_driver"}] + +[tasks.button.config] +pins = [ + { name = "BUTTON", pin = { port = 1, pin = 9}, alt = 0, pint = 0, direction = "input", opendrain = "normal" }, + { name = "RED_LED", pin = { port = 1, pin = 6}, alt = 0, direction = "output", value = true }, + { name = "GREEN_LED", pin = { port = 1, pin = 7}, alt = 0, direction = "output", value = true }, + { name = "BLUE_LED", pin = { port = 1, pin = 4}, alt = 0, direction = "output", value = true }, +] + +[tasks.usart_driver] +name = "drv-lpc55-usart" +priority = 4 +max-sizes = {flash = 8192, ram = 2048} +uses = ["flexcomm0"] +start = true +notifications = ["usart-irq"] +interrupts = {"flexcomm0.irq" = "usart-irq"} +stacksize = 1000 +task-slots = ["gpio_driver", "syscon_driver"] + +[tasks.usart_driver.config] +pins = [ + { pin = { port = 0, pin = 29}, alt = 1}, + { pin = { port = 0, pin = 30}, alt = 1} +] + +[tasks.i2c_driver] +name = "drv-lpc55-i2c" +priority = 4 +uses = ["flexcomm4"] +start = true +stacksize = 1000 +task-slots = ["gpio_driver", "syscon_driver"] + +[tasks.rng_driver] +name = "drv-lpc55-rng" +priority = 3 +max-sizes = {flash = 16384, ram = 4096} +uses = ["rng", "pmc"] +start = true +stacksize = 2200 +task-slots = ["syscon_driver"] + +[tasks.dump_agent] +name = "task-dump-agent" +features = ["no-rot"] +priority = 5 +max-sizes = {flash = 32768, ram = 2240 } +start = true +task-slots = ["jefe"] +stacksize = 1536 +extern-regions = ["sram2"] + +[tasks.attest] +name = "task-attest" +priority = 5 +max-sizes = {flash = 35072, ram = 16384} +stacksize = 12304 +start = false +extern-regions = ["dice_alias", "dice_certs"] + +[signing.certs] +signing-certs = ["../../support/fake_certs/fake_certificate.der.crt"] +root-certs = ["../../support/fake_certs/fake_certificate.der.crt"] +private-key = "../../support/fake_certs/fake_private_key.pem" diff --git a/idl/button.idol b/idl/button.idol new file mode 100644 index 000000000..b7ea953e5 --- /dev/null +++ b/idl/button.idol @@ -0,0 +1,43 @@ +// User Button and LED API + +Interface( + name: "Button", + ops: { + "press": ( + reply: Result( + ok: "u8", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + "off": ( + reply: Result( + ok: "()", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + "set": ( + args: { + "rgb": "u8", + }, + reply: Result( + ok: "()", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + "blink": ( + description: "blinks the LED at a fixed speed", + args: { + "on": "u32", + "off": "u32", + }, + reply: Result( + ok: "()", + err: CLike("ButtonError"), + ), + idempotent: true, + ), + }, +) diff --git a/task/button-api/Cargo.toml b/task/button-api/Cargo.toml new file mode 100644 index 000000000..9aac46b54 --- /dev/null +++ b/task/button-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "button-api" +version = "0.1.0" +edition = "2021" + +[features] + +[dependencies] +counters = { path = "../../lib/counters" } +derive-idol-err = { path = "../../lib/derive-idol-err" } +hubpack = { workspace = true } +idol-runtime = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } +zerocopy = { workspace = true } + +[build-dependencies] +idol = { workspace = true } +serde = { workspace = true } + +[lib] +test = false +doctest = false +bench = false + +[lints] +workspace = true diff --git a/task/button-api/build.rs b/task/button-api/build.rs new file mode 100644 index 000000000..16d638482 --- /dev/null +++ b/task/button-api/build.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::error::Error; + +fn main() -> Result<(), Box> { + idol::client::build_client_stub("../../idl/button.idol", "client_stub.rs")?; + Ok(()) +} diff --git a/task/button-api/src/lib.rs b/task/button-api/src/lib.rs new file mode 100644 index 000000000..9a0e2d1f7 --- /dev/null +++ b/task/button-api/src/lib.rs @@ -0,0 +1,24 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! API crate for the 'button' task. + +#![no_std] + +use derive_idol_err::IdolError; +use userlib::{sys_send, FromPrimitive}; + +#[derive(Copy, Clone, Debug, FromPrimitive, IdolError, counters::Count)] +pub enum ButtonError { + InvalidValue = 1, + TaskRestarted = 2, +} + +impl From for ButtonError { + fn from(_: idol_runtime::ServerDeath) -> Self { + ButtonError::TaskRestarted + } +} + +include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); diff --git a/task/button/Cargo.toml b/task/button/Cargo.toml new file mode 100644 index 000000000..e146ca1a2 --- /dev/null +++ b/task/button/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "task-button" +version = "0.1.0" +edition = "2021" + +[dependencies] +drv-lpc55-gpio-api = { path = "../../drv/lpc55-gpio-api"} +button-api = { path = "../button-api"} +arrayvec.workspace = true +hubpack = { workspace = true } +idol-runtime = { workspace = true } +num-traits = { workspace = true } +serde = { workspace = true } +serde_with = { version = "3.3.0", default-features = false, features = ["macros"] } +static-cell = { path = "../../lib/static-cell" } +unwrap-lite = { path = "../../lib/unwrap-lite" } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } +zerocopy = { workspace = true } + +[build-dependencies] +anyhow.workspace = true +idol.workspace = true +serde.workspace = true +quote = { workspace = true } +build-lpc55pins = { path = "../../build/lpc55pins" } +build-util = { path = "../../build/util" } + +[features] +no-ipc-counters = ["idol/no-counters"] + +[[bin]] +name = "task-button" +test = false +doctest = false +bench = false + +[lints] +workspace = true diff --git a/task/button/build.rs b/task/button/build.rs new file mode 100644 index 000000000..112715790 --- /dev/null +++ b/task/button/build.rs @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use anyhow::Result; +use build_lpc55pins::PinConfig; +use idol::{server::ServerStyle, CounterSettings}; +use serde::Deserialize; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields, rename_all = "kebab-case")] +struct TaskConfig { + pins: Vec, +} + +fn main() -> Result<()> { + build_util::expose_target_board(); + build_util::build_notifications()?; + + idol::Generator::new() + .with_counters(CounterSettings::default().with_server_counters(false)) + .build_server_support( + "../../idl/button.idol", + "server_stub.rs", + ServerStyle::InOrder, + ) + .unwrap(); + + let task_config = build_util::task_config::()?; + build_lpc55pins::codegen(task_config.pins)?; + + Ok(()) +} diff --git a/task/button/src/main.rs b/task/button/src/main.rs new file mode 100644 index 000000000..f34471797 --- /dev/null +++ b/task/button/src/main.rs @@ -0,0 +1,276 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Root of trust for reporting (RoT-R) task. +//! +//! Use the attest-api crate to interact with this task. + +#![no_std] +#![no_main] + +use button_api::ButtonError; +use drv_lpc55_gpio_api::{PintCondition, PintOp, Value}; +// use button_api::*; +use crate::idl::INCOMING_SIZE; +use idol_runtime::{ + // ClientError, Leased, LenLimit, + NotificationHandler, + RequestError, + // R, W, +}; +use userlib::{ + set_timer_relative, sys_irq_control, sys_set_timer, task_slot, TaskId, + UnwrapLite, +}; + +// Time is in approximate ms +const ON_DELAY: u32 = 1 * 1000; +const OFF_DELAY: u32 = ON_DELAY / 2; +const QUICKPRESS: u32 = 1500; + +task_slot!(GPIO, gpio_driver); + +struct ButtonServer { + gpio: drv_lpc55_gpio_api::Pins, + last_button_press: u64, + quick: usize, + + /// LED state + on: bool, + /// On ms or 0 to disable timer + on_ms: u32, + /// Off ms or 0 to disable timer + off_ms: u32, + rgb: u8, +} + +impl ButtonServer { + fn increment(&mut self) -> Result { + self.rgb = (self.rgb + 1) % 8; + let _ = self.update_leds()?; + Ok(self.rgb) + } + + fn to_rgb(v: u8) -> (bool, bool, bool) { + ((v & 0b100) != 0, (v & 0b010) != 0, (v & 0b001) != 0) + } + + fn update_leds(&self) -> Result { + let leds = self.rgb; + let (r, g, b) = if self.on { + Self::to_rgb(leds) + } else { + (false, false, false) + }; + // LED signals are active low. + self.gpio + .set_val(RED_LED, if r { Value::Zero } else { Value::One }); + self.gpio + .set_val(GREEN_LED, if g { Value::Zero } else { Value::One }); + self.gpio + .set_val(BLUE_LED, if b { Value::Zero } else { Value::One }); + Ok(leds) + } + + fn timer_expiry(&mut self) { + if self.on { + // LEDs were on + if self.off_ms > 0 { + self.on = false; + let _ = self.update_leds(); + set_timer_relative(self.off_ms, notifications::TIMER_MASK); + } else { + // no off timer; go to the next pattern and update. + let _ = self.increment(); + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } + } else { + // LEDs were off + if self.on_ms > 0 { + self.on = true; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } else { + // Leave them off and stop timer (this should be a redundant stop). + sys_set_timer(None, notifications::TIMER_MASK); + } + let _ = self.update_leds(); + } + } + + fn handle_button_press(&mut self) -> Result { + let now = userlib::sys_get_timer().now; + let last = self.last_button_press; + self.last_button_press = now; + let delta = now - last; + if delta < QUICKPRESS as u64 { + self.quick += 1; + match self.quick { + // Second press: Stop timer and turn off LEDs + 1 => { + self.on = false; + self.on_ms = 0; + self.off_ms = 0; + sys_set_timer(None, notifications::TIMER_MASK); + } + // Third press: Blink 1s on, 0.5s off. + 2 => { + self.on = true; + self.on_ms = ON_DELAY; + self.off_ms = OFF_DELAY; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } + // Forth and later presses: Increment pattern every second. + _ => { + self.on = true; + self.on_ms = ON_DELAY; + self.off_ms = 0; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + } + } + Ok(self.update_leds()?) + } else { + // This is a "first" press outside of the quick press time window. + // Make sure the LEDSs are on and increment the pattern. + self.quick = 0; + self.on = true; + self.on_ms = 0; + self.off_ms = 0; + Ok(self.increment()?) + } + } +} + +impl idl::InOrderButtonImpl for ButtonServer { + /// Simulate a button press + fn press( + &mut self, + _: &userlib::RecvMessage, + ) -> Result> { + self.handle_button_press()?; + Ok(self.rgb) + } + + fn off( + &mut self, + _: &userlib::RecvMessage, + ) -> Result<(), RequestError> { + self.rgb = 0; + let _ = self.update_leds()?; + Ok(()) + } + + fn set( + &mut self, + _: &userlib::RecvMessage, + rgb: u8, + ) -> Result<(), RequestError> { + if rgb >= 8 { + Err(ButtonError::InvalidValue.into()) + } else { + self.rgb = rgb % 8; + let _ = self.update_leds()?; + Ok(()) + } + } + + fn blink( + &mut self, + _: &userlib::RecvMessage, + on: u32, + off: u32, + ) -> Result<(), RequestError> { + self.on_ms = on; + self.off_ms = off; + self.on = true; + set_timer_relative(self.on_ms, notifications::TIMER_MASK); + let _ = self.update_leds()?; + Ok(()) + } +} + +impl NotificationHandler for ButtonServer { + fn current_notification_mask(&self) -> u32 { + notifications::TIMER_MASK + notifications::BUTTON_IRQ_MASK + } + + fn handle_notification(&mut self, bits: u32) { + if (bits & notifications::TIMER_MASK) != 0 { + self.timer_expiry() + } + + if (bits & notifications::BUTTON_IRQ_MASK) != 0 { + let detected = self + .gpio + .pint_op( + BUTTON_PINT_SLOT, + PintOp::Detected, + PintCondition::Falling, + ) + .map_or(false, |v| v.unwrap_lite()); + let _ = self.gpio.pint_op( + BUTTON_PINT_SLOT, + PintOp::Clear, + PintCondition::Falling, + ); + let _ = self.gpio.pint_op( + BUTTON_PINT_SLOT, + PintOp::Clear, + PintCondition::Status, + ); + if detected { + let _ = self.handle_button_press(); + } + sys_irq_control(notifications::BUTTON_IRQ_MASK, true); + } + } +} + +#[export_name = "main"] +fn main() -> ! { + let mut buffer = [0u8; INCOMING_SIZE]; + + let gpio_driver = GPIO.get_task_id(); + setup_pins(gpio_driver).unwrap_lite(); + let gpio = drv_lpc55_gpio_api::Pins::from(gpio_driver); + + let mut server = ButtonServer { + gpio, + quick: 0, + last_button_press: 0, + on: true, // LEDs are on + on_ms: 0, + off_ms: 0, // timer inactive, with on_ms > 0, increment + rgb: 0b111, // start with all LEDs on + }; + + // Assume the normal case where the PINT has been reset and no other + // task has fiddled our bits. + // We're not clearing any state from a possible task restart. + let _ = server.gpio.pint_op( + BUTTON_PINT_SLOT, + PintOp::Enable, + PintCondition::Falling, + ); + sys_irq_control(notifications::BUTTON_IRQ_MASK, true); + + let _ = server.update_leds(); + if server.on && server.on_ms > 0 { + set_timer_relative(server.on_ms, notifications::TIMER_MASK); + } else if !server.on && server.off_ms > 0 { + set_timer_relative(server.off_ms, notifications::TIMER_MASK); + } + + loop { + idol_runtime::dispatch(&mut buffer, &mut server); + } +} + +mod idl { + use crate::ButtonError; + + include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); +} + +include!(concat!(env!("OUT_DIR"), "/notifications.rs")); +include!(concat!(env!("OUT_DIR"), "/pin_config.rs"));