//! # Embassy-rs support for the Vorago VA108xx MCU family
//!
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for
//! the VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses
//! the TIM peripherals provided by the VA108xx family for this purpose.
//!
//! ## Usage
//!
//! This library exposes the [init] or the [init_with_custom_irqs] functions which set 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_with_custom_irqs] and [init] method.
//!
//! 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-oc30-oc31` feature flag. This library exposes three combinations:
//!
//! - `irq-oc30-oc31`: Uses [pac::Interrupt::OC30] and [pac::Interrupt::OC31]
//! - `irq-oc29-oc30`: Uses [pac::Interrupt::OC29] and [pac::Interrupt::OC30]
//! - `irq-oc28-oc29`: Uses [pac::Interrupt::OC28] and [pac::Interrupt::OC20]
//!
//! 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, [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/vorago-rs/src/branch/main/va108xx/examples/embassy)
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[cfg(feature = "irqs-in-lib")]
use va108xx_hal::pac::{self, interrupt};
use va108xx_hal::time::Hertz;
use va108xx_hal::timer::TimInstance;
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-oc30-oc31")]
embassy_time_driver_irqs!(timekeeper_irq = OC31, alarm_irq = OC30);
#[cfg(feature = "irq-oc29-oc30")]
embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
#[cfg(feature = "irq-oc28-oc29")]
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);

/// Initialization method for embassy.
///
/// This should be used if the interrupt handler is provided by the library, which is the
/// default case.
#[cfg(feature = "irqs-in-lib")]
pub fn init<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
    timekeeper_tim: TimekeeperTim,
    alarm_tim: AlarmTim,
    sysclk: Hertz,
) {
    time_driver().__init(sysclk, timekeeper_tim, alarm_tim, TIMEKEEPER_IRQ, ALARM_IRQ)
}

/// Initialization method for embassy when using custom IRQ handlers.
///
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs.
pub fn init_with_custom_irqs<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
    timekeeper_tim: TimekeeperTim,
    alarm_tim: AlarmTim,
    sysclk: Hertz,
    timekeeper_irq: pac::Interrupt,
    alarm_irq: pac::Interrupt,
) {
    time_driver().__init(sysclk, timekeeper_tim, alarm_tim, timekeeper_irq, alarm_irq)
}
