This commit is contained in:
Jakub Hlusička 2026-01-05 04:16:08 +01:00
parent 561be96f24
commit 352e52852f
17 changed files with 1824 additions and 256 deletions

View file

@ -7,7 +7,7 @@ target = "xtensa-esp32s3-none-elf"
rustflags = [ rustflags = [
# Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.) # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
# NOTE: May negatively impact performance of produced code # NOTE: May negatively impact performance of produced code
"-C", "force-frame-pointers", "-C", "link-arg=-Wl,--wrap=strncmp", "-C", "force-frame-pointers",
] ]

5
firmware2/Cargo.lock generated
View file

@ -20,6 +20,7 @@ dependencies = [
"embedded-cli", "embedded-cli",
"embedded-io 0.6.1", "embedded-io 0.6.1",
"embuild", "embuild",
"enumset",
"esp-alloc", "esp-alloc",
"esp-backtrace", "esp-backtrace",
"esp-bootloader-esp-idf", "esp-bootloader-esp-idf",
@ -6915,9 +6916,9 @@ dependencies = [
[[package]] [[package]]
name = "tinyrlibc" name = "tinyrlibc"
version = "0.5.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d07d242693682e46801cb0186c583858bce022f698401b3f04bbd729c9a87e7" checksum = "9af8adde15cc59c61328c5d709b41b8464522a733033584d33def687b26bd009"
dependencies = [ dependencies = [
"cc", "cc",
] ]

View file

@ -34,8 +34,8 @@ debug = []
trace = [] trace = []
# Development profiles # Development profiles
develop = ["limit-fps", "alt-log"] develop = ["limit-fps", "alt-log"]
develop-usb = ["limit-fps", "usb-log", "no-usb"] develop-usb = ["limit-fps", "usb-log", "no-usb", "ble"]
probe = ["limit-fps", "rtt-log", "no-usb"] probe = ["limit-fps", "rtt-log", "no-usb", "ble"]
[dependencies] [dependencies]
rmk = { version = "0.8.2", default-features = false, features = [ rmk = { version = "0.8.2", default-features = false, features = [
@ -54,7 +54,7 @@ esp-backtrace = { version = "0.18", default-features = false, features = [
] } ] }
esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] } esp-hal = { version = "1.0", features = ["esp32s3", "unstable", "psram", "log-04"] }
esp-storage = { version = "0.8.0", features = ["esp32s3"] } esp-storage = { version = "0.8.0", features = ["esp32s3"] }
esp-alloc = { version = "0.9.0" } esp-alloc = { version = "0.9.0", features = ["nightly"] }
esp-println = { version = "0.16.0", features = ["esp32s3", "log-04"] } esp-println = { version = "0.16.0", features = ["esp32s3", "log-04"] }
esp-radio = { version = "0.17", features = ["esp32s3", "unstable", "ble"] } esp-radio = { version = "0.17", features = ["esp32s3", "unstable", "ble"] }
esp-rtos = { version = "0.2", features = ["esp32s3", "esp-radio", "embassy"] } esp-rtos = { version = "0.2", features = ["esp32s3", "esp-radio", "embassy"] }
@ -79,6 +79,7 @@ panic-rtt-target = { version = "0.2.0", optional = true }
embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] } embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] }
embedded-io = "0.6.1" embedded-io = "0.6.1"
mutually_exclusive_features = "0.1.0" mutually_exclusive_features = "0.1.0"
enumset = "1.1.10"
[build-dependencies] [build-dependencies]
xz2 = "0.1.7" xz2 = "0.1.7"

View file

