diff --git a/rumcake-macros/src/keyboard.rs b/rumcake-macros/src/keyboard.rs index c62b159..763c50d 100644 --- a/rumcake-macros/src/keyboard.rs +++ b/rumcake-macros/src/keyboard.rs @@ -25,6 +25,7 @@ pub(crate) struct KeyboardSettings { split_central: Option, via: Option>, vial: Option>, + bootloader_double_tap_reset: Option>, } #[derive(Debug, FromMeta, Default)] @@ -671,6 +672,23 @@ pub(crate) fn keyboard_main( } } + if let Some(arg) = keyboard.bootloader_double_tap_reset { + let timeout = arg.unwrap_or(200); + + if timeout == 0 { + initialization.extend(quote! { + compile_error!("The timeout for double tapping the reset button should be > 0"); + }) + } + + spawning.extend(quote! { + unsafe { + ::rumcake::hw::check_double_tap_bootloader(#timeout).await; + } + }); + } + + let final_traits = traits.values(); quote! { diff --git a/rumcake/src/hw/mcu/rp.rs b/rumcake/src/hw/mcu/rp.rs index 25d3782..d67215a 100644 --- a/rumcake/src/hw/mcu/rp.rs +++ b/rumcake/src/hw/mcu/rp.rs @@ -10,6 +10,7 @@ use embassy_rp::config::Config; use embassy_rp::flash::Async; use embassy_rp::flash::Flash as HALFlash; use embassy_rp::peripherals::{FLASH, USB}; +use embassy_rp::rom_data::reset_to_usb_boot; use embassy_rp::usb::Driver; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; @@ -25,7 +26,7 @@ pub type RawMutex = ThreadModeRawMutex; /// A function that allows you to jump to the bootloader, usually for re-flashing the firmware. pub fn jump_to_bootloader() { - // TODO + reset_to_usb_boot(0, 0); } /// Initialize the MCU's internal clocks. diff --git a/rumcake/src/hw/mod.rs b/rumcake/src/hw/mod.rs index 08f3cf4..9a46778 100644 --- a/rumcake/src/hw/mod.rs +++ b/rumcake/src/hw/mod.rs @@ -15,9 +15,15 @@ compile_error!("Please enable only one chip feature flag."); #[cfg_attr(feature = "rp", path = "mcu/rp.rs")] pub mod mcu; +use core::ptr::read_volatile; +use core::ptr::write_volatile; +use core::mem::MaybeUninit; +use core::cell::UnsafeCell; +use crate::hw::mcu::jump_to_bootloader; use crate::State; use embassy_futures::select; use embassy_sync::signal::Signal; +use embassy_time::Timer; use mcu::RawMutex; @@ -123,6 +129,25 @@ pub async fn output_switcher() { } } +const BOOTLOADER_MAGIC: u32 = 0xDEADBEEF; + +#[link_section = ".uninit.FLAG"] +static mut FLAG: UnsafeCell> = UnsafeCell::new(MaybeUninit::uninit()); + +pub async unsafe fn check_double_tap_bootloader(timeout: u64) { + if read_volatile(FLAG.get().cast::()) == BOOTLOADER_MAGIC { + write_volatile(FLAG.get().cast(), 0); + + jump_to_bootloader(); + } + + write_volatile(FLAG.get().cast(), BOOTLOADER_MAGIC); + + Timer::after_millis(timeout).await; + + write_volatile(FLAG.get().cast(), 0); +} + extern "C" { /// This static value will have an address equal to the `__config_start` address in your /// `memory.x` file. You must set this, along with [`__config_end`], if you're using on-chip