diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 39b2af8..06e9916 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -30,6 +30,7 @@ dependencies = [ "esp-backtrace", "esp-bootloader-esp-idf", "esp-hal", + "esp-metadata-generated", "esp-println", "esp-radio", "esp-rtos", @@ -37,7 +38,8 @@ dependencies = [ "esp-sync", "gix", "hmac", - "i-slint-common", + "i-slint-common 1.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "i-slint-core", "indoc", "itertools 0.14.0", "json", @@ -59,6 +61,7 @@ dependencies = [ "slint-build", "spectre-api-sys", "static_cell", + "tinyvec", "xkbcommon 0.9.0 (git+https://github.com/Limeth/xkbcommon-rs?branch=esp32s3)", "xz2", ] @@ -954,8 +957,6 @@ checksum = "0b396d1f76d455557e1218ec8066ae14bba60b4b36ecd55577ba979f5db7ecaa" [[package]] name = "const-field-offset" version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fcde4ca1211b5a94b573083c472ee19e86b19a441913f66e1cc5c41daf0255" dependencies = [ "const-field-offset-macro", "field-offset", @@ -964,8 +965,6 @@ dependencies = [ [[package]] name = "const-field-offset-macro" version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5387f5bbc9e9e6c96436ea125afa12614cebf8ac67f49abc08c1e7a891466c90" dependencies = [ "proc-macro2", "quote", @@ -3823,13 +3822,11 @@ dependencies = [ [[package]] name = "i-slint-backend-linuxkms" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8fd06c00fbdac3dd490cf5c10da7daad3820d775060a19ea277d8ab944a160b" dependencies = [ "bytemuck", "calloop 0.14.3", "drm", - "i-slint-common", + "i-slint-common 1.14.1", "i-slint-core", "input", "memmap2", @@ -3840,13 +3837,11 @@ dependencies = [ [[package]] name = "i-slint-backend-selector" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e138660c634d6bbdf98bb2d0cfa487cda28e032e133a2a2c974f1cc494198765" dependencies = [ "cfg-if", "i-slint-backend-linuxkms", "i-slint-backend-winit", - "i-slint-common", + "i-slint-common 1.14.1", "i-slint-core", "i-slint-core-macros", ] @@ -3854,8 +3849,6 @@ dependencies = [ [[package]] name = "i-slint-backend-winit" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69bf9167fb1165942ef1f034e039645b60b07b23c0c76069cb83595f979243a4" dependencies = [ "bytemuck", "cfg-if", @@ -3863,7 +3856,7 @@ dependencies = [ "copypasta", "derive_more", "futures", - "i-slint-common", + "i-slint-common 1.14.1", "i-slint-core", "i-slint-core-macros", "i-slint-renderer-skia", @@ -3889,25 +3882,27 @@ dependencies = [ [[package]] name = "i-slint-common" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3566194c13f8dcf6e9f41a2090c96f08cf3f59b60c91380a86c1ed72f6e7d19" dependencies = [ "fontique", "ttf-parser 0.25.1", ] [[package]] -name = "i-slint-compiler" +name = "i-slint-common" version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a301031f6c1da6acdd483641cc44109b34990e5edd67478eb0cbca359ab7ae3f" +checksum = "b3566194c13f8dcf6e9f41a2090c96f08cf3f59b60c91380a86c1ed72f6e7d19" + +[[package]] +name = "i-slint-compiler" +version = "1.14.1" dependencies = [ "by_address", "codemap", "codemap-diagnostic", "derive_more", "fontdue", - "i-slint-common", + "i-slint-common 1.14.1", "image", "itertools 0.14.0", "linked_hash_set", @@ -3929,8 +3924,6 @@ dependencies = [ [[package]] name = "i-slint-core" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc140f1218cfc4451b9e8753306c42afbcaf0386cc888e53664c1a5f5330ae19" dependencies = [ "auto_enums", "bitflags 2.10.0", @@ -3942,7 +3935,7 @@ dependencies = [ "derive_more", "euclid", "fontdue", - "i-slint-common", + "i-slint-common 1.14.1", "i-slint-core-macros", "image", "integer-sqrt", @@ -3977,8 +3970,6 @@ dependencies = [ [[package]] name = "i-slint-core-macros" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4c6a3975ccaa66415f5524292750e631879e69178aa97e3928d2396b790d00d" dependencies = [ "quote", "serde_json", @@ -3988,8 +3979,6 @@ dependencies = [ [[package]] name = "i-slint-renderer-skia" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26ed0d42d66c457b7937d7f5ee07d2fbef3691c896a26212a69b30288844ddd" dependencies = [ "bytemuck", "cfg-if", @@ -3998,7 +3987,7 @@ dependencies = [ "derive_more", "glow", "glutin", - "i-slint-common", + "i-slint-common 1.14.1", "i-slint-core", "i-slint-core-macros", "lyon_path", @@ -6745,8 +6734,6 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slint" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c35c4bdca2c42c69b21ceb416aa4ba76c3f54df30e9ce85dcad0742229422a6" dependencies = [ "const-field-offset", "i-slint-backend-selector", @@ -6776,8 +6763,6 @@ dependencies = [ [[package]] name = "slint-macros" version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b4b4bada19852716991153ddd93bf133548ac74383a05db8df399b9003bbfe" dependencies = [ "i-slint-compiler", "proc-macro2", @@ -7685,8 +7670,6 @@ dependencies = [ [[package]] name = "vtable" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "753be81c38dff787d177b5939af1fa16f72f0d0d21a6b7d74ae56e29cd26f2a6" dependencies = [ "const-field-offset", "portable-atomic", @@ -7697,8 +7680,6 @@ dependencies = [ [[package]] name = "vtable-macro" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfcf6171aa2b0f85718ca5888ca32f6edf61d1849f8e4b3786ad890e5b68f68" dependencies = [ "proc-macro2", "quote", diff --git a/firmware/acid-firmware/Cargo.toml b/firmware/acid-firmware/Cargo.toml index 17ca028..ac0305b 100644 --- a/firmware/acid-firmware/Cargo.toml +++ b/firmware/acid-firmware/Cargo.toml @@ -68,6 +68,7 @@ itertools = { version = "0.14.0", default-features = false } bytemuck = "1.24.0" slint = { version = "1.14.1", default-features = false, features = ["compat-1-2", "libm", "log", "unsafe-single-threaded", "renderer-software"]} i-slint-common = "1.14.1" +i-slint-core = { version = "1.14.1", default-features = false } critical-section = "1.2.0" cfg-if = "1.0.4" xkbcommon = { git = "https://github.com/Limeth/xkbcommon-rs", branch = "esp32s3", default-features = false, features = ["c-lib-wrap"] } @@ -86,6 +87,8 @@ serde = { version = "1.0", default-features = false, features = ["derive"] } # serde_with = { version = "3.16", default-features = false, features = ["alloc", "macros"] } serde_bytes = { version = "0.11.19", default-features = false, features = ["alloc"] } chrono = { version = "0.4.43", default-features = false, features = ["alloc", "serde"] } # TODO: defmt +tinyvec = { version = "1.10.0", default-features = false, features = ["alloc"] } +esp-metadata-generated = { version = "0.3.0", features = ["esp32s3"] } # Crates for serial UART CLI embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] } diff --git a/firmware/acid-firmware/src/ffi/alloc.rs b/firmware/acid-firmware/src/ffi/alloc.rs index 91f9d21..17e4adb 100644 --- a/firmware/acid-firmware/src/ffi/alloc.rs +++ b/firmware/acid-firmware/src/ffi/alloc.rs @@ -4,12 +4,11 @@ use core::alloc::GlobalAlloc; use core::ffi::{c_size_t, c_void}; use enumset::EnumSet; -use esp_alloc::EspHeap; use crate::ffi::string::__xkbc_memcpy; // Here we select the allocator to use for libxkbcommon. -static XKBC_ALLOCATOR: &EspHeap = &crate::PSRAM_ALLOCATOR; +pub use crate::PSRAM_ALLOCATOR as XKBC_ALLOCATOR; // Implementation based on esp-alloc's `compat` feature. diff --git a/firmware/acid-firmware/src/main.rs b/firmware/acid-firmware/src/main.rs index cba4b81..29b4bb9 100644 --- a/firmware/acid-firmware/src/main.rs +++ b/firmware/acid-firmware/src/main.rs @@ -30,8 +30,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; -use embassy_time::Duration; -use esp_alloc::{HeapRegion, MemoryCapability}; +use embassy_time::{Duration, Timer}; +use esp_alloc::{HeapRegion, MemoryCapability, RegionStats}; use esp_hal::Blocking; use esp_hal::clock::CpuClock; use esp_hal::dma::{BurstConfig, DmaDescriptor, DmaTxBuf, ExternalBurstConfig}; @@ -159,7 +159,11 @@ async fn main(_spawner: Spawner) { // Use the internal DRAM as the heap. // Memory reclaimed from the esp-idf bootloader. - const HEAP_SIZE_RECLAIMED: usize = 72 * 1024; + const HEAP_SIZE_RECLAIMED: usize = const { + let range = esp_metadata_generated::memory_range!("DRAM2_UNINIT"); + range.end - range.start + }; + esp_alloc::heap_allocator!(#[ram(reclaimed)] size: HEAP_SIZE_RECLAIMED); esp_alloc::heap_allocator!(size: HEAP_SIZE - HEAP_SIZE_RECLAIMED); info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); @@ -424,6 +428,7 @@ async fn main(_spawner: Spawner) { // TODO: Probably want to select! instead and re-try. join_all![ + run_alloc_stats_reporter(), // We currently send the framebuffer data using the main core, which does not seem to slow // down the rest of the tasks too much. run_lcd(st7701s, framebuffer), @@ -447,6 +452,34 @@ async fn main(_spawner: Spawner) { .await; } +async fn run_alloc_stats_reporter() { + let mut psram_used_prev = 0; + let mut heap_used_prev = 0; + loop { + let psram_stats = PSRAM_ALLOCATOR.stats(); + let heap_stats = esp_alloc::HEAP.stats(); + if psram_stats.current_usage != psram_used_prev { + let difference = psram_stats.current_usage as isize - psram_used_prev as isize; + psram_used_prev = psram_stats.current_usage; + warn!( + "PSRAM usage changed: {}{}\n{psram_stats}", + if difference < 0 { '-' } else { '+' }, + difference.abs() + ); + } + if heap_stats.current_usage != heap_used_prev { + let difference = heap_stats.current_usage as isize - heap_used_prev as isize; + heap_used_prev = heap_stats.current_usage; + warn!( + "HEAP usage changed: {}{}\n{heap_stats}", + if difference < 0 { '-' } else { '+' }, + difference.abs() + ); + } + Timer::after_secs(1).await; + } +} + struct UserController { sub: ControllerSub, } @@ -551,7 +584,7 @@ async fn run_lcd(mut st7701s: St7701s<'static, Blocking>, framebuffer: &'static // TODO: Use bounce buffers: // https://docs.espressif.com/projects/esp-idf/en/v5.0/esp32s3/api-reference/peripherals/lcd.html#bounce-buffer-with-single-psram-frame-buffer - // They need to be implemented in esp-hal. + // This can be implemented as a `DmaTxBuffer`. let transfer = match st7701s.dpi.send(false, framebuffer.dma_buf.take().unwrap()) { Err((error, result_dpi, result_dma_buf)) => { error!( diff --git a/firmware/acid-firmware/src/peripherals/st7701s/lcd.rs b/firmware/acid-firmware/src/peripherals/st7701s/lcd.rs index ab9553c..03969db 100644 --- a/firmware/acid-firmware/src/peripherals/st7701s/lcd.rs +++ b/firmware/acid-firmware/src/peripherals/st7701s/lcd.rs @@ -107,6 +107,19 @@ pub async fn spi_read( use crate::peripherals::st7701s::*; +// struct InitSequenceAction { +// command: ArrayCommand<8>, +// sleep: u64, +// } + +// pub const INIT_SEQUENCE: [InitSequenceAction; _] = [CmdCn2bkxsel( +// CmdCn2bkxselArg0::new(), +// CmdCn2bkxselArg1::new(), +// CmdCn2bkxselArg2::new(), +// CmdCn2bkxselArg3::new(), +// CmdCn2bkxselArg4::new().with_bksel(0x3).with_cn2(true), +// )]; + lazy_static! { pub static ref INIT_SEQUENCE_COMMANDS: Vec<(Vec>, u64)> = vec![ (vec![ diff --git a/firmware/acid-firmware/src/peripherals/st7701s/mod.rs b/firmware/acid-firmware/src/peripherals/st7701s/mod.rs index 23f73e5..6543176 100644 --- a/firmware/acid-firmware/src/peripherals/st7701s/mod.rs +++ b/firmware/acid-firmware/src/peripherals/st7701s/mod.rs @@ -3,13 +3,15 @@ use esp_hal::{ DriverMode, gpio::{Flex, Level, Output}, lcd_cam::lcd::{ - ClockMode, DelayMode, Phase, Polarity, dpi::{Dpi, Format, FrameTiming} + ClockMode, DelayMode, Phase, Polarity, + dpi::{Dpi, Format, FrameTiming}, }, time::Rate, }; use lcd::spi_write; use log::debug; use paste::paste; +// use tinyvec::ArrayVec; mod lcd; @@ -18,6 +20,30 @@ pub trait Command { fn args_iter(&'_ self) -> core::slice::Iter<'_, u8>; } +// pub struct ArrayCommand { +// pub address: u8, +// pub args: ArrayVec<[u8; N]>, +// } + +// impl const ArrayCommand { +// const fn from(command: impl Command) -> Self { +// Self { +// address: command.address(), +// args: command.args_iter().copied().collect(), +// } +// } +// } + +// impl Command for ArrayCommand { +// fn address(&self) -> u8 { +// self.address +// } + +// fn args_iter(&'_ self) -> core::slice::Iter<'_, u8> { +// self.args.iter() +// } +// } + pub struct CustomCommand { pub address: u8, pub args: &'static [u8], diff --git a/firmware/acid-firmware/src/ui/backend.rs b/firmware/acid-firmware/src/ui/backend.rs index 3f76e89..ac3ea35 100644 --- a/firmware/acid-firmware/src/ui/backend.rs +++ b/firmware/acid-firmware/src/ui/backend.rs @@ -31,8 +31,6 @@ impl slint::platform::Platform for SlintBackend { fn create_window_adapter( &self, ) -> Result, slint::PlatformError> { - // TODO: Custom window adapter impl needs to be implemented, so we can change `rotation` on - // `SoftwareRenderer`. let renderer = SoftwareRenderer::new_with_repaint_buffer_type( RepaintBufferType::ReusedBuffer, /* TODO: Implement a swapchain */ ); diff --git a/firmware/acid-firmware/src/ui/mod.rs b/firmware/acid-firmware/src/ui/mod.rs index 902ad10..3840e42 100644 --- a/firmware/acid-firmware/src/ui/mod.rs +++ b/firmware/acid-firmware/src/ui/mod.rs @@ -40,7 +40,7 @@ use spectre_api_sys::{ #[cfg(feature = "limit-fps")] use crate::FRAME_DURATION_MIN; use crate::{ - SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER, + PSRAM_ALLOCATOR, SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER, db::{ AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers, PartitionAcid, ReadTransactionExt, @@ -187,95 +187,99 @@ fn spectre_derive_user_key(username: &CStr, password: &CStr) -> SpectreUserKey { pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: PartitionAcid) { let db = AcidDatabase::mount(flash_part_acid).await; - let value = SpectreUsersConfig { - users: vec![SpectreUserConfig { - username: "test".to_string(), - encrypted_key: [0; _], - key_id: [0; _], - }], - }; + // let value = SpectreUsersConfig { + // users: vec![SpectreUserConfig { + // username: "test".to_string(), + // encrypted_key: [0; _], + // key_id: [0; _], + // }], + // }; - { - let mut write = db.write_transaction().await; - write - .write( - &DbKey::new(DbPathSpectreUserSite { - user_sites: DbPathSpectreUserSites { - username: "test".into(), - }, - site: "example.org".into(), - }), - &postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(), - ) - .await - .unwrap(); - write - .write( - &DbKey::new(DbPathSpectreUserSite { - user_sites: DbPathSpectreUserSites { - username: "test".into(), - }, - site: "sub.example.org".into(), - }), - &postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(), - ) - .await - .unwrap(); - write - .write( - &DbKey::new(DbPathSpectreUsers), - &postcard::to_allocvec(&value).unwrap(), - ) - .await - .unwrap(); - write.commit().await.unwrap(); - } + // { + // let mut write = db.write_transaction().await; + // write + // .write( + // &DbKey::new(DbPathSpectreUserSite { + // user_sites: DbPathSpectreUserSites { + // username: "test".into(), + // }, + // site: "example.org".into(), + // }), + // &postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(), + // ) + // .await + // .unwrap(); + // write + // .write( + // &DbKey::new(DbPathSpectreUserSite { + // user_sites: DbPathSpectreUserSites { + // username: "test".into(), + // }, + // site: "sub.example.org".into(), + // }), + // &postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(), + // ) + // .await + // .unwrap(); + // write + // .write( + // &DbKey::new(DbPathSpectreUsers), + // &postcard::to_allocvec(&value).unwrap(), + // ) + // .await + // .unwrap(); + // write.commit().await.unwrap(); + // } - let read_value = { - let read = db.read_transaction().await; - // TODO: https://github.com/embassy-rs/ekv/issues/20 - let mut buffer = vec![0; 256]; - let slice = read - .read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer) - .await - .unwrap(); - let read_value = postcard::from_bytes::(&slice).unwrap(); + // let read_value = { + // let read = db.read_transaction().await; + // // TODO: https://github.com/embassy-rs/ekv/issues/20 + // let mut buffer = vec![0; 256]; + // let slice = read + // .read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer) + // .await + // .unwrap(); + // let read_value = postcard::from_bytes::(&slice).unwrap(); - let key_sites = DbKey::new(DbPathSpectreUserSites { - username: "test".into(), - }); - let mut key_buffer = [0_u8; ekv::config::MAX_KEY_SIZE]; - let mut cursor = read - .read_range(key_sites.range_of_children()) - .await - .unwrap(); + // let key_sites = DbKey::new(DbPathSpectreUserSites { + // username: "test".into(), + // }); + // let mut key_buffer = [0_u8; ekv::config::MAX_KEY_SIZE]; + // let mut cursor = read + // .read_range(key_sites.range_of_children()) + // .await + // .unwrap(); - while let Some((key_len, value_len)) = - cursor.next(&mut key_buffer, &mut buffer).await.unwrap() - { - let key = DbKey::from_raw(key_buffer[..key_len].into()); - let mut key_segments = key.segments(); - let value = &buffer[..value_len]; - let site_config = postcard::from_bytes::(&value).unwrap(); - let site = SpectreSite { - config: site_config, - username: key_segments.nth(2).unwrap().into(), - site_name: key_segments.nth(1).unwrap().into(), - }; + // while let Some((key_len, value_len)) = + // cursor.next(&mut key_buffer, &mut buffer).await.unwrap() + // { + // let key = DbKey::from_raw(key_buffer[..key_len].into()); + // let mut key_segments = key.segments(); + // let value = &buffer[..value_len]; + // let site_config = postcard::from_bytes::(&value).unwrap(); + // let site = SpectreSite { + // config: site_config, + // username: key_segments.nth(2).unwrap().into(), + // site_name: key_segments.nth(1).unwrap().into(), + // }; - info!("site = {:#?}", site); - } + // info!("site = {:#?}", site); + // } - read_value - }; + // read_value + // }; - info!("read_value = {:#?}", read_value); - assert_eq!(value, read_value, "values do not match"); + // info!("read_value = {:#?}", read_value); + // assert_eq!(value, read_value, "values do not match"); // TODO: // * Store a config as a versioned postcard-serialized struct // * Store accounts and sites as ranges in the DB + i_slint_core::properties::ALLOCATOR + .set(&PSRAM_ALLOCATOR) + .ok() + .unwrap(); slint::platform::set_platform(Box::new(backend)).expect("backend already initialized"); struct State { @@ -432,6 +436,7 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition .into() }; + warn!("Identicon: {identicon} ({identicon:?})"); state.window.set_user_edit_identicon(identicon.clone()); state.state_user_edit.hashed = Some(ProposedPassword { encrypted_key, diff --git a/firmware/acid-firmware/ui/main.slint b/firmware/acid-firmware/ui/main.slint index 28c723e..d0ba5bc 100644 --- a/firmware/acid-firmware/ui/main.slint +++ b/firmware/acid-firmware/ui/main.slint @@ -28,7 +28,7 @@ export enum AppState { export component AppWindow inherits Window { // Special characters to generate pre-render glyphs for. in property dummy: "ÄÖÜÁÉÍÓÚÝŔŚĹŹĆŃĚĽŽŠČŘĎŤŇŮÅäöüáéíóúýŕśĺźćńěľžščřďťňůåß„“”‘’—–@&$%+=¡¿¢£$¥€²³¼½¬¤¦§©®™°´ˇ¨"; - // in property dummy_identicon_symbols: "╔╚╰═█░▒▓☺☻╗╝╯═◈◎◐◑◒◓☀☁☂☃☄★☆☎☏⎈⌂☘☢☣☕⌚⌛⏰⚡⛄⛅☔♔♕♖♗♘♙♚♛♜♝♞♟♨♩♪♫⚐⚑⚔⚖⚙⚠⌘⏎✄✆✈✉✌"; + in property dummy_identicon_symbols: "╔╚╰═█░▒▓☺☻╗╝╯═◈◎◐◑◒◓☀☁☂☃☄★☆☎☏⎈⌂☘☢☣☕⌚⌛⏰⚡⛄⛅☔♔♕♖♗♘♙♚♛♜♝♞♟♨♩♪♫⚐⚑⚔⚖⚙⚠⌘⏎✄✆✈✉✌"; default-font-family: "IBM Plex Mono"; default-font-size: 16pt; height: 368px;