diff --git a/.cargo/config b/.cargo/config index fa95820..fb1cc56 100644 --- a/.cargo/config +++ b/.cargo/config @@ -2,6 +2,7 @@ runner = 'riscv64-unknown-elf-gdb -x openocd.gdb' rustflags = [ "-C", "link-arg=-Tmemory-c8.x", + "-C", "link-arg=-Tdevice.x", "-C", "link-arg=-Tlink.x", ] diff --git a/Cargo.toml b/Cargo.toml index 97f80d9..dc1f620 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,13 +15,35 @@ rustdoc-args = ["--cfg", "docsrs"] default-target = "x86_64-unknown-linux-gnu" [dependencies] -gd32vf103xx-hal = "0.5.0" embedded-hal = "0.2.6" nb = "1.0.0" riscv = "0.10.1" st7735-lcd = { version = "0.8.1", optional = true } embedded-sdmmc = { version = "0.3.0", optional = true } +[dependencies.gd32vf103xx-hal] +version = "0.5.0" +# Remove if/when upstream is updated +git = "https://github.com/rmsyn/gd32vf103xx-hal" +branch = "feature/usbfs" +features = ["rt"] + +[dependencies.ssmarshal] +version = "1.0" +default-features = false +optional = true + +[dependencies.usb-device] +version = "0.2.9" +optional = true + +[dependencies.usbd-hid] +version = "0.6" +# Current published crates.io package doesn't include the `KeyboardUsage` struct +# Remove when next version is published +git = "https://github.com/twitchyliquid64/usbd-hid" +optional = true + [dev-dependencies] riscv-rt = "0.11.0" panic-halt = "0.2.0" @@ -31,6 +53,7 @@ ushell = "0.3.5" [features] lcd = ["st7735-lcd"] sdcard = ["embedded-sdmmc"] +usbfs = ["ssmarshal", "usb-device", "usbd-hid"] [[example]] name = "display" @@ -44,3 +67,6 @@ required-features = ["lcd"] name = "sdcard_test" required-features = ["sdcard"] +[[example]] +name = "usbfs_device" +required-features = ["usbfs"] diff --git a/device.x b/device.x new file mode 100644 index 0000000..be3f1e4 --- /dev/null +++ b/device.x @@ -0,0 +1,65 @@ +PROVIDE(INT_SFT = DefaultHandler); +PROVIDE(INT_TMR = DefaultHandler); +PROVIDE(INT_BWEI = DefaultHandler); +PROVIDE(INT_PMOVI = DefaultHandler); +PROVIDE(WWDGT = DefaultHandler); +PROVIDE(EXTI_LVD = DefaultHandler); +PROVIDE(TAMPER = DefaultHandler); +PROVIDE(RTC = DefaultHandler); +PROVIDE(FMC = DefaultHandler); +PROVIDE(RCU = DefaultHandler); +PROVIDE(EXTI_LINE0 = DefaultHandler); +PROVIDE(EXTI_LINE1 = DefaultHandler); +PROVIDE(EXTI_LINE2 = DefaultHandler); +PROVIDE(EXTI_LINE3 = DefaultHandler); +PROVIDE(EXTI_LINE4 = DefaultHandler); +PROVIDE(DMA0_CHANNEL0 = DefaultHandler); +PROVIDE(DMA0_CHANNEL1 = DefaultHandler); +PROVIDE(DMA0_CHANNEL2 = DefaultHandler); +PROVIDE(DMA0_CHANNEL3 = DefaultHandler); +PROVIDE(DMA0_CHANNEL4 = DefaultHandler); +PROVIDE(DMA0_CHANNEL5 = DefaultHandler); +PROVIDE(DMA0_CHANNEL6 = DefaultHandler); +PROVIDE(ADC0_1 = DefaultHandler); +PROVIDE(CAN0_TX = DefaultHandler); +PROVIDE(CAN0_RX0 = DefaultHandler); +PROVIDE(CAN0_RX1 = DefaultHandler); +PROVIDE(CAN0_EWMC = DefaultHandler); +PROVIDE(EXTI_LINE9_5 = DefaultHandler); +PROVIDE(TIMER0_BRK = DefaultHandler); +PROVIDE(TIMER0_UP = DefaultHandler); +PROVIDE(TIMER0_TRG_CMT = DefaultHandler); +PROVIDE(TIMER0_CHANNEL = DefaultHandler); +PROVIDE(TIMER1 = DefaultHandler); +PROVIDE(TIMER2 = DefaultHandler); +PROVIDE(TIMER3 = DefaultHandler); +PROVIDE(I2C0_EV = DefaultHandler); +PROVIDE(I2C0_ER = DefaultHandler); +PROVIDE(I2C1_EV = DefaultHandler); +PROVIDE(I2C1_ER = DefaultHandler); +PROVIDE(SPI0 = DefaultHandler); +PROVIDE(SPI1 = DefaultHandler); +PROVIDE(USART0 = DefaultHandler); +PROVIDE(USART1 = DefaultHandler); +PROVIDE(USART2 = DefaultHandler); +PROVIDE(EXTI_LINE15_10 = DefaultHandler); +PROVIDE(RTC_ALARM = DefaultHandler); +PROVIDE(USBFS_WKUP = DefaultHandler); +PROVIDE(EXMC = DefaultHandler); +PROVIDE(TIMER4 = DefaultHandler); +PROVIDE(SPI2 = DefaultHandler); +PROVIDE(UART3 = DefaultHandler); +PROVIDE(UART4 = DefaultHandler); +PROVIDE(TIMER5 = DefaultHandler); +PROVIDE(TIMER6 = DefaultHandler); +PROVIDE(DMA1_CHANNEL0 = DefaultHandler); +PROVIDE(DMA1_CHANNEL1 = DefaultHandler); +PROVIDE(DMA1_CHANNEL2 = DefaultHandler); +PROVIDE(DMA1_CHANNEL3 = DefaultHandler); +PROVIDE(DMA1_CHANNEL4 = DefaultHandler); +PROVIDE(CAN1_TX = DefaultHandler); +PROVIDE(CAN1_RX0 = DefaultHandler); +PROVIDE(CAN1_RX1 = DefaultHandler); +PROVIDE(CAN1_EWMC = DefaultHandler); +PROVIDE(USBFS = DefaultHandler); + diff --git a/examples/usbfs_device.rs b/examples/usbfs_device.rs new file mode 100644 index 0000000..d435226 --- /dev/null +++ b/examples/usbfs_device.rs @@ -0,0 +1,144 @@ +#![no_std] +#![no_main] + +use panic_halt as _; + +use core::sync::atomic::{AtomicUsize, Ordering}; + +use gd32vf103xx_hal::{pac::Interrupt, timer::{self, Timer}, usbfs::device::UsbBus}; +use longan_nano::hal::{eclic::*, pac::{self, *}, prelude::*}; + +use riscv_rt::entry; + +use usb_device::{ + self, + class_prelude::UsbBusAllocator, + device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}, +}; +use usbd_hid::descriptor::{KeyboardReport, KeyboardUsage, SerializedDescriptor}; +use usbd_hid::hid_class::HIDClass; + +const HELLO: [(u8, u8); 5] = [ + ( + KeyboardUsage::KeyboardLeftShift as u8, + KeyboardUsage::KeyboardHh as u8, + ), + (0x00, KeyboardUsage::KeyboardEe as u8), + (0x00, KeyboardUsage::KeyboardLl as u8), + (0x00, KeyboardUsage::KeyboardLl as u8), + (0x00, KeyboardUsage::KeyboardOo as u8), +]; + +static INDEX: AtomicUsize = AtomicUsize::new(0); + +fn index() -> usize { + INDEX.load(Ordering::Relaxed) +} + +fn increment_index() -> usize { + let index = (INDEX.load(Ordering::Relaxed) + 1) % HELLO.len(); + INDEX.store(index, Ordering::SeqCst); + index +} + +#[entry] +fn main() -> ! { + let dp = pac::Peripherals::take().unwrap(); + + let mut rcu = dp + .RCU + .configure() + .ext_hf_clock(8.mhz()) + .sysclk(108.mhz()) + .freeze(); + + let mut timer = Timer::timer2(dp.TIMER2, 48.mhz(), &mut rcu); + timer.listen(timer::Event::Update); + + let usb_dev = UsbBus::new( + dp.USBFS_GLOBAL, + dp.USBFS_DEVICE, + dp.USBFS_PWRCLK, + timer, + ); + + unsafe { + let usb_bus = &*USB_BUS.insert(UsbBusAllocator::new(usb_dev)); + HID_CLASS = Some(HIDClass::new(usb_bus, KeyboardReport::desc(), 1)); + USB_DEV = Some( + UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x1209, 0x0001)) + .manufacturer("Longboi") + .product("Keeb") + .build(), + ); + } + + ECLIC::reset(); + ECLIC::set_threshold_level(Level::L0); + ECLIC::set_level_priority_bits(LevelPriorityBits::L3P1); + + // setup the USBFS interrupt + ECLIC::setup( + Interrupt::USBFS, + TriggerType::Level, + Level::L1, + Priority::P1, + ); + + // setup the USBFS_WKUP interrupt + ECLIC::setup( + Interrupt::USBFS_WKUP, + TriggerType::Level, + Level::L1, + Priority::P2, + ); + + unsafe { + // unmask and enable interrupts + ECLIC::unmask(Interrupt::USBFS); + ECLIC::unmask(Interrupt::USBFS_WKUP); + + riscv::interrupt::enable(); + }; + + loop {} +} + +static mut HID_CLASS: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_DEV: Option> = None; + +fn send_report() { + let idx = index(); + let key = HELLO[idx]; + + let report = KeyboardReport { + modifier: key.0, + reserved: 0, + leds: 0, + keycodes: [key.1, 0x00, 0x00, 0x00, 0x00, 0x00], + }; + + unsafe { + let hid_class = HID_CLASS.as_mut().unwrap(); + if hid_class.push_input(&report).is_ok() { + increment_index(); + } + + if let Some(usb) = USB_DEV.as_mut() { + usb.poll(&mut [hid_class]); + } + } +} + +#[allow(non_snake_case)] +#[no_mangle] +fn USBFS() { + send_report(); +} + +#[allow(non_snake_case)] +#[no_mangle] +fn USBFS_WKUP() { + send_report(); +}