cmdr-keyboard/rp2040/src/bootloader.rs

81 lines
2.3 KiB
Rust

//! 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<P, SM, I>(status_led: &mut StatusLed<P, SM, I>) -> !
where
P: PIOExt,
SM: StateMachineIndex,
I: AnyPin<Function = P::PinFunction>,
{
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() {
// Confirm the bootloader chord only fires when both Shifts, Ctrl, and two Fn keys are held.
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));
}
}