@ -1,3 +1,33 @@
# ACID firmware
## Building
### Compiling libxkbcommon
This needs to be done on Linux or in WSL.
meson.build needs to be changed such that `pkgconfig.generate` is called with the `variables` field embedded directly, for some reason:
```meson
variables: [
f'xkb_root=@XKB_CONFIG_ROOT@',
f'xkb_extra_path=@XKB_CONFIG_EXTRA_PATH@',
],
```
Then, the tests, fuzzes and benches need to be commented out.
Afterwards, the following command should succeed in building the static library in debug mode:
```sh
meson setup build-debug -Denable-x11=false -Dxkb-config-root=/usr/share/X11/xkb -Dx-locale-root=/usr/share/X11/locale -Denable-wayland=false --cross-file cross-esp32s3.txt -Denable-xkbregistry=false -Denable-docs=false -Denable-tools=false --wipe --buildtype=debug
meson compile -C build-debug
```
Afterwards, a static library with redefined externally-linked symbols needs to be generated:
```
path/to/root/libxkbcommon-redefine-syms.sh build-debug/libxkbcommon{,_redefined_syms}.a
```
# esp32s3 BLE example # esp32s3 BLE example
To run this example, you should have latest Rust in **esp** channel installed. The full instruction of installing esp Rust toolchain can be found [here](https://docs.esp-rs.org/book/installation/index.html). To run this example, you should have latest Rust in **esp** channel installed. The full instruction of installing esp Rust toolchain can be found [here](https://docs.esp-rs.org/book/installation/index.html).

View file

@ -35,10 +35,12 @@ fn main() {
println!(r#"cargo:rustc-link-search=native=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug"#); println!(r#"cargo:rustc-link-search=native=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug"#);
// 2. Link the static library (strip the 'lib' prefix and '.a' extension) // 2. Link the static library (strip the 'lib' prefix and '.a' extension)
println!("cargo:rustc-link-lib=static=xkbcommon"); // println!("cargo:rustc-link-lib=static=xkbcommon");
println!("cargo:rustc-link-lib=static=xkbcommon_redefined_syms");
// 3. Re-run if build.rs or the library changes // 3. Re-run if build.rs or the library changes
println!(r#"cargo:rerun-if-changed=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug\libxkbcommon.a"#); // println!(r#"cargo:rerun-if-changed=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug\libxkbcommon.a"#);
println!(r#"cargo:rerun-if-changed=C:\Users\Limeth\workspace\c\libxkbcommon-linux\build-debug\libxkbcommon_redefined_syms.a"#);
let slint_config = CompilerConfiguration::new() let slint_config = CompilerConfiguration::new()
// .with_scale_factor(4.0) // .with_scale_factor(4.0)

View file

@ -0,0 +1,39 @@
[binaries]
c = 'xtensa-esp32s3-elf-gcc'
cpp = 'xtensa-esp32s3-elf-g++'
ar = 'xtensa-esp32s3-elf-ar'
strip = 'xtensa-esp32s3-elf-strip'
[build_machine]
system = 'linux'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
[host_machine]
system = 'bare-metal' # Crucial: prevents Meson from assuming a POSIX environment
cpu_family = 'xtensa'
cpu = 'esp32s3'
endian = 'little'
[built-in options]
c_args = [
'-fdebug-prefix-map=@GLOBAL_SOURCE_ROOT@=C:/Users/Limeth/workspace/c/libxkbcommon-linux',
'-ffreestanding',
'-fno-builtin',
'-mlongcalls',
'-mfix-esp32-psram-cache-issue',
# Manually define the missing inttypes.h macros
'-DPRId64="lld"',
'-DPRIu64="llu"',
'-DPRIi64="lli"',
'-DPRId32="d"',
'-DPRIu32="u"',
'-DPRIx64="llx"'
]
c_link_args = [
'-nostdlib',
'-static'
]
# Ensure we only build static libraries for bare-metal
default_library = 'static'

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
#!/bin/bash
INPUT_LIB=$1
OUTPUT_LIB=$2
PREFIX="__xkbc_"
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <input_lib.a> <output_lib.a>"
exit 1
fi
echo "Redefining symbols from $INPUT_LIB into $OUTPUT_LIB..."
# We use <(...) to stream the symbol map directly into objcopy
# xtensa-esp-elf-objcopy --redefine-syms=<(
# xtensa-esp-elf-nm -u "$INPUT_LIB" |
# awk '{print $NF}' |
# sort -u |
# awk -v p="$PREFIX" '{print $1 " " p $1}'
# ) "$INPUT_LIB" "$OUTPUT_LIB"
xtensa-esp-elf-objcopy --redefine-syms=<(
comm -23 \
<(xtensa-esp-elf-nm -u "$INPUT_LIB" | awk '{print $NF}' | sort -u) \
<(xtensa-esp-elf-nm --defined-only "$INPUT_LIB" | awk '{print $NF}' | sort -u) | \
awk -v p="$PREFIX" '{print $1 " " p $1}'
) "$INPUT_LIB" "$OUTPUT_LIB"
echo "Done! Prefixed all externally linked symbols with $PREFIX."

View file

@ -1,241 +0,0 @@
#![allow(unused)]
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::{null, null_mut}};
unsafe extern "C" {
fn malloc(size: c_size_t) -> *mut c_void;
}
#[unsafe(no_mangle)]
unsafe extern "C" fn strpbrk(s: *const c_char, accept: *const c_char) -> *mut c_char {
todo!()
}
#[allow(non_camel_case_types)]
type c_intmax_t = c_longlong;
#[unsafe(no_mangle)]
unsafe extern "C" fn imaxabs(j: c_intmax_t) -> c_intmax_t {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn eaccess(pathname: *const c_char, mode: c_int ) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn vasprintf(strp: *mut *mut c_char, fmt: *const c_char, ap: VaList) -> c_int {
todo!()
}
#[allow(non_camel_case_types)]
type stat = c_void; // Not implemented
#[unsafe(no_mangle)]
unsafe extern "C" fn stat(path: *const c_char, buf: *mut stat) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn __errno() -> *mut c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn strerror(errnum: c_int) -> *mut c_char {
todo!()
}
#[allow(non_camel_case_types)]
type DIR = c_void; // Not implemented
#[allow(non_camel_case_types)]
type dirent = c_void; // Not implemented
#[unsafe(no_mangle)]
unsafe extern "C" fn opendir(name: *const c_char) -> *mut DIR {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn readdir(dirp: *mut DIR) -> *mut dirent {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn closedir(dirp: *mut DIR) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn qsort(
base: *mut c_void,
num: c_size_t,
size: c_size_t,
compar: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
) {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn __getreent() -> *mut c_void /*struct _reent*/ {
todo!()
}
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
enum FILE {}
#[unsafe(no_mangle)]
unsafe extern "C" fn fprintf(
stream: *mut FILE,
format: *const c_char,
...
) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn vfprintf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn strtol(nptr: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
unsafe { getenv(name) }
}
#[unsafe(no_mangle)]
unsafe extern "C" fn getenv(name: *const c_char) -> *mut c_char {
null_mut()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn fseek(
stream: *mut FILE,
offset: c_long,
whence: c_int,
) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn ftell(stream: *mut FILE) -> c_long {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn fread(
ptr: *mut c_void,
size: c_size_t,
nobj: c_size_t,
stream: *mut FILE,
) -> c_size_t {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn open(
path: *const c_char,
oflag: c_int,
...
) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn close(fd: c_int) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn fdopen(
fd: c_int,
mode: *const c_char,
) -> *mut FILE {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn strndup(
s: *const c_char,
n: c_size_t,
) -> *mut c_char {
let len = unsafe { strnlen(s, n) };
let new = unsafe { malloc(len + 1) } as *mut c_char;
if new.is_null() {
return null_mut();
}
unsafe {
*new.add(len) = b'\0';
memcpy(new as *mut c_void, s as *const c_void, len) as *mut c_char
}
}
#[unsafe(no_mangle)]
unsafe extern "C" fn labs(i: c_long) -> c_long {
i.abs()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn memcpy(dest: *mut c_void, src: *const c_void, n: c_size_t) -> *mut c_void {
todo!()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn strnlen(s: *const c_char, maxlen: c_size_t) -> c_size_t {
let found: *const c_char = unsafe { memchr(s as *const c_void, b'\0' as c_int, maxlen) } as *const c_char;
if !found.is_null() { unsafe { found.offset_from(s) as c_size_t } } else { maxlen }
}
#[unsafe(no_mangle)]
unsafe extern "C" fn memchr(s: *const c_void, c: c_int, n: c_size_t) -> *mut c_void {
let mut s = s as *const c_uchar;
let c = c as c_uchar;
for _ in 0..n {
unsafe {
if *s == c {
return s as *mut c_void;
}
s = s.add(1);
}
}
null_mut()
}
#[unsafe(no_mangle)]
unsafe extern "C" fn __wrap_strncmp(
s1: *const c_char,
s2: *const c_char,
n: c_size_t,
) -> c_int {
unsafe {
for i in 0..n as isize {
let s1_i = s1.offset(i);
let s2_i = s2.offset(i);
let val = *s1_i as c_int - *s2_i as c_int;
if val != 0 || *s1_i == 0 {
return val;
}
}
0
}
}

104
firmware2/src/ffi/alloc.rs Normal file
View file

@ -0,0 +1,104 @@
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
use core::alloc::GlobalAlloc;
use enumset::EnumSet;
use esp_alloc::EspHeap;
// Here we select the allocator to use for libxkbcommon.
static XKBC_ALLOCATOR: &EspHeap = &crate::PSRAM_ALLOCATOR;
// Implementation based on esp-alloc's `compat` feature.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_malloc(size: c_size_t) -> *mut c_void {
unsafe { malloc_with_caps(size, EnumSet::empty()) as *mut _ }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_calloc(
number: c_size_t,
size: c_size_t,
) -> *mut c_void {
let total_size = number as usize * size;
unsafe {
let ptr = __xkbc_malloc(total_size) as *mut u8;
if !ptr.is_null() {
for i in 0..total_size as isize {
ptr.offset(i).write_volatile(0);
}
}
ptr as *mut _
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_realloc(
ptr: *mut c_void,
new_size: c_size_t,
) -> *mut c_void {
unsafe { realloc_with_caps(ptr as *mut _, new_size, EnumSet::empty()) as *mut _ }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_free(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe {
let ptr = ptr.offset(-4);
let total_size = *(ptr as *const usize);
XKBC_ALLOCATOR.dealloc(
ptr as *mut u8,
core::alloc::Layout::from_size_align_unchecked(total_size, 4),
)
}
}
unsafe fn malloc_with_caps(
size: usize,
caps: EnumSet<crate::MemoryCapability>,
) -> *mut u8 {
let total_size = size + 4;
unsafe {
let ptr = XKBC_ALLOCATOR.alloc_caps(
caps,
core::alloc::Layout::from_size_align_unchecked(total_size, 4),
);
if ptr.is_null() {
return ptr;
}
*(ptr as *mut usize) = total_size;
ptr.offset(4)
}
}
unsafe fn realloc_with_caps(
ptr: *mut u8,
new_size: usize,
caps: enumset::EnumSet<crate::MemoryCapability>,
) -> *mut u8 {
unsafe extern "C" {
fn memcpy(d: *mut u8, s: *const u8, l: usize);
}
unsafe {
let p = malloc_with_caps(new_size, caps);
if !p.is_null() && !ptr.is_null() {
let len = usize::min(
(ptr as *const u32).sub(1).read_volatile() as usize,
new_size,
);
memcpy(p, ptr, len);
__xkbc_free(ptr as *mut _);
}
p
}
}

View file

@ -0,0 +1,12 @@
//! https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gccint/Libgcc.html
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
unsafe extern "C" {
fn __divdi3(a: c_long, b: c_long) -> c_long;
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc___divdi3(a: c_long, b: c_long) -> c_long {
unsafe { __divdi3(a, b) }
}

View file

@ -0,0 +1,25 @@
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
#[allow(non_camel_case_types)]
pub enum DIR {}
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct dirent {
// Not implemented
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_opendir(name: *const c_char) -> *mut DIR {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_readdir(dirp: *mut DIR) -> *mut dirent {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_closedir(dirp: *mut DIR) -> c_int {
todo!()
}

View file

@ -0,0 +1,9 @@
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
pub enum FILE {}
pub const STDIN: *mut FILE = 0 as _;
pub const STDOUT: *mut FILE = 1 as _;
pub const STDERR: *mut FILE = 2 as _;

View file

@ -0,0 +1,93 @@
//! This would've been called `io`, but rust-analyzer refuses to see
//! the submodules of this module when it's named `io`.
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
use log::info;
pub mod file;
pub mod dir;
pub use file::*;
pub use dir::*;
// File management
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fopen(
filename: *const c_char,
mode: *const c_char,
) -> *mut FILE {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fclose(file: *mut FILE) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fseek(
stream: *mut FILE,
offset: c_long,
whence: c_int,
) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_ftell(stream: *mut FILE) -> c_long {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fread(
ptr: *mut c_void,
size: c_size_t,
nobj: c_size_t,
stream: *mut FILE,
) -> c_size_t {
todo!()
}
// Printing
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fprintf(
stream: *mut FILE,
format: *const c_char,
mut args: ...
) -> c_int {
unsafe { __xkbc_vfprintf(stream, format, args.as_va_list()) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_vfprintf(stream: *mut FILE, format: *const c_char, ap: VaList) -> c_int {
if stream == STDOUT || stream == STDERR {
let string = ::alloc::format!("vfprintf({:?}, {:?}, {:?})", stream, format, ap);
info!("{}", string);
string.len() as c_int
} else {
-1
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_vasprintf(strp: *mut *mut c_char, fmt: *const c_char, ap: VaList) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_vsnprintf(string: *mut c_char, size: c_size_t, format: *const c_char, ap: VaList) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_snprintf(
s: *mut c_char,
n: c_size_t,
format: *const c_char,
...
) -> c_int {
todo!()
}

130
firmware2/src/ffi/mod.rs Normal file
View file

@ -0,0 +1,130 @@
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
use alloc::__xkbc_malloc;
use inout::{FILE, STDERR, STDIN, STDOUT};
use log::info;
pub mod alloc;
pub mod string;
pub mod inout;
pub mod gcc_runtime;
#[allow(non_camel_case_types)]
pub type c_intmax_t = c_longlong;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_imaxabs(j: c_intmax_t) -> c_intmax_t {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_eaccess(pathname: *const c_char, mode: c_int ) -> c_int {
todo!()
}
#[allow(non_camel_case_types)]
type stat = c_void; // Not implemented
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_stat(path: *const c_char, buf: *mut stat) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc___errno() -> *mut c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_qsort(
base: *mut c_void,
num: c_size_t,
size: c_size_t,
compar: Option<unsafe extern "C" fn(*const c_void, *const c_void) -> c_int>,
) {
todo!()
}
/// An incomplete representation of Newlib's `struct _reent`.
/// https://github.com/bminor/newlib/blob/b41e3b652c4215556a62a2f47e8f8f11553fa550/newlib/libc/include/sys/reent.h#L580
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
struct _reent {
_errno: c_int,
_stdin: *mut FILE,
_stdout: *mut FILE,
_stderr: *mut FILE,
// Rest unimplemented
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc___getreent() -> *mut _reent {
static mut REENT: [_reent; 2] = [_reent {
_errno: 0,
_stdin: STDIN,
_stdout: STDOUT,
_stderr: STDERR,
}; 2];
unsafe {
let reent = &mut REENT[esp_hal::system::Cpu::current() as usize];
reent as *mut _reent
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_secure_getenv(name: *const c_char) -> *mut c_char {
unsafe { __xkbc_getenv(name) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_getenv(name: *const c_char) -> *mut c_char {
null_mut()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_open(
path: *const c_char,
oflag: c_int,
...
) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fstat(fildes: c_int, buf: *mut stat) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_close(fd: c_int) -> c_int {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_fdopen(
fd: c_int,
mode: *const c_char,
) -> *mut FILE {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_labs(i: c_long) -> c_long {
i.abs()
}
unsafe extern "C" {
fn __assert_func(file: *const c_char, line: c_int, function: *const c_char, failedExpression: *const c_char);
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc___assert_func(file: *const c_char, line: c_int, function: *const c_char, failed_expression: *const c_char) {
unsafe { __assert_func(file, line, function, failed_expression); }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_atoi(s: *const c_char) -> c_int {
todo!()
}

193
firmware2/src/ffi/string.rs Normal file
View file

@ -0,0 +1,193 @@
use core::{ffi::{CStr, VaList, c_char, c_int, c_long, c_longlong, c_size_t, c_uchar, c_void}, ptr::null_mut};
use super::alloc::__xkbc_malloc;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memset(
dest_original: *mut c_void,
c: c_int,
n: c_size_t,
) -> *mut c_void {
if dest_original == null_mut() {
if n > 0 {
panic!("Attempted to memset a nullptr.");
} else {
return null_mut();
}
}
let mut dest = dest_original as *mut u8;
let c = c as u8;
for _ in 0..n {
unsafe {
*dest = c;
dest = dest.add(1);
}
}
dest_original
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memmove(
dst: *mut c_void,
src: *const c_void,
count: c_size_t,
) -> *mut c_void {
unsafe { core::ptr::copy(src, dst, count) };
dst
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memcpy(dst: *mut c_void, src: *const c_void, count: c_size_t) -> *mut c_void {
unsafe { core::ptr::copy_nonoverlapping(src, dst, count); }
dst
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memchr(s: *const c_void, c: c_int, n: c_size_t) -> *mut c_void {
let mut s = s as *const c_uchar;
let c = c as c_uchar;
for _ in 0..n {
unsafe {
if *s == c {
return s as *mut c_void;
}
s = s.add(1);
}
}
null_mut()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_memcmp(
mut s1: *const c_char,
mut s2: *const c_char,
n: c_size_t,
) -> c_int {
unsafe {
for i in 0..n as isize {
let s1_i = s1.offset(i);
let s2_i = s2.offset(i);
let val = *s1_i as c_int - *s2_i as c_int;
if val != 0 {
return val;
}
}
0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strpbrk(s: *const c_char, accept: *const c_char) -> *mut c_char {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strerror(errnum: c_int) -> *mut c_char {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strdup(string: *const c_char) -> *mut c_char {
strndup_inner(string, unsafe { __xkbc_strlen(string) })
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strndup(
string: *const c_char,
max_len: c_size_t,
) -> *mut c_char {
strndup_inner(string, unsafe { __xkbc_strnlen(string, max_len) })
}
fn strndup_inner(string: *const c_char, computed_len: c_size_t) -> *mut c_char {
let new = unsafe { __xkbc_malloc(computed_len + 1) } as *mut c_char;
if new.is_null() {
return null_mut();
}
unsafe {
*new.add(computed_len) = b'\0';
__xkbc_memcpy(new as *mut c_void, string as *const c_void, computed_len) as *mut c_char
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strlen(mut s: *const c_char) -> c_size_t {
let mut result = 0;
unsafe {
while *s != 0 {
s = s.offset(1);
result += 1;
}
}
result
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strnlen(s: *const c_char, maxlen: c_size_t) -> c_size_t {
let found: *const c_char = unsafe { __xkbc_memchr(s as *const c_void, b'\0' as c_int, maxlen) } as *const c_char;
if !found.is_null() { unsafe { found.offset_from(s) as c_size_t } } else { maxlen }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strncmp(
s1: *const c_char,
s2: *const c_char,
n: c_size_t,
) -> c_int {
unsafe {
for i in 0..n as isize {
let s1_i = s1.offset(i);
let s2_i = s2.offset(i);
let val = *s1_i as c_int - *s2_i as c_int;
if val != 0 || *s1_i == 0 {
return val;
}
}
0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strchr(
cs: *const c_char,
c: c_int,
) -> *mut c_char {
todo!()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strcmp(
s1: *const c_char,
s2: *const c_char,
) -> c_int {
unsafe {
for i in 0_isize.. {
let s1_i = s1.offset(i);
let s2_i = s2.offset(i);
let val = *s1_i as c_int - *s2_i as c_int;
if val != 0 || *s1_i == 0 {
return val;
}
}
0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __xkbc_strtol(nptr: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_long {
todo!()
}

View file

@ -8,6 +8,7 @@
//! https://esp32.com/viewtopic.php?f=12&t=26793&start=20#p95677 //! https://esp32.com/viewtopic.php?f=12&t=26793&start=20#p95677
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(allocator_api)]
#![feature(macro_metavar_expr)] #![feature(macro_metavar_expr)]
#![feature(c_variadic)] #![feature(c_variadic)]
#![feature(c_size_t)] #![feature(c_size_t)]
@ -17,9 +18,12 @@ extern crate alloc;
use core::alloc::Layout; use core::alloc::Layout;
use core::cell::RefCell; use core::cell::RefCell;
use core::ffi::CStr; use core::ffi::CStr;
use core::slice;
use alloc::string::ToString; use alloc::alloc::Allocator;
use alloc::boxed::Box; use alloc::string::{String, ToString};
use alloc::boxed::{self, Box};
use alloc::vec::Vec;
use alloc::vec; use alloc::vec;
use bt_hci::controller::ExternalController; use bt_hci::controller::ExternalController;
use cfg_if::cfg_if; use cfg_if::cfg_if;
@ -66,7 +70,6 @@ use slint::platform::software_renderer::Rgb565Pixel;
use slint::ComponentHandle; use slint::ComponentHandle;
use static_cell::StaticCell; use static_cell::StaticCell;
use ui::AppWindow; use ui::AppWindow;
use xkbcommon::xkb::{self, Keymap};
use {esp_alloc as _, esp_backtrace as _}; use {esp_alloc as _, esp_backtrace as _};
use crate::matrix::IoeMatrix; use crate::matrix::IoeMatrix;
@ -109,7 +112,7 @@ cfg_if! {
const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS const FRAME_DURATION_MIN: Duration = Duration::from_millis(40); // 25 FPS
static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty(); pub static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
/// Used to signal that MCU is ready to submit the framebuffer to the LCD. /// Used to signal that MCU is ready to submit the framebuffer to the LCD.
static SIGNAL_LCD_SUBMIT: Signal<CriticalSectionRawMutex, ()> = Signal::new(); static SIGNAL_LCD_SUBMIT: Signal<CriticalSectionRawMutex, ()> = Signal::new();
@ -153,6 +156,9 @@ async fn main(_spawner: Spawner) {
let alt_uart_rx_task = async {}; let alt_uart_rx_task = async {};
// Use the internal DRAM as the heap. // Use the internal DRAM as the heap.
// TODO: Can we combine the regular link section with dram2?
// esp_alloc::heap_allocator!(size: 80 * 1024);
// esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 72 * 1024);
esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024); esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024);
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
@ -384,7 +390,10 @@ async fn main(_spawner: Spawner) {
info!("Second core started!"); info!("Second core started!");
let hid_report_proxy_task = async {};
let hid_report_proxy_task = { let hid_report_proxy_task = {
use xkbcommon::xkb::{self, Keymap};
static KEYBOARD_REPORT_PROXY: Channel<CriticalSectionRawMutex, Report, 16> = Channel::new(); static KEYBOARD_REPORT_PROXY: Channel<CriticalSectionRawMutex, Report, 16> = Channel::new();
{ {
@ -395,10 +404,21 @@ async fn main(_spawner: Spawner) {
Timer::after_secs(1).await; Timer::after_secs(1).await;
info!("HID Report Proxy Task: Done waiting."); info!("HID Report Proxy Task: Done waiting.");
const KEYMAP_STRING: &str = include_str!("../keymaps/cz.xkb"); const KEYMAP_STRING: &str = include_str!("../keymaps/cz.xkb");
let keymap_string_buffer = unsafe {
let allocation = PSRAM_ALLOCATOR.alloc_caps(
MemoryCapability::External.into(),
Layout::from_size_align(KEYMAP_STRING.len(), 32).unwrap(),
);
let slice = str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(allocation, KEYMAP_STRING.len()));
slice.as_bytes_mut().copy_from_slice(KEYMAP_STRING.as_bytes());
Box::from_raw_in(slice as *mut str, &PSRAM_ALLOCATOR)
};
info!("HID Report Proxy Task: test"); info!("HID Report Proxy Task: test");
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
info!("HID Report Proxy Task: foo"); info!("HID Report Proxy Task: foo");
let keymap = Keymap::new_from_string(&context, KEYMAP_STRING, xkb::KEYMAP_FORMAT_TEXT_V1, xkb::KEYMAP_COMPILE_NO_FLAGS).unwrap(); let keymap = Keymap::new_from_string(&context, keymap_string_buffer, xkb::KEYMAP_FORMAT_TEXT_V1, xkb::KEYMAP_COMPILE_NO_FLAGS).unwrap();
info!("HID Report Proxy Task: bar"); info!("HID Report Proxy Task: bar");
async { async {
@ -472,6 +492,14 @@ impl Controller for UserController {
#[embassy_executor::task] #[embassy_executor::task]
async fn run_renderer_task(backend: SlintBackend) { async fn run_renderer_task(backend: SlintBackend) {
// TODO: Disable rendering because it causes crashes in allocation
loop {
SIGNAL_LCD_SUBMIT.signal(());
#[cfg(feature = "limit-fps")]
embassy_time::Timer::after(FRAME_DURATION_MIN).await;
SIGNAL_UI_RENDER.wait().await;
}
slint::platform::set_platform(Box::new(backend)) slint::platform::set_platform(Box::new(backend))
.expect("backend already initialized"); .expect("backend already initialized");