polymer 0.2.0

Modular keyboard firmware.
#![feature(alloc_error_handler, never_type)]
#![no_std]
#![no_main]

extern crate alloc;

#[cfg(feature = "semihosting")]
extern crate panic_semihosting;

#[panic_handler]
#[cfg(all(not(feature = "itm"), not(feature = "semihosting")))]
fn panic_bootloader(_: &core::panic::PanicInfo) -> ! {
    // At worst, this'll just reboot the board if the bkp isn't enabled
    unsafe { crate::reboot::reboot_bootloader(core::mem::transmute(())) }
}

#[cfg(feature = "itm")]
extern crate panic_itm;

#[cfg(all(feature = "itm", feature = "semihosting"))]
compile_error!("can't enable itm and semihosting simultaneously");

#[macro_use]
mod allocator;
#[macro_use]
mod matrix;

mod executor;
mod layout;
mod mutex;
mod reboot;
mod usb;
mod wake_flag;

use crate::{
    executor::Executor,
    matrix::{
        Scanner,
        StatefulScanner,
    },
    mutex::IFree,
    usb::UsbSink,
    wake_flag::*,
};
use cortex_m_rt as rt;
use embedded_hal::prelude::*;
use keebrs::{
    core::Core,
    led::NoLeds,
    matrix::{
        Matrix,
        Pull,
        Read,
    },
    net::chain::ChainNet,
    translate::Translator,
    usb::{
        hid::HidClass,
        keyboard::Keyboard,
    },
};
use log::{
    error,
    info,
};
use rtic::app;
use stm32_usbd::UsbBus;
use stm32f1xx_futures::{
    self,
    hal::{
        self,
        serial::{
            Config as SerialConfig,
            Serial,
        },
        time::KiloHertz,
        timer::Timer,
        usb::Peripheral as UsbPeripheral,
    },
    prelude::*,
    serial,
    stm32::{
        self,
        TIM1,
    },
    timer,
};
use usb_device::{
    bus::UsbBusAllocator,
    device::UsbDeviceBuilder,
    prelude::*,
};

type UsbBusType = UsbBus<UsbPeripheral>;

set_allocator!();

const SCANS_PER_SEC: usize = 2_000;
const DEBOUNCE_MS: usize = 5;

const SCAN_KHZ: usize = SCANS_PER_SEC / 1_000;
const DEBOUNCE_CYCLES: usize = SCAN_KHZ * DEBOUNCE_MS;

const VID: u16 = 0x1209;
const PID: u16 = 0x9200;

const USART_BAUD: u32 = 115_200;
// const USART_BAUD: u32 = 9_600;

