//! Project: CMtec CMDR joystick 24 //! Date: 2023-08-01 //! Author: Christoffer Martinsson //! Email: cm@cmtec.se //! License: Please refer to LICENSE in root directory use core::default::Default; use fugit::ExtU32; // use packed_struct::prelude::*; use usb_device::bus::UsbBus; use usb_device::class_prelude::UsbBusAllocator; use usbd_human_interface_device::usb_class::prelude::*; macro_rules! unwrap { ($arg:expr) => { match $crate::usb_joystick_device::Try::into_result($arg) { ::core::result::Result::Ok(t) => t, ::core::result::Result::Err(e) => { ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); } } }; ($arg:expr, $($msg:expr),+ $(,)? ) => { match $crate::usb_joystick_device::Try::into_result($arg) { ::core::result::Result::Ok(t) => t, ::core::result::Result::Err(e) => { ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); } } } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct NoneError; pub trait Try { type Ok; type Error; fn into_result(self) -> Result; } impl Try for Option { type Ok = T; type Error = NoneError; #[inline] fn into_result(self) -> Result { self.ok_or(NoneError) } } impl Try for Result { type Ok = T; type Error = E; #[inline] fn into_result(self) -> Self { self } } // Based on example device from https://github.com/dlkj/usbd-human-interface-device/blob/main/src/device/joystick.rs // Updated to 6x 12bit axis, 32x buttons and 4x hat switches #[rustfmt::skip] pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) // 7 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz, Slider 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) 0x09, 0x33, // Usage (Rx) 0x09, 0x34, // Usage (Ry) 0x09, 0x35, // Usage (Rz) 0x09, 0x36, // Usage (Slider) 0x16, 0x00, 0x80, // Logical Minimum (-32768) 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x75, 0x10, // Report Size (16) 0x95, 0x07, // Report Count (7) 0x81, 0x02, // Input (Data,Var,Abs) // 1 Hat Switch 0x09, 0x39, // Usage (Hat switch) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x07, // Logical Maximum (7) 0x35, 0x00, // Physical Minimum (0) 0x46, 0x3B, 0x01, // Physical Maximum (315) 0x65, 0x14, // Unit (Eng Rot: Degrees) 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x42, // Input (Data,Var,Abs,Null) // Padding for 4 bits to align to byte 0x75, 0x04, // Report Size (4) 0x95, 0x01, // Report Count (1) 0x81, 0x03, // Input (Const,Var,Abs) – padding // 32 Buttons (1-bit each) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (1) 0x29, 0x20, // Usage Maximum (32) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x20, // Report Count (32) 0x81, 0x02, // Input (Data,Var,Abs) 0xC0 // End Collection ]; #[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] pub struct JoystickReport { pub x: i16, // 16bit pub y: i16, // 16bit pub z: i16, // 16bit pub rx: i16, // 16bit pub ry: i16, // 16bit pub rz: i16, // 16bit pub slider: i16, // 16bit pub hat: u8, // 8bit pub buttons: u32, // 32bit } pub struct Joystick<'a, B: UsbBus> { interface: Interface<'a, B, InBytes32, OutNone, ReportSingle>, } impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { let mut data: [u8; 19] = [0; 19]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; data[1] = (report.x >> 8) as u8; data[2] = report.y as u8; data[3] = (report.y >> 8) as u8; data[4] = report.z as u8; data[5] = (report.z >> 8) as u8; data[6] = report.rx as u8; data[7] = (report.rx >> 8) as u8; data[8] = report.ry as u8; data[9] = (report.ry >> 8) as u8; data[10] = report.rz as u8; data[11] = (report.rz >> 8) as u8; data[12] = report.slider as u8; data[13] = (report.slider >> 8) as u8; data[14] = report.hat; data[15] = report.buttons as u8; data[16] = (report.buttons >> 8) as u8; data[17] = (report.buttons >> 16) as u8; data[18] = (report.buttons >> 24) as u8; self.interface .write_report(&data) .map(|_| ()) .map_err(UsbHidError::from) } } impl<'a, B: UsbBus> DeviceClass<'a> for Joystick<'a, B> { type I = Interface<'a, B, InBytes32, OutNone, ReportSingle>; fn interface(&mut self) -> &mut Self::I { &mut self.interface } fn reset(&mut self) {} fn tick(&mut self) -> Result<(), UsbHidError> { Ok(()) } } pub struct JoystickConfig<'a> { interface: InterfaceConfig<'a, InBytes32, OutNone, ReportSingle>, } impl Default for JoystickConfig<'_> { #[must_use] fn default() -> Self { Self::new( unwrap!( unwrap!(InterfaceBuilder::new(JOYSTICK_DESCRIPTOR)) .boot_device(InterfaceProtocol::None) .description("Joystick") .in_endpoint(10.millis()) ) .without_out_endpoint() .build(), ) } } impl<'a> JoystickConfig<'a> { #[must_use] pub fn new(interface: InterfaceConfig<'a, InBytes32, OutNone, ReportSingle>) -> Self { Self { interface } } } impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for JoystickConfig<'a> { type Allocated = Joystick<'a, B>; fn allocate(self, usb_alloc: &'a UsbBusAllocator) -> Self::Allocated { Self::Allocated { interface: Interface::new(usb_alloc, self.interface), } } }