//! # Embassy-rs support for the Vorago VA416xx MCU family
//!
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
//! VA416xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
//! peripherals provided by the VA416xx family for this purpose.
//!
//! ## Usage
//!
//! This library only exposes the [embassy::init] method which sets up the time driver. This
//! function must be called once at the start of the application.
//!
//! This implementation requires two TIM peripherals provided by the VA108xx device.
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
//! into the [init] method. If the interrupt handlers are provided by the library, the ID of the
//! used TIM peripherals has to match the ID of the passed timer peripherals. Currently, this
//! can only be checked at run-time, and a run-time assertion will panic on the embassy
//! initialization in case of a missmatch.
//!
//! The application also requires two interrupt handlers to handle the timekeeper and alarm
//! interrupts. By default, this library will define the interrupt handler inside the library
//! itself by using the `irq-tim14-tim15` feature flag. This library exposes three combinations:
//!
//! - `irq-tim14-tim15`: Uses [pac::Interrupt::TIM14] for alarm and [pac::Interrupt::TIM15]
//!   for timekeeper
//! - `irq-tim13-tim14`: Uses [pac::Interrupt::TIM13] for alarm and [pac::Interrupt::TIM14]
//!   for timekeeper
//! - `irq-tim22-tim23`: Uses [pac::Interrupt::TIM22] for alarm and [pac::Interrupt::TIM23]
//!   for timekeeper
//!
//! You can disable the default features and then specify one of the features above to use the
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library.
//!
//! ## Examples
//!
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use va416xx_hal::{
    clock::Clocks,
    irq_router::enable_and_init_irq_router,
    pac::{self, interrupt},
    timer::{TimInstance, TIM_IRQ_OFFSET},
};
use vorago_shared_hal::embassy::time_driver;

/// Macro to define the IRQ handlers for the time driver.
///
/// By default, the code generated by this macro will be defined inside the library depending on
/// the feature flags specified. However, the macro is exported to allow users to specify the
/// interrupt handlers themselves.
///
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
/// macro in the application code in case this macro is used there.
#[macro_export]
macro_rules! embassy_time_driver_irqs {
    (
        timekeeper_irq = $timekeeper_irq:ident,
        alarm_irq = $alarm_irq:ident
    ) => {
        const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::$timekeeper_irq;

        #[interrupt]
        #[allow(non_snake_case)]
        fn $timekeeper_irq() {
            // Safety: We call it once here.
            unsafe { $crate::time_driver().on_interrupt_timekeeping() }
        }

        const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;

        #[interrupt]
        #[allow(non_snake_case)]
        fn $alarm_irq() {
            // Safety: We call it once here.
            unsafe { $crate::time_driver().on_interrupt_alarm() }
        }
    };
}

// Provide three combinations of IRQs for the time driver by default.

#[cfg(feature = "irq-tim14-tim15")]
embassy_time_driver_irqs!(timekeeper_irq = TIM15, alarm_irq = TIM14);
#[cfg(feature = "irq-tim13-tim14")]
embassy_time_driver_irqs!(timekeeper_irq = TIM14, alarm_irq = TIM13);
#[cfg(feature = "irq-tim22-tim23")]
embassy_time_driver_irqs!(timekeeper_irq = TIM23, alarm_irq = TIM22);

/// Initialization method for embassy
///
/// If the interrupt handlers are provided by the library, the ID of the
/// used TIM peripherals has to match the ID of the passed timer peripherals. Currently, this
/// can only be checked at run-time, and a run-time assertion will panic on the embassy
/// initialization in case of a missmatch.
pub fn init<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
    timekeeper: TimekeeperTim,
    alarm: AlarmTim,
    clocks: &Clocks,
) {
    #[cfg(feature = "_irqs-in-lib")]
    assert_eq!(
        TimekeeperTim::ID.value(),
        TIMEKEEPER_IRQ as u8 - TIM_IRQ_OFFSET as u8,
        "Timekeeper TIM and IRQ missmatch"
    );
    #[cfg(feature = "_irqs-in-lib")]
    assert_eq!(
        AlarmTim::ID.value(),
        ALARM_IRQ as u8 - TIM_IRQ_OFFSET as u8,
        "Alarm TIM and IRQ missmatch"
    );
    enable_and_init_irq_router();
    time_driver().__init(timekeeper, alarm, clocks)
}
