//! Bootloader entry helpers shared between power-on checks and runtime combos. use crate::{KeyMatrix, StatusLed, layout, status::StatusMode}; #[cfg(all(test, feature = "std"))] use crate::NUMBER_OF_KEYS; use cortex_m::asm; use rp2040_hal::{ gpio::AnyPin, pio::{PIOExt, StateMachineIndex}, }; use usbd_human_interface_device::page::Keyboard; /// Returns true when the runtime bootloader chord is held. /// /// The chord requires two Fn buttons, both Shift buttons and the primary Ctrl. pub fn chord_requested(pressed_keys: &KeyMatrix) -> bool { if !modifier_pressed(pressed_keys, Keyboard::LeftShift) || !modifier_pressed(pressed_keys, Keyboard::RightShift) { return false; } if !modifier_pressed(pressed_keys, Keyboard::LeftControl) { return false; } let active_fn = layout::FN_BUTTONS .iter() .filter(|index| pressed_keys[**index as usize]) .count(); active_fn >= 2 } fn modifier_pressed(pressed_keys: &KeyMatrix, key: Keyboard) -> bool { layout::MAP[0] .iter() .enumerate() .any(|(index, mapped)| *mapped == key && pressed_keys[index]) } /// Puts the RP2040 into the ROM bootloader. pub fn enter(status_led: &mut StatusLed) -> ! where P: PIOExt, SM: StateMachineIndex, I: AnyPin, { status_led.update(StatusMode::Bootloader); let gpio_activity_pin_mask: u32 = 0; let disable_interface_mask: u32 = 0; rp2040_hal::rom_data::reset_to_usb_boot(gpio_activity_pin_mask, disable_interface_mask); loop { asm::nop(); } } #[cfg(all(test, feature = "std"))] mod tests { use super::*; #[test] fn chord_requires_all_modifier_keys_and_two_fn() { let mut pressed = [false; NUMBER_OF_KEYS]; for (index, key) in layout::MAP[0].iter().enumerate() { match key { Keyboard::LeftShift | Keyboard::RightShift | Keyboard::LeftControl => { pressed[index] = true; } _ => {} } } pressed[layout::FN_BUTTONS[0] as usize] = true; assert!(!chord_requested(&pressed)); pressed[layout::FN_BUTTONS[1] as usize] = true; assert!(chord_requested(&pressed)); } }