diff options
Diffstat (limited to 'usb/src/main.rs')
-rw-r--r-- | usb/src/main.rs | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/usb/src/main.rs b/usb/src/main.rs new file mode 100644 index 0000000..36e4d1e --- /dev/null +++ b/usb/src/main.rs @@ -0,0 +1,196 @@ +//! Take USB keyboard reports and echo them over I²C. + +#![no_std] +#![no_main] + +mod dotstar; +mod logger; +mod macros; +mod rtc; + +use atsamd_usb_host::SAMDHost; +use bootkbd::BootKeyboard; +use clint::HandlerArray; +use core::mem; +use core::panic::PanicInfo; +use cortex_m::asm::wfi; +use cortex_m_rt::{entry, exception, ExceptionFrame}; +use embedded_hal::{blocking::i2c::Write, digital::v2::OutputPin}; +use log::{info, LevelFilter}; +use smart_leds::colors; +use smart_leds_trait::SmartLedsWrite; +use trinket_m0::{ + self as hal, + clock::GenericClockController, + gpio::{OpenDrain, Output, Pa10, Pa6, Pa7, PfD}, + sercom, + target_device::{interrupt, Interrupt}, + time::*, + CorePeripherals, Peripherals, +}; +use usb_host::Driver; + +// A very unsafe copy of an LED to turn on when things go really, really wrong. +static mut LED: usize = 0; + +// I²C address to send keyboard reports to. +const NRF_WIREADDR: u8 = 4; + +// Interrupt handler table. +static HANDLERS: HandlerArray = HandlerArray::new(); + +#[entry] +fn main() -> ! { + let mut cp = CorePeripherals::take().expect("taking core peripherals"); + let mut dp = Peripherals::take().expect("taking device peripherals"); + + let mut clocks = GenericClockController::with_internal_32kosc( + dp.GCLK, + &mut dp.PM, + &mut dp.SYSCTRL, + &mut dp.NVMCTRL, + ); + + let mut pins = hal::Pins::new(dp.PORT); + + let uart = hal::uart( + &mut clocks, + 115_200.hz(), + dp.SERCOM0, + &mut cp.NVIC, + &mut dp.PM, + pins.d3, + pins.d4, + &mut pins.port, + ); + + let mut i2c_master = hal::i2c_master( + &mut clocks, + 400_000.hz(), + dp.SERCOM2, + &mut dp.PM, + pins.d0, + pins.d2, + &mut pins.port, + ); + + let mut red_led = pins.d13.into_open_drain_output(&mut pins.port); + red_led.set_low().expect("turning off red LED"); + unsafe { LED = mem::transmute(&red_led) } + + let mut dotstar = dotstar::new( + dp.SERCOM1, + pins.swdio, + pins.dotstar_di, + pins.dotstar_ci, + &mut pins.port, + &mut dp.PM, + &mut clocks, + ); + let black = [colors::BLACK]; + dotstar + .write(black.iter().cloned()) + .expect("turning off dotstar"); + + // We do the transmute because, while all the underlying data is + // static, we're unable to get a referecence to the UART or LED + // until run-time. Another option would be to use Option in the + // SerialLogger definition, but that requires a check every time + // they might be used. + let uart_wrapped = logger::WriteWrapper::new(uart); + let logger = logger::SerialLogger::new(uart_wrapped, red_led); + + // Wow, would I love to not be annotating this type. + let logger_ref: &'static logger::SerialLogger< + sercom::UART0<sercom::Sercom0Pad3<Pa7<PfD>>, sercom::Sercom0Pad2<Pa6<PfD>>, (), ()>, + Pa10<Output<OpenDrain>>, + > = unsafe { mem::transmute(&logger) }; + unsafe { log::set_logger_racy(logger_ref).expect("couldn't set logger") }; + log::set_max_level(LevelFilter::Trace); + + let mut rtc_handler = rtc::setup(dp.RTC, &mut clocks); + + let (mut usb_host, mut usb_handler) = SAMDHost::new( + dp.USB, + pins.usb_sof, + pins.usb_dm, + pins.usb_dp, + Some(pins.usb_host_enable), + &mut pins.port, + &mut clocks, + &mut dp.PM, + &rtc::millis, + ); + + let mut bootkbd = BootKeyboard::new(|addr, buf| { + info!("{}: {:?}", addr, buf); + let hdr: [u8; 2] = [I2CMessageType::Keyboard as u8, buf.len() as u8]; + i2c_master.write(NRF_WIREADDR, &hdr).ok(); + i2c_master.write(NRF_WIREADDR, &buf).ok(); + }); + let mut drivers: [&mut dyn Driver; 1] = [&mut bootkbd]; + + HANDLERS.with_overrides(|hs| { + hs.register(0, &mut rtc_handler); + unsafe { cp.NVIC.set_priority(Interrupt::USB, 0) }; + cp.NVIC.enable(Interrupt::RTC); + + hs.register(1, &mut usb_handler); + unsafe { cp.NVIC.set_priority(Interrupt::USB, 1) }; + cp.NVIC.enable(Interrupt::USB); + + info!("Bootstrap complete."); + + loop { + usb_host.task(&mut drivers[..]); + wfi() + } + }); + unreachable!(); +} + +#[allow(unused)] +#[repr(u8)] +enum I2CMessageType { + Debug = 0x00, + Keyboard = 0x01, + Invalid = 0xff, +} + +#[panic_handler] +fn panic_handler(pi: &PanicInfo) -> ! { + let red_led: &mut Pa10<Output<OpenDrain>> = unsafe { mem::transmute(LED) }; + red_led.set_high().ok(); + + logln_now!("~~~ PANIC ~~~"); + logln_now!("{}", pi); + logln_now!("flushing log"); + loop { + log::logger().flush(); + wfi() + } +} + +#[exception] +fn HardFault(ef: &ExceptionFrame) -> ! { + let red_led: &mut Pa10<Output<OpenDrain>> = unsafe { mem::transmute(LED) }; + red_led.set_high().ok(); + + log::logger().flush(); + logln_now!("!!! Hard Fault - ef: {:?} !!!", ef); + logln_now!("flushing log"); + loop { + log::logger().flush(); + wfi() + } +} + +#[interrupt] +fn RTC() { + HANDLERS.call(0); +} + +#[interrupt] +fn USB() { + HANDLERS.call(1); +} |