use defmt::assert;
use embassy_futures::join;
use embassy_sync::mutex::Mutex;
use embassy_sync::signal::Signal;
use crate::hw::platform::RawMutex;
use crate::hw::HIDDevice;
use crate::keyboard::KeyboardLayout;
use crate::storage::private::EmptyStorageDevice;
use crate::storage::{FlashStorage, StorageDevice, StorageKey, StorageService};
pub(crate) mod handlers;
pub(crate) mod protocol_12;
pub(crate) use protocol_12 as protocol;
pub use rumcake_macros::{connect_storage_service, setup_macro_buffer};
#[derive(Debug)]
pub struct MacroBuffer<'a, const N: usize, const S: usize> {
buffer: [u8; N],
sequences: [&'a [u8]; S],
}
impl<'a, const N: usize, const S: usize> MacroBuffer<'a, N, S> {
pub const fn new() -> Self {
Self {
buffer: [0; N],
sequences: [&[]; S],
}
}
pub fn update_buffer(&'a mut self, offset: usize, data: &[u8]) {
self.buffer[offset..(offset + data.len())].copy_from_slice(data);
let mut chunks = self.buffer.splitn(S + 1, |byte| *byte == 0);
for (i, action) in self.sequences.iter_mut().enumerate() {
if let Some(chunk) = chunks.nth(i) {
*action = chunk
}
}
}
}
pub enum BacklightType {
SimpleBacklight,
SimpleBacklightMatrix,
RGBBacklightMatrix,
}
pub trait ViaKeyboard {
type Layout: KeyboardLayout;
type StorageType: StorageDevice = EmptyStorageDevice;
fn get_storage_service() -> Option<
&'static StorageService<
'static,
<Self::StorageType as StorageDevice>::FlashStorageType,
Self::StorageType,
>,
>
where
[(); <<Self::StorageType as StorageDevice>::FlashStorageType as FlashStorage>::ERASE_SIZE]:,
{
None
}
const VIA_ENABLED: bool = true;
const VIA_FIRMWARE_VERSION: u32 = 1; const VIA_EEPROM_LAYOUT_OPTIONS_SIZE: usize = 1; const VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT: u32 = 0x00000000; const DYNAMIC_KEYMAP_LAYER_COUNT: usize = Self::Layout::LAYERS;
const DYNAMIC_KEYMAP_MACRO_COUNT: u8 = 0; const DYNAMIC_KEYMAP_MACRO_BUFFER_SIZE: u16 = 0;
const BACKLIGHT_TYPE: Option<BacklightType> = None;
fn get_macro_buffer() -> Option<
&'static mut MacroBuffer<
'static,
{ Self::DYNAMIC_KEYMAP_MACRO_BUFFER_SIZE as usize },
{ Self::DYNAMIC_KEYMAP_MACRO_COUNT as usize },
>,
> {
None
}
fn handle_via_command(data: &mut [u8]) -> bool {
false
}
fn handle_set_layout_options(updated_layout: u32) {}
fn handle_custom_value_command(data: &mut [u8], _len: u8) {
data[0] = protocol::ViaCommandId::Unhandled as u8;
}
}
pub(crate) const VIA_REPORT_DESCRIPTOR: &[u8] = &[
0x06, 0x60, 0xFF, 0x09, 0x61, 0xA1, 0x01, 0x09, 0x62, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x95, 0x20, 0x75, 0x08, 0x81, 0x02, 0x09, 0x63, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x95, 0x20, 0x75, 0x08, 0x91, 0x02, 0xC0, ];
#[rumcake_macros::task]
pub async fn via_process_task<K: ViaKeyboard + 'static, T: HIDDevice + 'static>(_k: K, _t: T)
where
[(); <<K::StorageType as StorageDevice>::FlashStorageType as FlashStorage>::ERASE_SIZE]:,
[(); K::DYNAMIC_KEYMAP_LAYER_COUNT * K::Layout::LAYOUT_COLS * K::Layout::LAYOUT_ROWS * 2]:,
[(); K::DYNAMIC_KEYMAP_LAYER_COUNT * K::Layout::NUM_ENCODERS * 2 * 2]:,
[(); (K::Layout::LAYOUT_COLS + u8::BITS as usize - 1) / u8::BITS as usize
* K::Layout::LAYOUT_ROWS]:,
[(); K::Layout::LAYERS]:,
[(); K::Layout::LAYOUT_ROWS]:,
[(); K::Layout::LAYOUT_COLS]:,
[(); K::DYNAMIC_KEYMAP_MACRO_BUFFER_SIZE as usize]:,
[(); K::DYNAMIC_KEYMAP_MACRO_COUNT as usize]:,
{
assert!(K::DYNAMIC_KEYMAP_LAYER_COUNT <= K::Layout::LAYERS);
assert!(K::DYNAMIC_KEYMAP_LAYER_COUNT <= 16);
if K::get_macro_buffer().is_some() {
assert!(
K::DYNAMIC_KEYMAP_MACRO_BUFFER_SIZE > 0,
"Macro buffer size must be greater than 0 if you are using Via macros."
);
assert!(
K::DYNAMIC_KEYMAP_MACRO_COUNT > 0,
"Macro count must be greater than 0 if you are using Via macros."
);
} else {
assert!(
K::DYNAMIC_KEYMAP_MACRO_COUNT == 0,
"Macro count should be 0 if you are not using Via macros."
);
}
let via_state: Mutex<RawMutex, protocol::ViaState<K>> = Mutex::new(Default::default());
let receive_channel = T::get_via_hid_receive_channel();
let send_channel = T::get_via_hid_send_channel();
let report_fut = async {
loop {
let mut report = receive_channel.receive().await;
if K::VIA_ENABLED {
{
let mut via_state = via_state.lock().await;
protocol::process_via_command::<K>(&mut report, &mut via_state).await;
}
send_channel.send(report).await;
}
}
};
join::join(report_fut, protocol::background_task::<K>(&via_state)).await;
}
static VIA_LAYOUT_OPTIONS: Signal<RawMutex, u32> = Signal::new();
pub async fn initialize_via_data<V: ViaKeyboard + 'static>(_v: V)
where
[(); <<V::StorageType as StorageDevice>::FlashStorageType as FlashStorage>::ERASE_SIZE]:,
[(); V::VIA_EEPROM_LAYOUT_OPTIONS_SIZE]:,
[(); V::DYNAMIC_KEYMAP_LAYER_COUNT * V::Layout::LAYOUT_COLS * V::Layout::LAYOUT_ROWS * 2]:,
[(); V::DYNAMIC_KEYMAP_LAYER_COUNT * V::Layout::NUM_ENCODERS * 2 * 2]:,
[(); V::DYNAMIC_KEYMAP_MACRO_BUFFER_SIZE as usize]:,
[(); V::DYNAMIC_KEYMAP_MACRO_COUNT as usize]:,
[(); V::Layout::LAYERS]:,
[(); V::Layout::LAYOUT_ROWS]:,
[(); V::Layout::LAYOUT_COLS]:,
{
if let Some(database) = V::get_storage_service() {
let options_metadata = [V::VIA_EEPROM_LAYOUT_OPTIONS_SIZE as u8];
let _ = database
.check_metadata(crate::storage::StorageKey::LayoutOptions, &options_metadata)
.await;
if let Ok((stored_data, stored_len)) = database
.read_raw(crate::storage::StorageKey::LayoutOptions)
.await
{
let mut bytes = [0; 4];
bytes[(4 - stored_len)..].copy_from_slice(&stored_data[..stored_len]);
VIA_LAYOUT_OPTIONS.signal(u32::from_be_bytes(bytes))
};
let layout_metadata = [
V::DYNAMIC_KEYMAP_LAYER_COUNT as u8,
V::Layout::LAYOUT_COLS as u8,
V::Layout::LAYOUT_ROWS as u8,
];
let _ = database
.check_metadata(crate::storage::StorageKey::DynamicKeymap, &layout_metadata)
.await;
if let Ok((stored_data, stored_len)) = database
.read_raw(crate::storage::StorageKey::DynamicKeymap)
.await
{
let mut layout = V::Layout::get_layout().layout.lock().await;
for byte in (0..stored_len).step_by(2) {
if let Some(action) = protocol::keycodes::convert_keycode_to_action::<V>(
u16::from_be_bytes(stored_data[byte..byte + 2].try_into().unwrap()),
) {
let layer = byte / (V::Layout::LAYOUT_ROWS * V::Layout::LAYOUT_COLS * 2);
let row = (byte / (V::Layout::LAYOUT_COLS * 2)) % V::Layout::LAYOUT_ROWS;
let col = (byte / 2) % V::Layout::LAYOUT_COLS;
layout
.change_action((row as u8, col as u8), layer, action)
.unwrap();
}
}
} else {
let mut layout = V::Layout::get_layout().layout.lock().await;
let mut buf = [0; V::DYNAMIC_KEYMAP_LAYER_COUNT
* V::Layout::LAYOUT_COLS
* V::Layout::LAYOUT_ROWS
* 2];
for byte in (0..buf.len()).step_by(2) {
let layer = byte / (V::Layout::LAYOUT_ROWS * V::Layout::LAYOUT_COLS * 2);
let row = (byte / (V::Layout::LAYOUT_COLS * 2)) % V::Layout::LAYOUT_ROWS;
let col = (byte / 2) % V::Layout::LAYOUT_COLS;
buf[(byte)..(byte + 2)].copy_from_slice(
&protocol::keycodes::convert_action_to_keycode::<V>(
layout.get_action((row as u8, col as u8), layer).unwrap(),
)
.to_be_bytes(),
);
}
let _ = database.write_raw(StorageKey::DynamicKeymap, &buf).await;
};
let encoder_metadata = [
V::DYNAMIC_KEYMAP_LAYER_COUNT as u8,
V::Layout::NUM_ENCODERS as u8,
];
let _ = database
.check_metadata(
crate::storage::StorageKey::DynamicKeymapEncoder,
&encoder_metadata,
)
.await;
let _ = database
.check_metadata(
crate::storage::StorageKey::DynamicKeymapMacro,
&layout_metadata,
)
.await;
if let Ok((stored_data, stored_len)) = database
.read_raw(crate::storage::StorageKey::DynamicKeymapMacro)
.await
{
if let Some(macro_data) = V::get_macro_buffer() {
macro_data.update_buffer(0, &stored_data[..stored_len])
}
};
}
}