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