diff --git a/Cargo.lock b/Cargo.lock index 8bfc35eb4..a580ead37 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2038,6 +2038,7 @@ dependencies = [ name = "drv-stm32xx-sys-api" version = "0.1.0" dependencies = [ + "build-stm32xx-sys", "byteorder", "cfg-if", "counters", diff --git a/build/stm32xx-sys/src/lib.rs b/build/stm32xx-sys/src/lib.rs index b5498a091..0018f734d 100644 --- a/build/stm32xx-sys/src/lib.rs +++ b/build/stm32xx-sys/src/lib.rs @@ -2,14 +2,16 @@ // 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::Context; use quote::{quote, TokenStreamExt}; use serde::Deserialize; -use std::collections::BTreeMap; +use std::{collections::BTreeMap, io::Write}; #[derive(Deserialize, Default)] -#[serde(rename_all = "kebab-case", deny_unknown_fields)] +#[serde(rename_all = "kebab-case")] pub struct SysConfig { /// EXTI interrupts + #[serde(default)] gpio_irqs: BTreeMap, } @@ -17,7 +19,7 @@ pub struct SysConfig { #[serde(rename_all = "kebab-case", deny_unknown_fields)] struct GpioIrqConfig { port: Port, - pin: u8, + pin: usize, owner: GpioIrqOwner, } @@ -68,6 +70,62 @@ to_tokens_enum! { } } +pub fn build_gpio_irq_pins() -> anyhow::Result<()> { + let out_dir = build_util::out_dir(); + let dest_path = out_dir.join("gpio_irq_pins.rs"); + let mut out = std::fs::File::create(&dest_path).with_context(|| { + format!("failed to create file '{}'", dest_path.display()) + })?; + + let Some(sys_config) = + build_util::other_task_full_config::("sys")?.config + else { + // No GPIO IRQs are configured; nothing left to do here! + return Ok(()); + }; + + let task = build_util::task_name(); + let pins = sys_config + .gpio_irqs + .iter() + .filter_map(|(name, cfg)| { + let &GpioIrqConfig { + pin, + port, + ref owner, + } = cfg; + // Only generate constants for pins owned by the current task. + if owner.name != task { + return None; + } + + let name = match to_const_name(name.clone()) { + Ok(name) => name, + Err(e) => return Some(Err(e)), + }; + + Some(Ok(quote! { + pub const #name: PinSet = #port.pin(#pin); + })) + }) + .collect::>>()?; + + // Don't generate an empty module if there are no pins. + if pins.is_empty() { + return Ok(()); + } + + let tokens = quote! { + pub mod gpio_irq_pins { + use drv_stm32xx_gpio_common::{PinSet, Port}; + #( #pins )* + } + }; + writeln!(out, "{tokens}")?; + + Ok(()) +} + impl SysConfig { pub fn load() -> anyhow::Result { Ok(build_util::task_maybe_config::()?.unwrap_or_default()) @@ -111,7 +169,7 @@ impl SysConfig { let task = syn::parse_str(&owner.name)?; let note = quote::format_ident!( "{}_MASK", - owner.notification.to_uppercase().replace('-', "_") + to_const_name(owner.notification.clone())? ); let name = quote::format_ident!( @@ -181,3 +239,10 @@ impl SysConfig { }) } } + +fn to_const_name(mut s: String) -> anyhow::Result { + s.make_ascii_uppercase(); + let s = s.replace("-", "_"); + syn::parse_str::(&s) + .with_context(|| format!("`{s}` is not a valid Rust identifier")) +} diff --git a/build/util/src/lib.rs b/build/util/src/lib.rs index c4f062675..5054e4767 100644 --- a/build/util/src/lib.rs +++ b/build/util/src/lib.rs @@ -42,6 +42,11 @@ pub fn target_os() -> String { std::env::var("CARGO_CFG_TARGET_OS").unwrap() } +/// Reads the `HUBRIS_TASK_NAME` env var. +pub fn task_name() -> String { + crate::env_var("HUBRIS_TASK_NAME").expect("missing HUBRIS_TASK_NAME") +} + /// Checks to see whether the given feature is enabled pub fn has_feature(s: &str) -> bool { std::env::var(format!( @@ -104,12 +109,10 @@ pub fn config() -> Result { /// Pulls the task configuration. See `config` for more details. pub fn task_config() -> Result { - let task_name = - crate::env_var("HUBRIS_TASK_NAME").expect("missing HUBRIS_TASK_NAME"); task_maybe_config()?.ok_or_else(|| { anyhow!( "app.toml missing task config section [tasks.{}.config]", - task_name + task_name() ) }) } diff --git a/drv/stm32h7-sprot-server/src/main.rs b/drv/stm32h7-sprot-server/src/main.rs index efcc639ea..d694e6037 100644 --- a/drv/stm32h7-sprot-server/src/main.rs +++ b/drv/stm32h7-sprot-server/src/main.rs @@ -97,7 +97,10 @@ const MAX_UPDATE_ATTEMPTS: u16 = 3; // On the flipside, we have learned via unintended experiment that 5ms is too short! const DUMP_TIMEOUT: u32 = 1000; -// ROT_IRQ comes from app.toml +// On Gemini, the STM32H753 is in a LQFP176 package with ROT_IRQ +// on pin2/PE3 +use drv_stm32xx_sys_api::gpio_irq_pins::ROT_IRQ; + // We use spi3 on gimletlet and spi4 on gemini and gimlet. // You should be able to move the RoT board between SPI3, SPI4, and SPI6 // without much trouble even though SPI3 is the preferred connector and @@ -117,20 +120,10 @@ cfg_if::cfg_if! { target_board = "psc-c", target_board = "gemini-bu-1" ))] { - const ROT_IRQ: sys_api::PinSet = sys_api::PinSet { - // On Gemini, the STM32H753 is in a LQFP176 package with ROT_IRQ - // on pin2/PE3 - port: sys_api::Port::E, - pin_mask: 1 << 3, - }; const ROT_SPI_DEVICE: u8 = drv_spi_api::devices::ROT; fn debug_config(_sys: &sys_api::Sys) { } fn debug_set(_sys: &sys_api::Sys, _asserted: bool) { } } else if #[cfg(target_board = "gimletlet-2")] { - const ROT_IRQ: sys_api::PinSet = sys_api::PinSet { - port: sys_api::Port::D, - pin_mask: 1 << 0, - }; const DEBUG_PIN: sys_api::PinSet = sys_api::PinSet { port: sys_api::Port::E, pin_mask: 1 << 6, @@ -151,7 +144,7 @@ cfg_if::cfg_if! { } const ROT_SPI_DEVICE: u8 = drv_spi_api::devices::SPI3_HEADER; } else { - compile_error!("No configuration for ROT_IRQ"); + compile_error!("No configuration for ROT_SPI_DEVICE"); } } diff --git a/drv/stm32xx-sys-api/Cargo.toml b/drv/stm32xx-sys-api/Cargo.toml index d46974634..60fc89b84 100644 --- a/drv/stm32xx-sys-api/Cargo.toml +++ b/drv/stm32xx-sys-api/Cargo.toml @@ -18,6 +18,7 @@ userlib = { path = "../../sys/userlib" } [build-dependencies] idol.workspace = true +build-stm32xx-sys = { path = "../../build/stm32xx-sys" } [features] family-stm32h7 = ["drv-stm32xx-gpio-common/family-stm32h7"] diff --git a/drv/stm32xx-sys-api/build.rs b/drv/stm32xx-sys-api/build.rs index 41c969ce9..4bd6cdd3f 100644 --- a/drv/stm32xx-sys-api/build.rs +++ b/drv/stm32xx-sys-api/build.rs @@ -3,6 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. fn main() -> Result<(), Box> { + build_stm32xx_sys::build_gpio_irq_pins()?; idol::client::build_client_stub( "../../idl/stm32xx-sys.idol", "client_stub.rs", diff --git a/drv/stm32xx-sys-api/src/lib.rs b/drv/stm32xx-sys-api/src/lib.rs index 5931eb230..f4fd5cc5a 100644 --- a/drv/stm32xx-sys-api/src/lib.rs +++ b/drv/stm32xx-sys-api/src/lib.rs @@ -367,3 +367,4 @@ impl From> for IrqControl { } include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); +include!(concat!(env!("OUT_DIR"), "/gpio_irq_pins.rs")); diff --git a/task/nucleo-user-button/src/main.rs b/task/nucleo-user-button/src/main.rs index ae3acf667..8f85c4c5f 100644 --- a/task/nucleo-user-button/src/main.rs +++ b/task/nucleo-user-button/src/main.rs @@ -11,7 +11,7 @@ #![no_std] #![no_main] -use drv_stm32xx_sys_api::{Edge, IrqControl, PinSet, Port, Pull}; +use drv_stm32xx_sys_api::{Edge, IrqControl, Pull}; use ringbuf::ringbuf_entry; use userlib::*; @@ -72,10 +72,7 @@ pub fn main() -> ! { }); sys.gpio_configure_input( - PinSet { - port: Port::C, - pin_mask: (1 << 13), - }, + drv_stm32xx_sys_api::gpio_irq_pins::BUTTON, Pull::None, );