#[cfg(all(not(feature = "stm32"), not(feature = "nrf"), not(feature = "rp")))]
compile_error!("Please enable the appropriate feature flag for the chip you're using.");
#[cfg(any(
all(feature = "stm32", feature = "nrf"),
all(feature = "nrf", feature = "rp"),
all(feature = "rp", feature = "stm32")
))]
compile_error!("Please enable only one chip feature flag.");
#[cfg_attr(feature = "stm32", path = "mcu/stm32.rs")]
#[cfg_attr(feature = "nrf", path = "mcu/nrf.rs")]
#[cfg_attr(feature = "rp", path = "mcu/rp.rs")]
pub mod platform;
use crate::hw::platform::jump_to_bootloader;
use crate::State;
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ptr::read_volatile;
use core::ptr::write_volatile;
use defmt::info;
use embassy_futures::select;
use embassy_sync::channel::Channel;
use embassy_sync::signal::Signal;
use embassy_time::Timer;
use embedded_hal::digital::v2::OutputPin;
use platform::RawMutex;
use usbd_human_interface_device::device::consumer::MultipleConsumerReport;
use usbd_human_interface_device::device::keyboard::NKROBootKeyboardReport;
pub static BATTERY_LEVEL_STATE: State<u8> = State::new(
100,
&[
#[cfg(feature = "display")]
&crate::display::BATTERY_LEVEL_LISTENER,
#[cfg(feature = "bluetooth")]
&crate::bluetooth::BATTERY_LEVEL_LISTENER,
],
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum OutputMode {
Usb,
Bluetooth,
}
pub static OUTPUT_MODE_STATE: State<OutputMode> = State::new(
if cfg!(feature = "bluetooth") {
OutputMode::Bluetooth
} else {
OutputMode::Usb
},
&[
&OUTPUT_MODE_STATE_LISTENER,
#[cfg(feature = "display")]
&crate::display::OUTPUT_MODE_STATE_LISTENER,
],
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HIDOutput {
Usb,
Bluetooth,
}
pub static CURRENT_OUTPUT_STATE: State<Option<HIDOutput>> = State::new(
None,
&[
#[cfg(feature = "usb")]
&crate::usb::KB_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(feature = "usb")]
&crate::usb::CONSUMER_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(all(feature = "usb", feature = "via"))]
&crate::usb::VIA_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(feature = "bluetooth")]
&crate::bluetooth::CURRENT_OUTPUT_STATE_LISTENER,
],
);
pub(crate) static OUTPUT_MODE_STATE_LISTENER: Signal<RawMutex, ()> = Signal::new();
pub(crate) static USB_RUNNING_STATE_LISTENER: Signal<RawMutex, ()> = Signal::new();
pub(crate) static BLUETOOTH_CONNECTED_STATE_LISTENER: Signal<RawMutex, ()> = Signal::new();
pub(crate) static HARDWARE_COMMAND_CHANNEL: Channel<RawMutex, HardwareCommand, 2> = Channel::new();
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum HardwareCommand {
ToggleOutput = 0,
OutputUSB = 1,
OutputBluetooth = 2,
}
#[rumcake_macros::task]
pub async fn output_switcher() {
let switcher_fut = async {
loop {
let output = match OUTPUT_MODE_STATE.get().await {
#[cfg(feature = "usb")]
OutputMode::Usb => {
if crate::usb::USB_RUNNING_STATE.get().await {
Some(HIDOutput::Usb)
} else {
None
}
}
#[cfg(feature = "bluetooth")]
OutputMode::Bluetooth => {
if crate::bluetooth::BLUETOOTH_CONNECTED_STATE.get().await {
Some(HIDOutput::Bluetooth)
} else {
None
}
}
#[allow(unreachable_patterns)]
_ => None,
};
CURRENT_OUTPUT_STATE.set(output).await;
info!("[HW] Output updated: {:?}", defmt::Debug2Format(&output));
select::select3(
USB_RUNNING_STATE_LISTENER.wait(),
BLUETOOTH_CONNECTED_STATE_LISTENER.wait(),
OUTPUT_MODE_STATE_LISTENER.wait(),
)
.await;
}
};
let command_fut = async {
loop {
match HARDWARE_COMMAND_CHANNEL.receive().await {
HardwareCommand::ToggleOutput => {
OUTPUT_MODE_STATE.set(match OUTPUT_MODE_STATE.get().await {
OutputMode::Usb => OutputMode::Bluetooth,
OutputMode::Bluetooth => OutputMode::Usb,
});
}
HardwareCommand::OutputUSB => {
OUTPUT_MODE_STATE.set(OutputMode::Usb);
}
HardwareCommand::OutputBluetooth => {
OUTPUT_MODE_STATE.set(OutputMode::Bluetooth);
}
}
}
};
select::select(switcher_fut, command_fut).await;
info!("[HW] Output switching task has failed. This should not happen.");
}
const BOOTLOADER_MAGIC: u32 = 0xDEADBEEF;
#[link_section = ".uninit.FLAG"]
static mut FLAG: UnsafeCell<MaybeUninit<u32>> = UnsafeCell::new(MaybeUninit::uninit());
pub async unsafe fn check_double_tap_bootloader(timeout: u64) {
if read_volatile(FLAG.get().cast::<u32>()) == 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" {
pub static __config_start: u32;
pub static __config_end: u32;
}
pub struct Multiplexer<T, const P: usize> {
cur_channel: u8,
pins: [Option<T>; P],
en: Option<T>,
}
impl<E, T: OutputPin<Error = E>, const P: usize> Multiplexer<T, P> {
pub fn new(pins: [Option<T>; P], en: Option<T>) -> Self {
let mut multiplexer = Self {
pins,
en,
cur_channel: 0,
};
let _ = multiplexer.select_channel(0);
multiplexer
}
pub fn select_channel(&mut self, mut channel: u8) -> Result<(), E> {
for i in 0..P {
if let Some(ref mut pin) = self.pins[i] {
if channel & 0x01 == 0x01 {
pin.set_high()?;
} else {
pin.set_low()?;
}
}
channel >>= 1;
}
self.cur_channel = channel;
Ok(())
}
pub fn enable(&mut self) -> Result<(), E> {
if let Some(ref mut en) = self.en {
en.set_low()?;
}
Ok(())
}
pub fn disable(&mut self) -> Result<(), E> {
if let Some(ref mut en) = self.en {
en.set_high()?;
}
Ok(())
}
}
pub trait HIDDevice {
fn get_keyboard_report_send_channel() -> &'static Channel<RawMutex, NKROBootKeyboardReport, 1> {
static KEYBOARD_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, NKROBootKeyboardReport, 1> =
Channel::new();
&KEYBOARD_REPORT_HID_SEND_CHANNEL
}
fn get_consumer_report_send_channel() -> &'static Channel<RawMutex, MultipleConsumerReport, 1> {
static CONSUMER_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, MultipleConsumerReport, 1> =
Channel::new();
&CONSUMER_REPORT_HID_SEND_CHANNEL
}
#[cfg(feature = "via")]
fn get_via_hid_send_channel() -> &'static Channel<RawMutex, [u8; 32], 1> {
static VIA_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, [u8; 32], 1> = Channel::new();
&VIA_REPORT_HID_SEND_CHANNEL
}
#[cfg(feature = "via")]
fn get_via_hid_receive_channel() -> &'static Channel<RawMutex, [u8; 32], 1> {
static VIA_REPORT_HID_RECEIVE_CHANNEL: Channel<RawMutex, [u8; 32], 1> = Channel::new();
&VIA_REPORT_HID_RECEIVE_CHANNEL
}
}