esp_idf_sys/
start.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#![cfg(any(feature = "binstart", feature = "libstart"))]

// When compiling a binary Rust crate in STD (e.g. in Cargo-first builds) and NOT doing
// any tricks like using #[no_main] or #[start], the Rust compiler will autogenerate
// a C function with the signature as below which will be proxying
// the real Rust main function of your binary crate
//
// So to bridge this function with the real C "app_main()" entrypoint
// that ESP-IDF expects it is enough to implement app_main() and call in it
// the "main" C function autogenerated by the Rust compiler
//
// See https://github.com/rust-lang/rust/issues/29633 for more information
#[cfg(all(feature = "std", feature = "binstart"))]
extern "C" {
    fn main(p1: isize, p2: *const *const u8) -> isize;
}

// When compiling a static Rust library crate (e.g. by using a PIO->Cargo or a CMake->Cargo) build,
// there is no main function that the Rust compiler expects, nor autogeneration of a callable
// wrapper around it.
//
// In that case (and if the "libstart" feature is enabled), it is _us_ (not the Rust compiler)
// expecting the user to define a rust #[no_mangle] "main" function and it is our code below which is explicitly
// calling it from app_main(). If the user does not define a main() runction in Rust, there will
// be a linkage error instead of the nice Rust syntax error for binary crates.
//
// Another restriction of the "libstart" feature is that the Rust main function will always have one
// fixed signature: "fn main() -> ()" - as opposed to the flexibility of main() in binary crates
// where it can have quite a few different returning types
//
// When compiling a binary Rust crate in no_std, we end up in identical situation:
// - There is no Rust "lang = start" item defined
// - As such, there is no magic C "main" function defined for, which would proxy our binary crate main
#[cfg(any(all(not(feature = "std"), feature = "binstart"), feature = "libstart"))]
extern "Rust" {
    fn main();
}

#[no_mangle]
pub extern "C" fn app_main() {
    unsafe {
        // #[cfg(all(feature = "uart0_driver_init", esp_idf_comp_driver_enabled, esp_idf_comp_vfs_enabled))]
        // uart_init::init_uart0();

        #[cfg(all(feature = "std", feature = "binstart"))]
        main(0, &[core::ptr::null()] as *const *const u8);

        #[cfg(any(all(not(feature = "std"), feature = "binstart"), feature = "libstart"))]
        main();
    }
}

// TODO: Move to `esp-idf-hal`. We should start precisely in the state ESP IDF was configured to be. The below should become a utility method,
// ideally as part of `esp_idf_hal::uart` and possibly with support from future ESP IDF VFS safe APIs
// #[cfg(all(feature = "uart0_driver_init", esp_idf_comp_driver_enabled, esp_idf_comp_vfs_enabled))]
// mod uart_init {
//     use crate::{esp_vfs_dev_uart_use_driver, uart_driver_install};
//     use core::{ffi::c_int, ptr::null_mut};

//     const UART_BUFFER_SIZE: c_int = 512;
//     const UART_QUEUE_SIZE: c_int = 10;

//     pub(super) fn init_uart0() {
//         // Enable UART0 driver so stdin can be read.
//         unsafe {
//             crate::esp!(uart_driver_install(
//                 0,
//                 UART_BUFFER_SIZE,
//                 UART_BUFFER_SIZE,
//                 UART_QUEUE_SIZE,
//                 null_mut(),
//                 0
//             ))
//             .expect("unable to initialize UART0 driver");
//             esp_vfs_dev_uart_use_driver(0);
//         }
//     }
// }