esp_rom_sys/
lib.rs

1#![doc = include_str!("../README.md")]
2//! ## Feature Flags
3#![doc = document_features::document_features!()]
4#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
5#![allow(rustdoc::bare_urls)]
6#![no_std]
7
8use core::ffi::c_char;
9
10#[doc(hidden)]
11/// Helper macro for checking doctest code snippets
12#[macro_export]
13macro_rules! before_snippet {
14    () => {
15        r#"
16# #![no_std]
17# use procmacros::handler;
18# use esp_hal::{interrupt::{self, InterruptConfigurable}, time::{Duration, Instant, Rate}};
19# macro_rules! println {
20#     ($($tt:tt)*) => { };
21# }
22# macro_rules! print {
23#     ($($tt:tt)*) => { };
24# }
25# #[panic_handler]
26# fn panic(_ : &core::panic::PanicInfo) -> ! {
27#     loop {}
28# }
29# fn main() {
30#   let _ = example();
31# }
32# struct ExampleError {}
33# impl <T> From<T> for ExampleError where T: core::fmt::Debug {
34#   fn from(_value: T) -> Self {
35#       Self{}
36#   }
37# }
38# async fn example() -> Result<(), ExampleError> {
39#   let mut peripherals = esp_hal::init(esp_hal::Config::default());
40"#
41    };
42}
43
44pub mod rom;
45mod syscall;
46
47pub use syscall::init_syscall_table;
48
49/// This is needed by `libesp_rom.a` (if used)
50/// Other crates (i.e. esp-radio) also rely on this being defined somewhere
51#[unsafe(no_mangle)]
52unsafe extern "C" fn __assert_func(
53    file: *const core::ffi::c_char,
54    line: i32,
55    func: *const core::ffi::c_char,
56    expr: *const core::ffi::c_char,
57) -> ! {
58    unsafe {
59        panic!(
60            "__assert_func in {}:{} ({}): {}",
61            core::ffi::CStr::from_ptr(file).to_str().unwrap(),
62            line,
63            core::ffi::CStr::from_ptr(func).to_str().unwrap(),
64            core::ffi::CStr::from_ptr(expr).to_str().unwrap(),
65        );
66    }
67}
68
69// We cannot just use the ROM function since (on some targets, currently ESP32-S2) it calls
70// `__getreent`
71//
72// From docs: The __getreent() function returns a per-task pointer to struct
73// _reent in newlib libc. This structure is allocated on the TCB of each task.
74// i.e. it assumes a FreeRTOS task calling it.
75#[unsafe(no_mangle)]
76unsafe extern "C" fn __strcasecmp(
77    s1: *const core::ffi::c_char,
78    s2: *const core::ffi::c_char,
79) -> i32 {
80    let mut i = 0;
81    loop {
82        unsafe {
83            let s1_i = s1.add(i);
84            let s2_i = s2.add(i);
85
86            let val = (*s1_i).to_ascii_lowercase() as i32 - (*s2_i).to_ascii_lowercase() as i32;
87            if val != 0 || *s1_i == 0 {
88                return val;
89            }
90        }
91
92        i += 1;
93    }
94}
95
96#[unsafe(no_mangle)]
97unsafe extern "C" fn __strnlen(chars: *const c_char, maxlen: isize) -> usize {
98    let mut len = 0;
99    loop {
100        unsafe {
101            if chars.offset(len).read_volatile() == 0 {
102                break;
103            }
104            len += 1;
105
106            if len >= maxlen {
107                break;
108            }
109        }
110    }
111
112    len as usize
113}
114
115// We cannot just use the ROM function since it calls `__getreent`
116//
117// From docs: The __getreent() function returns a per-task pointer to struct
118// _reent in newlib libc. This structure is allocated on the TCB of each task.
119// i.e. it assumes a FreeRTOS task calling it.
120#[unsafe(no_mangle)]
121unsafe extern "C" fn __atoi(str: *const i8) -> i32 {
122    let mut sign: i32 = 1;
123    let mut res: i32 = 0;
124    let mut idx = 0;
125
126    // skip leading spaces
127    while unsafe { str.add(idx).read() } as u8 == b' ' {
128        idx += 1;
129    }
130
131    // check sign
132    let c = unsafe { str.add(idx).read() } as u8;
133    if c == b'-' || c == b'+' {
134        if c == b'-' {
135            sign = -1;
136        }
137        idx += 1;
138    }
139
140    // parse number digit by digit
141    loop {
142        let c = unsafe { str.add(idx).read() } as u8;
143
144        if !c.is_ascii_digit() {
145            break;
146        }
147
148        // if the result would exceed the bounds - return max-value
149        if res > i32::MAX / 10 || (res == i32::MAX / 10 && c - b'0' > 7) {
150            return if sign == 1 { i32::MAX } else { i32::MIN };
151        }
152
153        res = 10 * res + (c - b'0') as i32;
154        idx += 1;
155    }
156    res * sign
157}
158
159#[derive(Debug, Copy, Clone)]
160#[repr(C)]
161struct Tm {
162    tm_sec: u32,   // seconds after the minute - [0, 60] including leap second
163    tm_min: u32,   // minutes after the hour - [0, 59]
164    tm_hour: u32,  // hours since midnight - [0, 23]
165    tm_mday: u32,  // day of the month - [1, 31]
166    tm_mon: u32,   // months since January - [0, 11]
167    tm_year: u32,  // years since 1900
168    tm_wday: u32,  // days since Sunday - [0, 6]
169    tm_yday: u32,  // days since January 1 - [0, 365]
170    tm_isdst: u32, // daylight savings time flag
171}
172
173#[unsafe(no_mangle)]
174unsafe extern "C" fn __mktime(time: *const Tm) -> i64 {
175    let time = unsafe { *time };
176
177    // Simplified implementation, ignoring time zones, leap seconds, and other
178    // complexities
179    let mut days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
180
181    let is_leap_year = |year: u32| {
182        year.is_multiple_of(4) && (!year.is_multiple_of(100) || year.is_multiple_of(400))
183    };
184
185    let mut days = 0;
186    let year = time.tm_year + 1900;
187    for y in 1970..year {
188        days += if is_leap_year(y) { 366 } else { 365 };
189    }
190
191    if is_leap_year(year) {
192        days_in_month[1] = 29;
193    }
194
195    for m in 0..time.tm_mon {
196        days += days_in_month[m as usize];
197    }
198    days += time.tm_mday - 1;
199
200    let seconds = days * 24 * 60 * 60 + time.tm_hour * 60 * 60 + time.tm_min * 60 + time.tm_sec;
201
202    seconds as i64
203}