#[app(device = stm32f1xx_futures::stm32, peripherals = true)]
const APP: () = {
    struct Resources {
        KB_CLASS: HidClass<'static, UsbBusType, Keyboard<NoLeds>>,
        USB_DEVICE: UsbDevice<'static, UsbBusType>,

        MATRIX: Scanner,
        SCAN_REACTOR: timer::Reactor<TIM1>,
        LEFT_REACTOR: serial::Reactor<stm32::USART1, [u8; 32]>,
        RIGHT_REACTOR: serial::Reactor<stm32::USART3, [u8; 32]>,

        #[init(WakeFlag::new())]
        USB_FLAG: WakeFlag,
    }

    #[init]
    fn init(mut ctx: init::Context) -> init::LateResources {
        static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None;

        init_allocator();

        #[cfg(feature = "semihosting")]
        cortex_m_logger::SemihostingLogger::init();

        #[cfg(feature = "itm")]
        cortex_m_logger::ITMLogger::init();

        #[cfg(any(feature = "itm", feature = "semihosting"))]
        log::set_max_level(log::LevelFilter::max());

        let mut flash = ctx.device.FLASH.constrain();
        let mut rcc = ctx.device.RCC.constrain();

        let clocks = rcc
            .cfgr
            .use_hse(8.mhz())
            .sysclk(72.mhz())
            .pclk1(36.mhz())
            .pclk2(72.mhz())
            .freeze(&mut flash.acr);

        let mut afio = ctx.device.AFIO.constrain(&mut rcc.apb2);

        let mut gpioa = ctx.device.GPIOA.split(&mut rcc.apb2);
        let mut gpiob = ctx.device.GPIOB.split(&mut rcc.apb2);
        let mut gpioc = ctx.device.GPIOC.split(&mut rcc.apb2);

        let mut matrix = build_scanner!(afio, gpioa, gpiob, gpioc);

        let bkp = rcc
            .bkp
            .constrain(ctx.device.BKP, &mut rcc.apb1, &mut ctx.device.PWR);

        check_bootloader_key(bkp, &mut matrix);

        let tim1 =
            Timer::tim1(ctx.device.TIM1, &clocks, &mut rcc.apb2).start_count_down(KiloHertz(2));
        let scan_reactor = timer::Reactor::new(tim1);

        let bus = UsbBusType::new(UsbPeripheral {
            usb: ctx.device.USB,
            pin_dm: gpioa.pa11.into_floating_input(&mut gpioa.crh),
            pin_dp: gpioa.pa12.into_floating_input(&mut gpioa.crh),
        });

        let reporter = keebrs::usb::report::Reporter::new();

        *USB_BUS = Some(bus);

        let kb_class = HidClass::new(
            Keyboard::new(NoLeds::new(), reporter),
            USB_BUS.as_ref().unwrap(),
        );
        let mut usb_device = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(VID, PID))
            .manufacturer("JRC")
            .product("Polymer")
            .serial_number(env!("GIT_VERSION"))
            .build();
        let _ = usb_device.force_reset();

        let left_usart = Serial::usart1(
            ctx.device.USART1,
            (
                gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl),
                gpiob.pb7,
            ),
            &mut afio.mapr,
            SerialConfig::default()
                .baudrate(USART_BAUD.bps())
                .parity_even(),
            clocks,
            &mut rcc.apb2,
        );
        let left_reactor = serial::Reactor::new(left_usart.split());

        let right_usart = Serial::usart3(
            ctx.device.USART3,
            (
                gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh),
                gpiob.pb11,
            ),
            &mut afio.mapr,
            SerialConfig::default()
                .baudrate(USART_BAUD.bps())
                .parity_even(),
            clocks,
            &mut rcc.apb1,
        );
        let right_reactor = serial::Reactor::new(right_usart.split());

        init::LateResources {
            MATRIX: matrix,
            SCAN_REACTOR: scan_reactor,
            LEFT_REACTOR: left_reactor,
            RIGHT_REACTOR: right_reactor,
            USB_DEVICE: usb_device,
            KB_CLASS: kb_class,
        }
    }

    #[idle(resources = [MATRIX, SCAN_REACTOR, LEFT_REACTOR, RIGHT_REACTOR, KB_CLASS, USB_FLAG])]
    fn idle(mut ctx: idle::Context) -> ! {
        info!("Init finished");

        let mut scan_timer = ctx.resources.SCAN_REACTOR.take();

        scan_timer.start(KiloHertz(2));

        let left = ctx.resources.LEFT_REACTOR.take();

        let right = ctx.resources.RIGHT_REACTOR.take();

        let net = ChainNet::new(left, right);

        let scanner =
            StatefulScanner::with_groups(ctx.resources.MATRIX, DEBOUNCE_CYCLES, 5, || {
                cortex_m::asm::delay(5 * 72)
            });

        let mut executor = Executor::new();
        let mut spawner = executor.local_spawner();

        let inner_usb_sink = UsbSink(ctx.resources.KB_CLASS);
        let usb_flag = &mut ctx.resources.USB_FLAG;

        let usb_sink = alloc::boxed::Box::pin(async move {
            usb_flag.wait().await;
            inner_usb_sink
        });

        let core = async move {
            let (task, net) = keebrs::net::background::<IFree, _, _, _>(net);

            spawner
                .spawn(task)
                .expect("failed to spawn background net task");

            let mut core = Core::new(
                "ortho5x7",
                Translator::new(layout::LAYOUT, 2 * 1000),
                net,
                usb_sink,
                scan_timer,
                scanner,
            );

            core.run().await;

            error!("uh oh, shouldn't get here");
        };

        executor.spawn(core);

        info!("starting executor");

        loop {
            executor.run();
        }
    }

    #[task(binds = USB_HP_CAN_TX, resources = [USB_DEVICE, KB_CLASS, USB_FLAG])]
    fn USB_HP_CAN_TX(ctx: USB_HP_CAN_TX::Context) {
        let dev = &mut *ctx.resources.USB_DEVICE;
        let class = &mut *ctx.resources.KB_CLASS;
        if dev.poll(&mut [class]) {
            ctx.resources.USB_FLAG.wake();
        }
    }

    #[task(binds = USB_LP_CAN_RX0, resources = [USB_DEVICE, KB_CLASS, USB_FLAG])]
    fn USB_LP_CAN_RX0(ctx: USB_LP_CAN_RX0::Context) {
        let dev = &mut *ctx.resources.USB_DEVICE;
        let class = &mut *ctx.resources.KB_CLASS;
        if dev.poll(&mut [class]) {
            ctx.resources.USB_FLAG.wake();
        }
    }

    #[task(binds = USART1, resources = [LEFT_REACTOR])]
    fn USART1(ctx: USART1::Context) {
        ctx.resources.LEFT_REACTOR.turn()
    }

    #[task(binds = USART3, resources = [RIGHT_REACTOR])]
    fn USART3(ctx: USART3::Context) {
        ctx.resources.RIGHT_REACTOR.turn()
    }

    #[task(binds = TIM1_UP, resources = [SCAN_REACTOR])]
    fn TIM1_UP(ctx: TIM1_UP::Context) {
        ctx.resources.SCAN_REACTOR.turn()
    }
};

fn check_bootloader_key<M: Pull + Read>(bkp: hal::backup_domain::BackupDomain, matrix: &mut M) {
    matrix.pull(0);
    cortex_m::asm::delay(5 * 72);
    let reboot = matrix.read(0);
    matrix.release(0);

    if reboot {
        reboot::reboot_bootloader(bkp);
    }
}