/src/peripherals.rs
mod keyboard;
mod usbdev;

use core::cell::RefCell;

use crate::bsp;
use crate::mpu::init_mpu;
use crate::timer::init_timer;
use bsp::hal::clocks::init_clocks_and_plls;
use bsp::hal::gpio;
use bsp::hal::pac;
use bsp::hal::watchdog::Watchdog;
use bsp::hal::Sio;
use bsp::hal::Timer;
use bsp::Pins;
use cortex_m::peripheral::scb::SystemHandler;
use critical_section::Mutex;

use embedded_hal::digital::v2::PinState;
pub use keyboard::Keyboard;
use usbdev::init_usb;
pub use usbdev::with_usb;

const EXTERNAL_XTAL_FREQ_HZ: u32 = 12_000_000;

static PERIPHERALS: Mutex<RefCell<Option<Peripherals>>> = Mutex::new(RefCell::new(None));

type LedPin = gpio::Pin<gpio::bank0::Gpio7, gpio::FunctionSioOutput, gpio::PullNone>;

pub struct Peripherals {
    watchdog: Watchdog,
    timer: Timer,
    led: LedPin,
    keyboard: Keyboard,
}

impl Peripherals {
    pub fn timer(&mut self) -> &mut Timer {
        &mut self.timer
    }

    pub fn led(&mut self) -> &mut LedPin {
        &mut self.led
    }

    pub fn keyboard(&mut self) -> &mut Keyboard {
        &mut self.keyboard
    }
}

pub fn init_peripherals() {
    let mut core = pac::CorePeripherals::take().unwrap();
    // Set some core exception priorities
    unsafe {
        core.SCB.set_priority(SystemHandler::SysTick, 0xFF);
        core.SCB.set_priority(SystemHandler::SVCall, 0xFF);
        core.SCB.set_priority(SystemHandler::PendSV, 0xFF);
    }
    let mut platform = pac::Peripherals::take().unwrap();
    let mut watchdog = Watchdog::new(platform.WATCHDOG);
    let clocks = init_clocks_and_plls(
        EXTERNAL_XTAL_FREQ_HZ,
        platform.XOSC,
        platform.CLOCKS,
        platform.PLL_SYS,
        platform.PLL_USB,
        &mut platform.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    let sio = Sio::new(platform.SIO);
    let pins = Pins::new(
        platform.IO_BANK0,
        platform.PADS_BANK0,
        sio.gpio_bank0,
        &mut platform.RESETS,
    );
    let led = pins.led.into_push_pull_output().into_pull_type();
    let keyboard = Keyboard::new(
        [
            pins.kb_c1.into_pull_up_input().into_dyn_pin(),
            pins.kb_c2.into_pull_up_input().into_dyn_pin(),
            pins.kb_c3.into_pull_up_input().into_dyn_pin(),
            pins.kb_c4.into_pull_up_input().into_dyn_pin(),
            pins.kb_c5.into_pull_up_input().into_dyn_pin(),
            pins.kb_c6.into_pull_up_input().into_dyn_pin(),
            pins.kb_c7.into_pull_up_input().into_dyn_pin(),
            pins.kb_c8.into_pull_up_input().into_dyn_pin(),
            pins.kb_c9.into_pull_up_input().into_dyn_pin(),
        ],
        [
            pins.kb_r1
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r2
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r3
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r4
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r5
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r6
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r7
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r8
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
            pins.kb_r9
                .into_push_pull_output_in_state(PinState::High)
                .into_pull_type()
                .into_dyn_pin(),
        ],
    );

    init_timer(core.SYST);
    let timer = Timer::new(platform.TIMER, &mut platform.RESETS, &clocks);

    init_usb(
        platform.USBCTRL_REGS,
        platform.USBCTRL_DPRAM,
        clocks.usb_clock,
        &mut platform.RESETS,
    );
    unsafe {
        pac::NVIC::unmask(pac::Interrupt::USBCTRL_IRQ);
    }

    init_mpu(core.MPU);

    critical_section::with(|cs| {
        let mut peripherals = PERIPHERALS.borrow_ref_mut(cs);
        *peripherals = Some(Peripherals {
            watchdog,
            timer,
            led,
            keyboard,
        });
    })
}

pub fn with_peripherals<F, R>(mut f: F) -> R
where
    F: FnMut(&mut Peripherals) -> R,
{
    critical_section::with(|cs| {
        let mut peripherals = PERIPHERALS.borrow_ref_mut(cs);
        if let Some(ref mut peripherals) = *peripherals {
            f(peripherals)
        } else {
            panic!("Peripherals not initialized");
        }
    })
}