lora_things_plus/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Board file for SparkFun LoRa Thing Plus - expLoRaBLE
6//!
7//! - <https://www.sparkfun.com/products/17506>
8//!
9//! A Semtech SX1262 is connected as a SPI slave to IOM3
10//! See <https://www.northernmechatronics.com/_files/ugd/3c68cb_764598422c704ed1b32400b047fc7651.pdf>
11//! and <https://www.northernmechatronics.com/nm180100> for details
12//!
13//! See <https://github.com/NorthernMechatronics/nmsdk/blob/master/bsp/nm180100evb/bsp_pins.src>
14//! and <https://cdn.sparkfun.com/assets/4/4/f/7/e/expLoRaBLE_Thing_Plus_schematic.pdf>
15//! for details on the pin break outs
16//!
17//! IOM0: Qwiic I2C
18//! IOM1: Not connected
19//! IOM2: Broken out SPI
20//! IOM3: Semtech SX1262
21//!     Apollo 3 Pin Number | Apollo 3 Name | SX1262 Pin Number | SX1262 Name | SX1262 Description
22//!                      H6 |       GPIO 36 |                19 |  NSS        | SPI slave select
23//!                      J6 |       GPIO 38 |                17 |  MOSI       | SPI slave input
24//!                      J5 |       GPIO 43 |                16 |  MISO       | SPI slave output
25//!                      H5 |       GPIO 42 |                18 |  SCK        | SPI clock input
26//!                      J8 |       GPIO 39 |                14 |  BUSY       | Radio busy indicator
27//!                      J9 |       GPIO 40 |                13 |  DIO1       | Multipurpose digital I/O
28//!                      H9 |       GPIO 47 |                6  |  DIO3       | Multipurpose digital I/O
29//!                      J7 |       GPIO 44 |                15 |  NRESET     | Radio reset signal, active low
30//! IOM4: Not connected
31//! IOM5: Pins used by UART0
32
33#![no_std]
34#![no_main]
35#![deny(missing_docs)]
36#![feature(custom_test_frameworks)]
37#![test_runner(test_runner)]
38#![reexport_test_harness_main = "test_main"]
39
40use core::ptr::addr_of;
41use core::ptr::addr_of_mut;
42
43use apollo3::chip::Apollo3DefaultPeripherals;
44use capsules_core::virtualizers::virtual_alarm::MuxAlarm;
45use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
46use components::bme280::Bme280Component;
47use components::ccs811::Ccs811Component;
48use kernel::capabilities;
49use kernel::component::Component;
50use kernel::hil::flash::HasClient;
51use kernel::hil::hasher::Hasher;
52use kernel::hil::i2c::I2CMaster;
53use kernel::hil::led::LedHigh;
54use kernel::hil::spi::SpiMaster;
55use kernel::hil::time::Counter;
56use kernel::platform::{KernelResources, SyscallDriverLookup};
57use kernel::scheduler::round_robin::RoundRobinSched;
58use kernel::{create_capability, debug, static_init};
59
60#[cfg(feature = "atecc508a")]
61use {
62    capsules_core::virtualizers::virtual_i2c::MuxI2C,
63    components::atecc508a::Atecc508aComponent,
64    kernel::hil::entropy::Entropy32,
65    kernel::hil::gpio::{Configure, Output},
66    kernel::hil::rng::Rng,
67};
68
69#[cfg(any(feature = "chirp_i2c_moisture", feature = "dfrobot_i2c_rainfall"))]
70use capsules_core::virtualizers::virtual_i2c::MuxI2C;
71
72/// Support routines for debugging I/O.
73pub mod io;
74
75#[cfg(test)]
76mod tests;
77
78// Number of concurrent processes this platform supports.
79const NUM_PROCS: usize = 4;
80
81// Actual memory for holding the active process structures.
82static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] = [None; 4];
83
84// Static reference to chip for panic dumps.
85static mut CHIP: Option<&'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> = None;
86// Static reference to process printer for panic dumps.
87static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
88    None;
89
90// How should the kernel respond when a process faults.
91const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
92    capsules_system::process_policies::PanicFaultPolicy {};
93
94// Test access to the peripherals
95static mut PERIPHERALS: Option<&'static Apollo3DefaultPeripherals> = None;
96// Test access to board
97#[cfg(test)]
98static mut BOARD: Option<&'static kernel::Kernel> = None;
99// Test access to platform
100#[cfg(test)]
101static mut PLATFORM: Option<&'static LoRaThingsPlus> = None;
102// Test access to main loop capability
103#[cfg(test)]
104static mut MAIN_CAP: Option<&dyn kernel::capabilities::MainLoopCapability> = None;
105// Test access to alarm
106static mut ALARM: Option<&'static MuxAlarm<'static, apollo3::stimer::STimer<'static>>> = None;
107// Test access to sensors
108static mut BME280: Option<
109    &'static capsules_extra::bme280::Bme280<
110        'static,
111        capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
112    >,
113> = None;
114static mut CCS811: Option<&'static capsules_extra::ccs811::Ccs811<'static>> = None;
115#[cfg(feature = "atecc508a")]
116static mut ATECC508A: Option<&'static capsules_extra::atecc508a::Atecc508a<'static>> = None;
117
118/// Dummy buffer that causes the linker to reserve enough space for the stack.
119#[no_mangle]
120#[link_section = ".stack_buffer"]
121pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
122
123const LORA_SPI_DRIVER_NUM: usize = capsules_core::driver::NUM::LoRaPhySPI as usize;
124const LORA_GPIO_DRIVER_NUM: usize = capsules_core::driver::NUM::LoRaPhyGPIO as usize;
125
126type ChirpI2cMoistureType = components::chirp_i2c_moisture::ChirpI2cMoistureComponentType<
127    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
128>;
129type DFRobotRainFallType = components::dfrobot_rainfall_sensor::DFRobotRainFallSensorComponentType<
130    capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
131        'static,
132        apollo3::stimer::STimer<'static>,
133    >,
134    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
135>;
136type BME280Sensor = components::bme280::Bme280ComponentType<
137    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
138>;
139
140type TemperatureDriver = components::temperature::TemperatureComponentType<BME280Sensor>;
141type HumidityDriver = components::humidity::HumidityComponentType<BME280Sensor>;
142
143/// A structure representing this platform that holds references to all
144/// capsules for this platform.
145struct LoRaThingsPlus {
146    alarm: &'static capsules_core::alarm::AlarmDriver<
147        'static,
148        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
149    >,
150    led: &'static capsules_core::led::LedDriver<
151        'static,
152        LedHigh<'static, apollo3::gpio::GpioPin<'static>>,
153        1,
154    >,
155    gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
156    console: &'static capsules_core::console::Console<'static>,
157    i2c_master:
158        &'static capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
159    external_spi_controller: &'static capsules_core::spi_controller::Spi<
160        'static,
161        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
162            'static,
163            apollo3::iom::Iom<'static>,
164        >,
165    >,
166    sx1262_spi_controller: &'static capsules_core::spi_controller::Spi<
167        'static,
168        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
169            'static,
170            apollo3::iom::Iom<'static>,
171        >,
172    >,
173    sx1262_gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
174    temperature: &'static TemperatureDriver,
175    humidity: &'static HumidityDriver,
176    air_quality: &'static capsules_extra::air_quality::AirQualitySensor<'static>,
177    moisture: Option<&'static components::moisture::MoistureComponentType<ChirpI2cMoistureType>>,
178    rainfall: Option<&'static components::rainfall::RainFallComponentType<DFRobotRainFallType>>,
179    rng: Option<
180        &'static capsules_core::rng::RngDriver<
181            'static,
182            capsules_core::rng::Entropy32ToRandom<
183                'static,
184                capsules_extra::atecc508a::Atecc508a<'static>,
185            >,
186        >,
187    >,
188    scheduler: &'static RoundRobinSched<'static>,
189    systick: cortexm4::systick::SysTick,
190    kv_driver: &'static capsules_extra::kv_driver::KVStoreDriver<
191        'static,
192        capsules_extra::virtual_kv::VirtualKVPermissions<
193            'static,
194            capsules_extra::kv_store_permissions::KVStorePermissions<
195                'static,
196                capsules_extra::tickv_kv_store::TicKVKVStore<
197                    'static,
198                    capsules_extra::tickv::TicKVSystem<
199                        'static,
200                        capsules_core::virtualizers::virtual_flash::FlashUser<
201                            'static,
202                            apollo3::flashctrl::FlashCtrl<'static>,
203                        >,
204                        capsules_extra::sip_hash::SipHasher24<'static>,
205                        { apollo3::flashctrl::PAGE_SIZE },
206                    >,
207                    [u8; 8],
208                >,
209            >,
210        >,
211    >,
212}
213
214#[cfg(feature = "atecc508a")]
215fn atecc508a_wakeup() {
216    let peripherals = (unsafe { PERIPHERALS }).unwrap();
217
218    peripherals.gpio_port[6].make_output();
219    peripherals.gpio_port[6].clear();
220
221    // The ATECC508A requires the SDA line to be low for at least 60us
222    // to wake up.
223    for _i in 0..700 {
224        cortexm4::support::nop();
225    }
226
227    // Enable SDA and SCL for I2C (exposed via Qwiic)
228    let _ = &peripherals
229        .gpio_port
230        .enable_i2c(&peripherals.gpio_port[6], &peripherals.gpio_port[5]);
231}
232
233#[cfg(feature = "atecc508a")]
234unsafe fn setup_atecc508a(
235    board_kernel: &'static kernel::Kernel,
236    memory_allocation_cap: &dyn capabilities::MemoryAllocationCapability,
237    mux_i2c: &'static MuxI2C<'static, apollo3::iom::Iom<'static>>,
238) -> &'static capsules_core::rng::RngDriver<
239    'static,
240    capsules_core::rng::Entropy32ToRandom<'static, capsules_extra::atecc508a::Atecc508a<'static>>,
241> {
242    let atecc508a = Atecc508aComponent::new(mux_i2c, 0x60, atecc508a_wakeup).finalize(
243        components::atecc508a_component_static!(apollo3::iom::Iom<'static>),
244    );
245    ATECC508A = Some(atecc508a);
246
247    // Convert hardware RNG to the Random interface.
248    let entropy_to_random = static_init!(
249        capsules_core::rng::Entropy32ToRandom<
250            'static,
251            capsules_extra::atecc508a::Atecc508a<'static>,
252        >,
253        capsules_core::rng::Entropy32ToRandom::new(atecc508a)
254    );
255    atecc508a.set_client(entropy_to_random);
256    // Setup RNG for userspace
257    let rng_local = static_init!(
258        capsules_core::rng::RngDriver<
259            'static,
260            capsules_core::rng::Entropy32ToRandom<
261                'static,
262                capsules_extra::atecc508a::Atecc508a<'static>,
263            >,
264        >,
265        capsules_core::rng::RngDriver::new(
266            entropy_to_random,
267            board_kernel.create_grant(capsules_core::rng::DRIVER_NUM, memory_allocation_cap)
268        )
269    );
270    entropy_to_random.set_client(rng_local);
271
272    rng_local
273}
274
275#[cfg(feature = "chirp_i2c_moisture")]
276unsafe fn setup_chirp_i2c_moisture(
277    board_kernel: &'static kernel::Kernel,
278    _memory_allocation_cap: &dyn capabilities::MemoryAllocationCapability,
279    mux_i2c: &'static MuxI2C<'static, apollo3::iom::Iom<'static>>,
280) -> &'static components::moisture::MoistureComponentType<ChirpI2cMoistureType> {
281    let chirp_moisture =
282        components::chirp_i2c_moisture::ChirpI2cMoistureComponent::new(mux_i2c, 0x20).finalize(
283            components::chirp_i2c_moisture_component_static!(apollo3::iom::Iom<'static>),
284        );
285
286    let moisture = components::moisture::MoistureComponent::new(
287        board_kernel,
288        capsules_extra::moisture::DRIVER_NUM,
289        chirp_moisture,
290    )
291    .finalize(components::moisture_component_static!(ChirpI2cMoistureType));
292
293    moisture
294}
295
296#[cfg(feature = "dfrobot_i2c_rainfall")]
297unsafe fn setup_dfrobot_i2c_rainfall(
298    board_kernel: &'static kernel::Kernel,
299    _memory_allocation_cap: &dyn capabilities::MemoryAllocationCapability,
300    mux_i2c: &'static MuxI2C<'static, apollo3::iom::Iom<'static>>,
301    mux_alarm: &'static MuxAlarm<'static, apollo3::stimer::STimer<'static>>,
302) -> &'static components::rainfall::RainFallComponentType<DFRobotRainFallType> {
303    let dfrobot_rainfall =
304        components::dfrobot_rainfall_sensor::DFRobotRainFallSensorComponent::new(
305            mux_i2c, 0x1D, mux_alarm,
306        )
307        .finalize(components::dfrobot_rainfall_sensor_component_static!(
308            apollo3::stimer::STimer<'static>,
309            apollo3::iom::Iom<'static>
310        ));
311
312    let rainfall = components::rainfall::RainFallComponent::new(
313        board_kernel,
314        capsules_extra::rainfall::DRIVER_NUM,
315        dfrobot_rainfall,
316    )
317    .finalize(components::rainfall_component_static!(DFRobotRainFallType));
318
319    rainfall
320}
321
322/// Mapping of integer syscalls to objects that implement syscalls.
323impl SyscallDriverLookup for LoRaThingsPlus {
324    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
325    where
326        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
327    {
328        match driver_num {
329            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
330            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
331            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
332            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
333            capsules_core::i2c_master::DRIVER_NUM => f(Some(self.i2c_master)),
334            capsules_core::spi_controller::DRIVER_NUM => f(Some(self.external_spi_controller)),
335            LORA_SPI_DRIVER_NUM => f(Some(self.sx1262_spi_controller)),
336            LORA_GPIO_DRIVER_NUM => f(Some(self.sx1262_gpio)),
337            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temperature)),
338            capsules_extra::humidity::DRIVER_NUM => f(Some(self.humidity)),
339            capsules_extra::air_quality::DRIVER_NUM => f(Some(self.air_quality)),
340            capsules_extra::kv_driver::DRIVER_NUM => f(Some(self.kv_driver)),
341            capsules_core::rng::DRIVER_NUM => {
342                if let Some(rng) = self.rng {
343                    f(Some(rng))
344                } else {
345                    f(None)
346                }
347            }
348            capsules_extra::moisture::DRIVER_NUM => {
349                if let Some(moisture) = self.moisture {
350                    f(Some(moisture))
351                } else {
352                    f(None)
353                }
354            }
355            capsules_extra::rainfall::DRIVER_NUM => {
356                if let Some(rainfall) = self.rainfall {
357                    f(Some(rainfall))
358                } else {
359                    f(None)
360                }
361            }
362            _ => f(None),
363        }
364    }
365}
366
367impl KernelResources<apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> for LoRaThingsPlus {
368    type SyscallDriverLookup = Self;
369    type SyscallFilter = ();
370    type ProcessFault = ();
371    type Scheduler = RoundRobinSched<'static>;
372    type SchedulerTimer = cortexm4::systick::SysTick;
373    type WatchDog = ();
374    type ContextSwitchCallback = ();
375
376    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
377        self
378    }
379    fn syscall_filter(&self) -> &Self::SyscallFilter {
380        &()
381    }
382    fn process_fault(&self) -> &Self::ProcessFault {
383        &()
384    }
385    fn scheduler(&self) -> &Self::Scheduler {
386        self.scheduler
387    }
388    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
389        &self.systick
390    }
391    fn watchdog(&self) -> &Self::WatchDog {
392        &()
393    }
394    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
395        &()
396    }
397}
398
399// Ensure that `setup()` is never inlined
400// This helps reduce the stack frame, see https://github.com/tock/tock/issues/3518
401#[inline(never)]
402unsafe fn setup() -> (
403    &'static kernel::Kernel,
404    &'static LoRaThingsPlus,
405    &'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
406) {
407    let peripherals = static_init!(Apollo3DefaultPeripherals, Apollo3DefaultPeripherals::new());
408    PERIPHERALS = Some(peripherals);
409
410    // No need to statically allocate mcu/pwr/clk_ctrl because they are only used in main!
411    let mcu_ctrl = apollo3::mcuctrl::McuCtrl::new();
412    let pwr_ctrl = apollo3::pwrctrl::PwrCtrl::new();
413    let clkgen = apollo3::clkgen::ClkGen::new();
414
415    clkgen.set_clock_frequency(apollo3::clkgen::ClockFrequency::Freq48MHz);
416
417    // initialize capabilities
418    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
419
420    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
421
422    // Power up components
423    pwr_ctrl.enable_uart0();
424    pwr_ctrl.enable_iom0();
425    pwr_ctrl.enable_iom2();
426    pwr_ctrl.enable_iom3();
427
428    peripherals.init();
429
430    // Enable PinCfg
431    peripherals
432        .gpio_port
433        .enable_uart(&peripherals.gpio_port[48], &peripherals.gpio_port[49]);
434    // Enable Main SPI
435    peripherals.gpio_port.enable_spi(
436        &peripherals.gpio_port[27],
437        &peripherals.gpio_port[28],
438        &peripherals.gpio_port[25],
439    );
440    // Enable SPI for SX1262
441    peripherals.gpio_port.enable_spi(
442        &peripherals.gpio_port[42],
443        &peripherals.gpio_port[38],
444        &peripherals.gpio_port[43],
445    );
446    // Enable the radio pins
447    peripherals.gpio_port.enable_sx1262_radio_pins();
448
449    // Configure kernel debug gpios as early as possible
450    kernel::debug::assign_gpios(Some(&peripherals.gpio_port[26]), None, None);
451
452    // Create a shared UART channel for the console and for kernel debug.
453    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
454        .finalize(components::uart_mux_component_static!());
455
456    // Setup the console.
457    let console = components::console::ConsoleComponent::new(
458        board_kernel,
459        capsules_core::console::DRIVER_NUM,
460        uart_mux,
461    )
462    .finalize(components::console_component_static!());
463    // Create the debugger object that handles calls to `debug!()`.
464    components::debug_writer::DebugWriterComponent::new(
465        uart_mux,
466        create_capability!(capabilities::SetDebugWriterCapability),
467    )
468    .finalize(components::debug_writer_component_static!());
469
470    // LEDs
471    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
472        LedHigh<'static, apollo3::gpio::GpioPin>,
473        LedHigh::new(&peripherals.gpio_port[19]),
474    ));
475
476    // GPIOs
477    // Details are at: https://github.com/NorthernMechatronics/nmsdk/blob/master/bsp/nm180100evb/bsp_pins.src
478    let gpio = components::gpio::GpioComponent::new(
479        board_kernel,
480        capsules_core::gpio::DRIVER_NUM,
481        components::gpio_component_helper!(
482            apollo3::gpio::GpioPin,
483            0 => &peripherals.gpio_port[13],  // A0
484            1 => &peripherals.gpio_port[12],  // A1
485            2 => &peripherals.gpio_port[32],  // A2
486            3 => &peripherals.gpio_port[35],  // A3
487            4 => &peripherals.gpio_port[34],  // A4
488        ),
489    )
490    .finalize(components::gpio_component_static!(apollo3::gpio::GpioPin));
491
492    // Create a shared virtualisation mux layer on top of a single hardware
493    // alarm.
494    let _ = peripherals.stimer.start();
495    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.stimer).finalize(
496        components::alarm_mux_component_static!(apollo3::stimer::STimer),
497    );
498    let alarm = components::alarm::AlarmDriverComponent::new(
499        board_kernel,
500        capsules_core::alarm::DRIVER_NUM,
501        mux_alarm,
502    )
503    .finalize(components::alarm_component_static!(apollo3::stimer::STimer));
504    ALARM = Some(mux_alarm);
505
506    // Create a process printer for panic.
507    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
508        .finalize(components::process_printer_text_component_static!());
509    PROCESS_PRINTER = Some(process_printer);
510
511    // Enable SDA and SCL for I2C (exposed via Qwiic)
512    peripherals
513        .gpio_port
514        .enable_i2c(&peripherals.gpio_port[6], &peripherals.gpio_port[5]);
515
516    // Init the I2C device attached via Qwiic
517    let i2c_master_buffer = static_init!(
518        [u8; capsules_core::i2c_master::BUFFER_LENGTH],
519        [0; capsules_core::i2c_master::BUFFER_LENGTH]
520    );
521    let i2c_master = static_init!(
522        capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
523        capsules_core::i2c_master::I2CMasterDriver::new(
524            &peripherals.iom0,
525            i2c_master_buffer,
526            board_kernel.create_grant(
527                capsules_core::i2c_master::DRIVER_NUM,
528                &memory_allocation_cap
529            )
530        )
531    );
532
533    peripherals.iom0.set_master_client(i2c_master);
534    peripherals.iom0.enable();
535
536    let mux_i2c = components::i2c::I2CMuxComponent::new(&peripherals.iom0, None).finalize(
537        components::i2c_mux_component_static!(apollo3::iom::Iom<'static>),
538    );
539
540    let bme280 = Bme280Component::new(mux_i2c, 0x77).finalize(
541        components::bme280_component_static!(apollo3::iom::Iom<'static>),
542    );
543    let temperature = components::temperature::TemperatureComponent::new(
544        board_kernel,
545        capsules_extra::temperature::DRIVER_NUM,
546        bme280,
547    )
548    .finalize(components::temperature_component_static!(BME280Sensor));
549    let humidity = components::humidity::HumidityComponent::new(
550        board_kernel,
551        capsules_extra::humidity::DRIVER_NUM,
552        bme280,
553    )
554    .finalize(components::humidity_component_static!(BME280Sensor));
555    BME280 = Some(bme280);
556
557    let ccs811 = Ccs811Component::new(mux_i2c, 0x5B).finalize(
558        components::ccs811_component_static!(apollo3::iom::Iom<'static>),
559    );
560    let air_quality = components::air_quality::AirQualityComponent::new(
561        board_kernel,
562        capsules_extra::temperature::DRIVER_NUM,
563        ccs811,
564    )
565    .finalize(components::air_quality_component_static!());
566    CCS811 = Some(ccs811);
567
568    #[cfg(feature = "chirp_i2c_moisture")]
569    let moisture = Some(setup_chirp_i2c_moisture(
570        board_kernel,
571        &memory_allocation_cap,
572        mux_i2c,
573    ));
574    #[cfg(not(feature = "chirp_i2c_moisture"))]
575    let moisture = None;
576
577    #[cfg(feature = "dfrobot_i2c_rainfall")]
578    let rainfall = Some(setup_dfrobot_i2c_rainfall(
579        board_kernel,
580        &memory_allocation_cap,
581        mux_i2c,
582        mux_alarm,
583    ));
584    #[cfg(not(feature = "dfrobot_i2c_rainfall"))]
585    let rainfall = None;
586
587    #[cfg(feature = "atecc508a")]
588    let rng = Some(setup_atecc508a(
589        board_kernel,
590        &memory_allocation_cap,
591        mux_i2c,
592    ));
593    #[cfg(not(feature = "atecc508a"))]
594    let rng = None;
595
596    // Init the broken out SPI controller
597    let external_mux_spi = components::spi::SpiMuxComponent::new(&peripherals.iom2).finalize(
598        components::spi_mux_component_static!(apollo3::iom::Iom<'static>),
599    );
600
601    let external_spi_controller = components::spi::SpiSyscallComponent::new(
602        board_kernel,
603        external_mux_spi,
604        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
605            &peripherals.gpio_port[11], // A5
606        ),
607        capsules_core::spi_controller::DRIVER_NUM,
608    )
609    .finalize(components::spi_syscall_component_static!(
610        apollo3::iom::Iom<'static>
611    ));
612
613    // Init the internal SX1262 SPI controller
614    let sx1262_mux_spi = components::spi::SpiMuxComponent::new(&peripherals.iom3).finalize(
615        components::spi_mux_component_static!(apollo3::iom::Iom<'static>),
616    );
617
618    let sx1262_spi_controller = components::spi::SpiSyscallComponent::new(
619        board_kernel,
620        sx1262_mux_spi,
621        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
622            &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
623        ),
624        LORA_SPI_DRIVER_NUM,
625    )
626    .finalize(components::spi_syscall_component_static!(
627        apollo3::iom::Iom<'static>
628    ));
629    peripherals
630        .iom3
631        .specify_chip_select(kernel::hil::spi::cs::IntoChipSelect::<
632            _,
633            kernel::hil::spi::cs::ActiveLow,
634        >::into_cs(
635            &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
636        ))
637        .unwrap();
638
639    let sx1262_gpio = components::gpio::GpioComponent::new(
640        board_kernel,
641        LORA_GPIO_DRIVER_NUM,
642        components::gpio_component_helper!(
643            apollo3::gpio::GpioPin,
644            0 => &peripherals.gpio_port[36], // H6 - SX1262 Slave Select
645            1 => &peripherals.gpio_port[39], // J8 - SX1262 Radio Busy Indicator
646            2 => &peripherals.gpio_port[40], // J9 - SX1262 Multipurpose digital I/O (DIO1)
647            3 => &peripherals.gpio_port[47], // H9 - SX1262 Multipurpose digital I/O (DIO3)
648            4 => &peripherals.gpio_port[44], // J7 - SX1262 Reset
649        ),
650    )
651    .finalize(components::gpio_component_static!(apollo3::gpio::GpioPin));
652
653    // Setup BLE
654    mcu_ctrl.disable_ble();
655
656    // Flash
657    let flash_ctrl_read_buf = static_init!(
658        [u8; apollo3::flashctrl::PAGE_SIZE],
659        [0; apollo3::flashctrl::PAGE_SIZE]
660    );
661    let page_buffer = static_init!(
662        apollo3::flashctrl::Apollo3Page,
663        apollo3::flashctrl::Apollo3Page::default()
664    );
665
666    let mux_flash = components::flash::FlashMuxComponent::new(&peripherals.flash_ctrl).finalize(
667        components::flash_mux_component_static!(apollo3::flashctrl::FlashCtrl),
668    );
669
670    // SipHash
671    let sip_hash = static_init!(
672        capsules_extra::sip_hash::SipHasher24,
673        capsules_extra::sip_hash::SipHasher24::new()
674    );
675    kernel::deferred_call::DeferredCallClient::register(sip_hash);
676
677    // TicKV
678    let tickv = components::tickv::TicKVComponent::new(
679        sip_hash,
680        mux_flash, // Flash controller
681        core::ptr::addr_of!(_skv_data) as usize / apollo3::flashctrl::PAGE_SIZE, // Region offset (Last 0x28000 bytes of flash)
682        // Region Size, the final page doens't work correctly
683        core::ptr::addr_of!(_lkv_data) as usize - apollo3::flashctrl::PAGE_SIZE,
684        flash_ctrl_read_buf, // Buffer used internally in TicKV
685        page_buffer,         // Buffer used with the flash controller
686    )
687    .finalize(components::tickv_component_static!(
688        apollo3::flashctrl::FlashCtrl,
689        capsules_extra::sip_hash::SipHasher24,
690        { apollo3::flashctrl::PAGE_SIZE }
691    ));
692    HasClient::set_client(&peripherals.flash_ctrl, mux_flash);
693    sip_hash.set_client(tickv);
694
695    let kv_store = components::kv::TicKVKVStoreComponent::new(tickv).finalize(
696        components::tickv_kv_store_component_static!(
697            capsules_extra::tickv::TicKVSystem<
698                capsules_core::virtualizers::virtual_flash::FlashUser<
699                    apollo3::flashctrl::FlashCtrl,
700                >,
701                capsules_extra::sip_hash::SipHasher24<'static>,
702                { apollo3::flashctrl::PAGE_SIZE },
703            >,
704            capsules_extra::tickv::TicKVKeyType,
705        ),
706    );
707
708    let kv_store_permissions = components::kv::KVStorePermissionsComponent::new(kv_store).finalize(
709        components::kv_store_permissions_component_static!(
710            capsules_extra::tickv_kv_store::TicKVKVStore<
711                capsules_extra::tickv::TicKVSystem<
712                    capsules_core::virtualizers::virtual_flash::FlashUser<
713                        apollo3::flashctrl::FlashCtrl,
714                    >,
715                    capsules_extra::sip_hash::SipHasher24<'static>,
716                    { apollo3::flashctrl::PAGE_SIZE },
717                >,
718                capsules_extra::tickv::TicKVKeyType,
719            >
720        ),
721    );
722
723    let mux_kv = components::kv::KVPermissionsMuxComponent::new(kv_store_permissions).finalize(
724        components::kv_permissions_mux_component_static!(
725            capsules_extra::kv_store_permissions::KVStorePermissions<
726                capsules_extra::tickv_kv_store::TicKVKVStore<
727                    capsules_extra::tickv::TicKVSystem<
728                        capsules_core::virtualizers::virtual_flash::FlashUser<
729                            apollo3::flashctrl::FlashCtrl,
730                        >,
731                        capsules_extra::sip_hash::SipHasher24<'static>,
732                        { apollo3::flashctrl::PAGE_SIZE },
733                    >,
734                    capsules_extra::tickv::TicKVKeyType,
735                >,
736            >
737        ),
738    );
739
740    let virtual_kv_driver = components::kv::VirtualKVPermissionsComponent::new(mux_kv).finalize(
741        components::virtual_kv_permissions_component_static!(
742            capsules_extra::kv_store_permissions::KVStorePermissions<
743                capsules_extra::tickv_kv_store::TicKVKVStore<
744                    capsules_extra::tickv::TicKVSystem<
745                        capsules_core::virtualizers::virtual_flash::FlashUser<
746                            apollo3::flashctrl::FlashCtrl,
747                        >,
748                        capsules_extra::sip_hash::SipHasher24<'static>,
749                        { apollo3::flashctrl::PAGE_SIZE },
750                    >,
751                    capsules_extra::tickv::TicKVKeyType,
752                >,
753            >
754        ),
755    );
756
757    let kv_driver = components::kv::KVDriverComponent::new(
758        virtual_kv_driver,
759        board_kernel,
760        capsules_extra::kv_driver::DRIVER_NUM,
761    )
762    .finalize(components::kv_driver_component_static!(
763        capsules_extra::virtual_kv::VirtualKVPermissions<
764            capsules_extra::kv_store_permissions::KVStorePermissions<
765                capsules_extra::tickv_kv_store::TicKVKVStore<
766                    capsules_extra::tickv::TicKVSystem<
767                        capsules_core::virtualizers::virtual_flash::FlashUser<
768                            apollo3::flashctrl::FlashCtrl,
769                        >,
770                        capsules_extra::sip_hash::SipHasher24<'static>,
771                        { apollo3::flashctrl::PAGE_SIZE },
772                    >,
773                    capsules_extra::tickv::TicKVKeyType,
774                >,
775            >,
776        >
777    ));
778
779    mcu_ctrl.print_chip_revision();
780
781    debug!("Initialization complete. Entering main loop");
782
783    // These symbols are defined in the linker script.
784    extern "C" {
785        /// Beginning of the ROM region containing app images.
786        static _sapps: u8;
787        /// End of the ROM region containing app images.
788        static _eapps: u8;
789        /// Beginning of the RAM region for app memory.
790        static mut _sappmem: u8;
791        /// End of the RAM region for app memory.
792        static _eappmem: u8;
793        /// Beginning of the RAM region containing K/V data.
794        static _skv_data: u8;
795        /// Length of the RAM region containing K/V data.
796        static _lkv_data: u8;
797    }
798
799    let scheduler = components::sched::round_robin::RoundRobinComponent::new(&*addr_of!(PROCESSES))
800        .finalize(components::round_robin_component_static!(NUM_PROCS));
801
802    let systick = cortexm4::systick::SysTick::new_with_calibration(48_000_000);
803
804    let artemis_nano = static_init!(
805        LoRaThingsPlus,
806        LoRaThingsPlus {
807            alarm,
808            led,
809            gpio,
810            console,
811            i2c_master,
812            external_spi_controller,
813            sx1262_spi_controller,
814            sx1262_gpio,
815            temperature,
816            humidity,
817            air_quality,
818            moisture,
819            rainfall,
820            rng,
821            scheduler,
822            systick,
823            kv_driver,
824        }
825    );
826
827    let chip = static_init!(
828        apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
829        apollo3::chip::Apollo3::new(peripherals)
830    );
831    CHIP = Some(chip);
832
833    let checking_policy;
834    #[cfg(feature = "atecc508a")]
835    {
836        // Create the software-based SHA engine.
837        // We could use the ATECC508a for SHA, but writing the entire
838        // application to the device to compute a digtest ends up being
839        // pretty slow and the ATECC508a doesn't support the DigestVerify trait
840        let sha = components::sha::ShaSoftware256Component::new()
841            .finalize(components::sha_software_256_component_static!());
842
843        // These are the generated test keys used below, please do not use them
844        // for anything important!!!!
845        //
846        // These keys are not leaked, they are only used for this test case.
847        //
848        // -----BEGIN PRIVATE KEY-----
849        // MIGHAgEBMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWClhguWHtAK85Kqc
850        // /BucDBQMGQw6R2PEQkyISHkn5xWhRANCAAQUFMTFoNL9oFpGmg6Cp351hQMq9hol
851        // KpEdQfjP1nYF1jxqz52YjPpFHvudkK/fFsik5Rd0AevNkQqjBdWEqmpW
852        // -----END PRIVATE KEY-----
853        //
854        // -----BEGIN PUBLIC KEY-----
855        // MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFBTExaDS/aBaRpoOgqd+dYUDKvYa
856        // JSqRHUH4z9Z2BdY8as+dmIz6RR77nZCv3xbIpOUXdAHrzZEKowXVhKpqVg==
857        // -----END PUBLIC KEY-----
858        let public_key = static_init!(
859            [u8; 64],
860            [
861                0x14, 0x14, 0xc4, 0xc5, 0xa0, 0xd2, 0xfd, 0xa0, 0x5a, 0x46, 0x9a, 0x0e, 0x82, 0xa7,
862                0x7e, 0x75, 0x85, 0x03, 0x2a, 0xf6, 0x1a, 0x25, 0x2a, 0x91, 0x1d, 0x41, 0xf8, 0xcf,
863                0xd6, 0x76, 0x05, 0xd6, 0x3c, 0x6a, 0xcf, 0x9d, 0x98, 0x8c, 0xfa, 0x45, 0x1e, 0xfb,
864                0x9d, 0x90, 0xaf, 0xdf, 0x16, 0xc8, 0xa4, 0xe5, 0x17, 0x74, 0x01, 0xeb, 0xcd, 0x91,
865                0x0a, 0xa3, 0x05, 0xd5, 0x84, 0xaa, 0x6a, 0x56
866            ]
867        );
868
869        ATECC508A.unwrap().set_public_key(Some(public_key));
870
871        checking_policy = components::appid::checker_signature::AppCheckerSignatureComponent::new(
872            sha,
873            ATECC508A.unwrap(),
874            tock_tbf::types::TbfFooterV2CredentialsType::EcdsaNistP256,
875        )
876        .finalize(components::app_checker_signature_component_static!(
877            capsules_extra::atecc508a::Atecc508a<'static>,
878            capsules_extra::sha256::Sha256Software<'static>,
879            32,
880            64,
881        ));
882    };
883    #[cfg(not(feature = "atecc508a"))]
884    {
885        checking_policy = components::appid::checker_null::AppCheckerNullComponent::new()
886            .finalize(components::app_checker_null_component_static!());
887    }
888
889    // Create the AppID assigner.
890    let assigner = components::appid::assigner_name::AppIdAssignerNamesComponent::new()
891        .finalize(components::appid_assigner_names_component_static!());
892
893    // Create the process checking machine.
894    let checker = components::appid::checker::ProcessCheckerMachineComponent::new(checking_policy)
895        .finalize(components::process_checker_machine_component_static!());
896
897    let storage_permissions_policy =
898        components::storage_permissions::tbf_header::StoragePermissionsTbfHeaderComponent::new()
899            .finalize(
900                components::storage_permissions_tbf_header_component_static!(
901                    apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
902                    kernel::process::ProcessStandardDebugFull,
903                ),
904            );
905
906    let app_flash = core::slice::from_raw_parts(
907        core::ptr::addr_of!(_sapps),
908        core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
909    );
910    let app_memory = core::slice::from_raw_parts_mut(
911        core::ptr::addr_of_mut!(_sappmem),
912        core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
913    );
914
915    // Create and start the asynchronous process loader.
916    let _loader = components::loader::sequential::ProcessLoaderSequentialComponent::new(
917        checker,
918        &mut *addr_of_mut!(PROCESSES),
919        board_kernel,
920        chip,
921        &FAULT_RESPONSE,
922        assigner,
923        storage_permissions_policy,
924        app_flash,
925        app_memory,
926    )
927    .finalize(components::process_loader_sequential_component_static!(
928        apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
929        kernel::process::ProcessStandardDebugFull,
930        NUM_PROCS,
931    ));
932
933    (board_kernel, artemis_nano, chip)
934}
935
936/// Main function.
937///
938/// This function is called from the arch crate after some very basic RISC-V
939/// setup and RAM initialization.
940#[no_mangle]
941pub unsafe fn main() {
942    apollo3::init();
943
944    #[cfg(test)]
945    test_main();
946
947    #[cfg(not(test))]
948    {
949        let (board_kernel, sf_lora_thing_plus_board, chip) = setup();
950
951        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
952
953        board_kernel.kernel_loop(
954            sf_lora_thing_plus_board,
955            chip,
956            None::<&kernel::ipc::IPC<{ NUM_PROCS as u8 }>>,
957            &main_loop_cap,
958        );
959    }
960}
961
962#[cfg(test)]
963use kernel::platform::watchdog::WatchDog;
964
965#[cfg(test)]
966fn test_runner(tests: &[&dyn Fn()]) {
967    unsafe {
968        let (board_kernel, sf_lora_thing_plus_board, _chip) = setup();
969
970        BOARD = Some(board_kernel);
971        PLATFORM = Some(&sf_lora_thing_plus_board);
972        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
973
974        PLATFORM.map(|p| {
975            p.watchdog().setup();
976        });
977
978        for test in tests {
979            test();
980        }
981    }
982
983    loop {}
984}