/src/peripherals/usbdev.rs
use core::cell::RefCell;
use crate::bsp;
use crate::console::with_console;
use bsp::hal::clocks::UsbClock;
use bsp::hal::usb::UsbBus;
use bsp::pac::{interrupt, RESETS, USBCTRL_DPRAM, USBCTRL_REGS};
use critical_section::Mutex;
use serde::{ser::SerializeTuple, Serialize, Serializer};
use usb_device::{class_prelude::*, prelude::*};
use usbd_hid::descriptor::{gen_hid_descriptor, AsInputReport, SerializedDescriptor};
use usbd_hid::hid_class::{
HIDClass, HidClassSettings, HidCountryCode, HidProtocol, HidSubClass, ProtocolModeConfig,
};
use usbd_serial::SerialPort;
#[gen_hid_descriptor(
(collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = KEYBOARD) = {
(usage_page = KEYBOARD, usage_min = 0xE0, usage_max = 0xE7) = {
#[packed_bits 8]
#[item_settings data, variable, absolute]
modifier = input;
};
#[item_settings constant, array, absolute]
reserved = input;
(usage_page = LEDS, usage_min = 0x01, usage_max = 0x03) = {
#[packed_bits 3]
#[item_settings data, variable, absolute]
leds = output;
};
(usage_page = KEYBOARD, usage_min = 0x00, usage_max = 0xFF) = {
#[item_settings data, array, absolute]
keycodes = input;
};
}
)]
struct KeyboardReport {
pub modifier: u8,
pub reserved: u8,
pub leds: u8,
pub keycodes: [u8; 6],
}
static mut USB_ALLOCATOR: Option<UsbBusAllocator<UsbBus>> = None;
static USB_MANAGER: Mutex<RefCell<Option<UsbManager>>> = Mutex::new(RefCell::new(None));
pub struct UsbManager<'a> {
dev: UsbDevice<'a, UsbBus>,
keyboard: HIDClass<'a, UsbBus>,
serial: SerialPort<'a, UsbBus>,
}
impl<'a> UsbManager<'a> {
pub fn update(&mut self) {
if self.dev.poll(&mut [&mut self.keyboard, &mut self.serial]) {
let mut buf = [0u8; 64];
match self.serial.read(&mut buf) {
Ok(n) => {
// n bytes read
with_console(|c| c.fill_read_buffer(&buf[0..n]));
}
Err(UsbError::WouldBlock) => (),
Err(err) => panic!("Failed to read serial: {:?}", err),
}
match self.serial.flush() {
Ok(_) => {
with_console(|c| c.write_ready());
}
Err(UsbError::WouldBlock) => (),
Err(err) => panic!("Failed to flush serial: {:?}", err),
}
}
}
pub fn keyboard(&mut self) -> &mut HIDClass<'a, UsbBus> {
&mut self.keyboard
}
pub fn serial(&mut self) -> &mut SerialPort<'a, UsbBus> {
&mut self.serial
}
}
pub fn init_usb(
ctrl_reg: USBCTRL_REGS,
ctrl_dpram: USBCTRL_DPRAM,
pll: UsbClock,
resets: &mut RESETS,
) {
let bus = UsbBus::new(ctrl_reg, ctrl_dpram, pll, true, resets);
// The Mutex<RefCell<...>> pattern doesn't work here because
// the devices need to keep a reference to the allocator. So
// we do something a little unsafe.
let usballoc = unsafe {
USB_ALLOCATOR = Some(UsbBusAllocator::new(bus));
USB_ALLOCATOR.as_ref().unwrap()
};
let keyboard = HIDClass::new_ep_in_with_settings(
usballoc,
KeyboardReport::desc(),
1,
HidClassSettings {
subclass: HidSubClass::NoSubClass,
protocol: HidProtocol::Keyboard,
config: ProtocolModeConfig::DefaultBehavior,
locale: HidCountryCode::NotSupported,
},
);
let serial = SerialPort::new(usballoc);
let dev = UsbDeviceBuilder::new(usballoc, UsbVidPid(0x1209, 0x0001))
.manufacturer("The Dominion of Awesome")
.product("Smorgasboard")
.serial_number("LOLBUTTS")
.composite_with_iads()
.build();
critical_section::with(|cs| {
let mut usb_manager = USB_MANAGER.borrow_ref_mut(cs);
*usb_manager = Some(UsbManager {
dev,
keyboard,
serial,
})
});
}
pub fn with_usb<F, R>(mut f: F) -> R
where
F: FnMut(&mut UsbManager) -> R,
{
critical_section::with(|cs| {
let mut usb_manager = USB_MANAGER.borrow_ref_mut(cs);
if let Some(ref mut usb_manager) = *usb_manager {
return f(usb_manager);
} else {
panic!("USB Manager not initialized");
}
})
}
#[interrupt]
fn USBCTRL_IRQ() {
with_usb(|u| u.update());
}