summaryrefslogtreecommitdiffstats
path: root/usb/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'usb/src/main.rs')
-rw-r--r--usb/src/main.rs196
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);
+}