Async USB HID class
This commit is contained in:
		
							parent
							
								
									8fe3b44d82
								
							
						
					
					
						commit
						5ee7a85b33
					
				
							
								
								
									
										18
									
								
								embassy-usb-hid/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								embassy-usb-hid/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "embassy-usb-hid"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
default = ["usbd-hid"]
 | 
			
		||||
usbd-hid = ["dep:usbd-hid", "ssmarshal"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
embassy = { version = "0.1.0", path = "../embassy" }
 | 
			
		||||
embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
 | 
			
		||||
 | 
			
		||||
defmt = { version = "0.3", optional = true }
 | 
			
		||||
log = { version = "0.4.14", optional = true }
 | 
			
		||||
usbd-hid = { version = "0.5.2", optional = true }
 | 
			
		||||
ssmarshal = { version = "1.0", default-features = false, optional = true }
 | 
			
		||||
futures-util = { version = "0.3.21", default-features = false }
 | 
			
		||||
							
								
								
									
										225
									
								
								embassy-usb-hid/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								embassy-usb-hid/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,225 @@
 | 
			
		||||
#![macro_use]
 | 
			
		||||
#![allow(unused_macros)]
 | 
			
		||||
 | 
			
		||||
#[cfg(all(feature = "defmt", feature = "log"))]
 | 
			
		||||
compile_error!("You may not enable both `defmt` and `log` features.");
 | 
			
		||||
 | 
			
		||||
macro_rules! assert {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::assert!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::assert!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! assert_eq {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::assert_eq!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::assert_eq!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! assert_ne {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::assert_ne!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::assert_ne!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug_assert {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::debug_assert!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug_assert!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug_assert_eq {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::debug_assert_eq!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug_assert_eq!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug_assert_ne {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::debug_assert_ne!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug_assert_ne!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! todo {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::todo!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::todo!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! unreachable {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::unreachable!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::unreachable!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! panic {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(not(feature = "defmt"))]
 | 
			
		||||
            ::core::panic!($($x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::panic!($($x)*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! trace {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::trace!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::trace!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! debug {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::debug!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::debug!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! info {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::info!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::info!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! warn {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::warn!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::warn!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! error {
 | 
			
		||||
    ($s:literal $(, $x:expr)* $(,)?) => {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "log")]
 | 
			
		||||
            ::log::error!($s $(, $x)*);
 | 
			
		||||
            #[cfg(feature = "defmt")]
 | 
			
		||||
            ::defmt::error!($s $(, $x)*);
 | 
			
		||||
            #[cfg(not(any(feature = "log", feature="defmt")))]
 | 
			
		||||
            let _ = ($( & $x ),*);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "defmt")]
 | 
			
		||||
macro_rules! unwrap {
 | 
			
		||||
    ($($x:tt)*) => {
 | 
			
		||||
        ::defmt::unwrap!($($x)*)
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(feature = "defmt"))]
 | 
			
		||||
macro_rules! unwrap {
 | 
			
		||||
    ($arg:expr) => {
 | 
			
		||||
        match $crate::fmt::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::fmt::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<Self::Ok, Self::Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Try for Option<T> {
 | 
			
		||||
    type Ok = T;
 | 
			
		||||
    type Error = NoneError;
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn into_result(self) -> Result<T, NoneError> {
 | 
			
		||||
        self.ok_or(NoneError)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T, E> Try for Result<T, E> {
 | 
			
		||||
    type Ok = T;
 | 
			
		||||
    type Error = E;
 | 
			
		||||
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn into_result(self) -> Self {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										529
									
								
								embassy-usb-hid/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										529
									
								
								embassy-usb-hid/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,529 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![feature(generic_associated_types)]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
//! Implements HID functionality for a usb-device device.
 | 
			
		||||
 | 
			
		||||
// This mod MUST go first, so that the others see its macros.
 | 
			
		||||
pub(crate) mod fmt;
 | 
			
		||||
 | 
			
		||||
use core::mem::MaybeUninit;
 | 
			
		||||
 | 
			
		||||
use embassy::channel::signal::Signal;
 | 
			
		||||
use embassy::time::Duration;
 | 
			
		||||
use embassy_usb::driver::{EndpointOut, ReadError};
 | 
			
		||||
use embassy_usb::{
 | 
			
		||||
    control::{ControlHandler, InResponse, OutResponse, Request, RequestType},
 | 
			
		||||
    driver::{Driver, Endpoint, EndpointIn, WriteError},
 | 
			
		||||
    UsbDeviceBuilder,
 | 
			
		||||
};
 | 
			
		||||
use futures_util::future::{select, Either};
 | 
			
		||||
use futures_util::pin_mut;
 | 
			
		||||
#[cfg(feature = "usbd-hid")]
 | 
			
		||||
use ssmarshal::serialize;
 | 
			
		||||
#[cfg(feature = "usbd-hid")]
 | 
			
		||||
use usbd_hid::descriptor::AsInputReport;
 | 
			
		||||
 | 
			
		||||
const USB_CLASS_HID: u8 = 0x03;
 | 
			
		||||
const USB_SUBCLASS_NONE: u8 = 0x00;
 | 
			
		||||
const USB_PROTOCOL_NONE: u8 = 0x00;
 | 
			
		||||
 | 
			
		||||
// HID
 | 
			
		||||
const HID_DESC_DESCTYPE_HID: u8 = 0x21;
 | 
			
		||||
const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
 | 
			
		||||
const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01];
 | 
			
		||||
const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00;
 | 
			
		||||
 | 
			
		||||
const HID_REQ_SET_IDLE: u8 = 0x0a;
 | 
			
		||||
const HID_REQ_GET_IDLE: u8 = 0x02;
 | 
			
		||||
const HID_REQ_GET_REPORT: u8 = 0x01;
 | 
			
		||||
const HID_REQ_SET_REPORT: u8 = 0x09;
 | 
			
		||||
const HID_REQ_GET_PROTOCOL: u8 = 0x03;
 | 
			
		||||
const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 | 
			
		||||
pub enum ReportId {
 | 
			
		||||
    In(u8),
 | 
			
		||||
    Out(u8),
 | 
			
		||||
    Feature(u8),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ReportId {
 | 
			
		||||
    fn try_from(value: u16) -> Result<Self, ()> {
 | 
			
		||||
        match value >> 8 {
 | 
			
		||||
            1 => Ok(ReportId::In(value as u8)),
 | 
			
		||||
            2 => Ok(ReportId::Out(value as u8)),
 | 
			
		||||
            3 => Ok(ReportId::Feature(value as u8)),
 | 
			
		||||
            _ => Err(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct State<'a, const IN_N: usize, const OUT_N: usize, const FEATURE_N: usize> {
 | 
			
		||||
    control: MaybeUninit<Control<'a, OUT_N, FEATURE_N>>,
 | 
			
		||||
    out_signal: Signal<(usize, [u8; OUT_N])>,
 | 
			
		||||
    feature_signal: Signal<(usize, [u8; FEATURE_N])>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, const IN_N: usize, const OUT_N: usize, const FEATURE_N: usize>
 | 
			
		||||
    State<'a, IN_N, OUT_N, FEATURE_N>
 | 
			
		||||
{
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        State {
 | 
			
		||||
            control: MaybeUninit::uninit(),
 | 
			
		||||
            out_signal: Signal::new(),
 | 
			
		||||
            feature_signal: Signal::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct HidClass<
 | 
			
		||||
    'd,
 | 
			
		||||
    D: Driver<'d>,
 | 
			
		||||
    const IN_N: usize,
 | 
			
		||||
    const OUT_N: usize,
 | 
			
		||||
    const FEATURE_N: usize,
 | 
			
		||||
> {
 | 
			
		||||
    input: ReportWriter<'d, D, IN_N>,
 | 
			
		||||
    output: ReportReader<'d, D, OUT_N>,
 | 
			
		||||
    feature: ReportReader<'d, D, FEATURE_N>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: usize>
 | 
			
		||||
    HidClass<'d, D, IN_N, OUT_N, FEATURE_N>
 | 
			
		||||
{
 | 
			
		||||
    /// Creates a new HidClass.
 | 
			
		||||
    ///
 | 
			
		||||
    /// poll_ms configures how frequently the host should poll for reading/writing
 | 
			
		||||
    /// HID reports. A lower value means better throughput & latency, at the expense
 | 
			
		||||
    /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
 | 
			
		||||
    /// high performance uses, and a value of 255 is good for best-effort usecases.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This allocates two endpoints (IN and OUT).
 | 
			
		||||
    /// See new_ep_in (IN endpoint only) and new_ep_out (OUT endpoint only) to only create a single
 | 
			
		||||
    /// endpoint.
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        builder: &mut UsbDeviceBuilder<'d, D>,
 | 
			
		||||
        state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>,
 | 
			
		||||
        report_descriptor: &'static [u8],
 | 
			
		||||
        request_handler: Option<&'d dyn RequestHandler>,
 | 
			
		||||
        poll_ms: u8,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let ep_out = Some(builder.alloc_interrupt_endpoint_out(64, poll_ms));
 | 
			
		||||
        let ep_in = Some(builder.alloc_interrupt_endpoint_in(64, poll_ms));
 | 
			
		||||
        Self::new_inner(
 | 
			
		||||
            builder,
 | 
			
		||||
            state,
 | 
			
		||||
            report_descriptor,
 | 
			
		||||
            request_handler,
 | 
			
		||||
            ep_out,
 | 
			
		||||
            ep_in,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a new HidClass with the provided UsbBus & HID report descriptor.
 | 
			
		||||
    /// See new() for more details.
 | 
			
		||||
    pub fn new_ep_in(
 | 
			
		||||
        builder: &mut UsbDeviceBuilder<'d, D>,
 | 
			
		||||
        state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>,
 | 
			
		||||
        report_descriptor: &'static [u8],
 | 
			
		||||
        request_handler: Option<&'d dyn RequestHandler>,
 | 
			
		||||
        poll_ms: u8,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let ep_out = None;
 | 
			
		||||
        let ep_in = Some(builder.alloc_interrupt_endpoint_in(64, poll_ms));
 | 
			
		||||
        Self::new_inner(
 | 
			
		||||
            builder,
 | 
			
		||||
            state,
 | 
			
		||||
            report_descriptor,
 | 
			
		||||
            request_handler,
 | 
			
		||||
            ep_out,
 | 
			
		||||
            ep_in,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a new HidClass with the provided UsbBus & HID report descriptor.
 | 
			
		||||
    /// See new() for more details.
 | 
			
		||||
    pub fn new_ep_out(
 | 
			
		||||
        builder: &mut UsbDeviceBuilder<'d, D>,
 | 
			
		||||
        state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>,
 | 
			
		||||
        report_descriptor: &'static [u8],
 | 
			
		||||
        request_handler: Option<&'d dyn RequestHandler>,
 | 
			
		||||
        poll_ms: u8,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let ep_out = Some(builder.alloc_interrupt_endpoint_out(64, poll_ms));
 | 
			
		||||
        let ep_in = None;
 | 
			
		||||
        Self::new_inner(
 | 
			
		||||
            builder,
 | 
			
		||||
            state,
 | 
			
		||||
            report_descriptor,
 | 
			
		||||
            request_handler,
 | 
			
		||||
            ep_out,
 | 
			
		||||
            ep_in,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn new_inner(
 | 
			
		||||
        builder: &mut UsbDeviceBuilder<'d, D>,
 | 
			
		||||
        state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>,
 | 
			
		||||
        report_descriptor: &'static [u8],
 | 
			
		||||
        request_handler: Option<&'d dyn RequestHandler>,
 | 
			
		||||
        ep_out: Option<D::EndpointOut>,
 | 
			
		||||
        ep_in: Option<D::EndpointIn>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let control = state.control.write(Control::new(
 | 
			
		||||
            report_descriptor,
 | 
			
		||||
            &state.out_signal,
 | 
			
		||||
            &state.feature_signal,
 | 
			
		||||
            request_handler,
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        control.build(builder, ep_out.as_ref(), ep_in.as_ref());
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            input: ReportWriter { ep_in },
 | 
			
		||||
            output: ReportReader {
 | 
			
		||||
                ep_out,
 | 
			
		||||
                receiver: &state.out_signal,
 | 
			
		||||
            },
 | 
			
		||||
            feature: ReportReader {
 | 
			
		||||
                ep_out: None,
 | 
			
		||||
                receiver: &state.feature_signal,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets the [`ReportWriter`] for input reports.
 | 
			
		||||
    ///
 | 
			
		||||
    /// **Note:** If the `HidClass` was created with [`new_ep_out()`](Self::new_ep_out)
 | 
			
		||||
    /// this writer will be useless as no endpoint is availabe to send reports.
 | 
			
		||||
    pub fn input(&mut self) -> &mut ReportWriter<'d, D, IN_N> {
 | 
			
		||||
        &mut self.input
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets the [`ReportReader`] for output reports.
 | 
			
		||||
    pub fn output(&mut self) -> &mut ReportReader<'d, D, OUT_N> {
 | 
			
		||||
        &mut self.output
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets the [`ReportReader`] for feature reports.
 | 
			
		||||
    pub fn feature(&mut self) -> &mut ReportReader<'d, D, FEATURE_N> {
 | 
			
		||||
        &mut self.feature
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Splits this `HidClass` into seperate readers/writers for each report type.
 | 
			
		||||
    pub fn split(
 | 
			
		||||
        self,
 | 
			
		||||
    ) -> (
 | 
			
		||||
        ReportWriter<'d, D, IN_N>,
 | 
			
		||||
        ReportReader<'d, D, OUT_N>,
 | 
			
		||||
        ReportReader<'d, D, FEATURE_N>,
 | 
			
		||||
    ) {
 | 
			
		||||
        (self.input, self.output, self.feature)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ReportWriter<'d, D: Driver<'d>, const N: usize> {
 | 
			
		||||
    ep_in: Option<D::EndpointIn>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ReportReader<'d, D: Driver<'d>, const N: usize> {
 | 
			
		||||
    ep_out: Option<D::EndpointOut>,
 | 
			
		||||
    receiver: &'d Signal<(usize, [u8; N])>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, D: Driver<'d>, const N: usize> ReportWriter<'d, D, N> {
 | 
			
		||||
    /// Tries to write an input report by serializing the given report structure.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Panics if no endpoint is available.
 | 
			
		||||
    #[cfg(feature = "usbd-hid")]
 | 
			
		||||
    pub async fn serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), WriteError> {
 | 
			
		||||
        let mut buf: [u8; N] = [0; N];
 | 
			
		||||
        let size = match serialize(&mut buf, r) {
 | 
			
		||||
            Ok(size) => size,
 | 
			
		||||
            Err(_) => return Err(WriteError::BufferOverflow),
 | 
			
		||||
        };
 | 
			
		||||
        self.write(&buf[0..size]).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Writes `report` to its interrupt endpoint.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Panics if no endpoint is available.
 | 
			
		||||
    pub async fn write(&mut self, report: &[u8]) -> Result<(), WriteError> {
 | 
			
		||||
        assert!(report.len() <= N);
 | 
			
		||||
 | 
			
		||||
        let ep = self
 | 
			
		||||
            .ep_in
 | 
			
		||||
            .as_mut()
 | 
			
		||||
            .expect("An IN endpoint must be allocated to write input reports.");
 | 
			
		||||
 | 
			
		||||
        let max_packet_size = usize::from(ep.info().max_packet_size);
 | 
			
		||||
        let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0);
 | 
			
		||||
        for chunk in report.chunks(max_packet_size) {
 | 
			
		||||
            ep.write(chunk).await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if zlp_needed {
 | 
			
		||||
            ep.write(&[]).await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, D: Driver<'d>, const N: usize> ReportReader<'d, D, N> {
 | 
			
		||||
    pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
 | 
			
		||||
        assert!(buf.len() >= N);
 | 
			
		||||
        if let Some(ep) = &mut self.ep_out {
 | 
			
		||||
            let max_packet_size = usize::from(ep.info().max_packet_size);
 | 
			
		||||
 | 
			
		||||
            let mut chunks = buf.chunks_mut(max_packet_size);
 | 
			
		||||
 | 
			
		||||
            // Wait until we've received a chunk from the endpoint or a report from a SET_REPORT control request
 | 
			
		||||
            let (mut total, data) = {
 | 
			
		||||
                let chunk = unwrap!(chunks.next());
 | 
			
		||||
                let fut1 = ep.read(chunk);
 | 
			
		||||
                pin_mut!(fut1);
 | 
			
		||||
                match select(fut1, self.receiver.wait()).await {
 | 
			
		||||
                    Either::Left((Ok(size), _)) => (size, None),
 | 
			
		||||
                    Either::Left((Err(err), _)) => return Err(err),
 | 
			
		||||
                    Either::Right(((size, data), _)) => (size, Some(data)),
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if let Some(data) = data {
 | 
			
		||||
                buf[0..total].copy_from_slice(&data[0..total]);
 | 
			
		||||
                Ok(total)
 | 
			
		||||
            } else {
 | 
			
		||||
                for chunk in chunks {
 | 
			
		||||
                    let size = ep.read(chunk).await?;
 | 
			
		||||
                    total += size;
 | 
			
		||||
                    if size < max_packet_size || total == N {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                Ok(total)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            let (total, data) = self.receiver.wait().await;
 | 
			
		||||
            buf[0..total].copy_from_slice(&data[0..total]);
 | 
			
		||||
            Ok(total)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait RequestHandler {
 | 
			
		||||
    /// Read the value of report `id` into `buf` returning the size.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Returns `None` if `id` is invalid or no data is available.
 | 
			
		||||
    fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> {
 | 
			
		||||
        let _ = (id, buf);
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set the idle rate for `id` to `dur`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If `id` is `None`, set the idle rate of all input reports to `dur`. If
 | 
			
		||||
    /// an indefinite duration is requested, `dur` will be set to `Duration::MAX`.
 | 
			
		||||
    fn set_idle(&self, id: Option<ReportId>, dur: Duration) {
 | 
			
		||||
        let _ = (id, dur);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the idle rate for `id`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If `id` is `None`, get the idle rate for all reports. Returning `None`
 | 
			
		||||
    /// will reject the control request. Any duration above 1.020 seconds or 0
 | 
			
		||||
    /// will be returned as an indefinite idle rate.
 | 
			
		||||
    fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
 | 
			
		||||
        let _ = id;
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Control<'d, const OUT_N: usize, const FEATURE_N: usize> {
 | 
			
		||||
    report_descriptor: &'static [u8],
 | 
			
		||||
    out_signal: &'d Signal<(usize, [u8; OUT_N])>,
 | 
			
		||||
    feature_signal: &'d Signal<(usize, [u8; FEATURE_N])>,
 | 
			
		||||
    request_handler: Option<&'d dyn RequestHandler>,
 | 
			
		||||
    hid_descriptor: [u8; 9],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, const OUT_N: usize, const FEATURE_N: usize> Control<'a, OUT_N, FEATURE_N> {
 | 
			
		||||
    fn new(
 | 
			
		||||
        report_descriptor: &'static [u8],
 | 
			
		||||
        out_signal: &'a Signal<(usize, [u8; OUT_N])>,
 | 
			
		||||
        feature_signal: &'a Signal<(usize, [u8; FEATURE_N])>,
 | 
			
		||||
        request_handler: Option<&'a dyn RequestHandler>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Control {
 | 
			
		||||
            report_descriptor,
 | 
			
		||||
            out_signal,
 | 
			
		||||
            feature_signal,
 | 
			
		||||
            request_handler,
 | 
			
		||||
            hid_descriptor: [
 | 
			
		||||
                // Length of buf inclusive of size prefix
 | 
			
		||||
                9,
 | 
			
		||||
                // Descriptor type
 | 
			
		||||
                HID_DESC_DESCTYPE_HID,
 | 
			
		||||
                // HID Class spec version
 | 
			
		||||
                HID_DESC_SPEC_1_10[0],
 | 
			
		||||
                HID_DESC_SPEC_1_10[1],
 | 
			
		||||
                // Country code not supported
 | 
			
		||||
                HID_DESC_COUNTRY_UNSPEC,
 | 
			
		||||
                // Number of following descriptors
 | 
			
		||||
                1,
 | 
			
		||||
                // We have a HID report descriptor the host should read
 | 
			
		||||
                HID_DESC_DESCTYPE_HID_REPORT,
 | 
			
		||||
                // HID report descriptor size,
 | 
			
		||||
                (report_descriptor.len() & 0xFF) as u8,
 | 
			
		||||
                (report_descriptor.len() >> 8 & 0xFF) as u8,
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn build<'d, D: Driver<'d>>(
 | 
			
		||||
        &'d mut self,
 | 
			
		||||
        builder: &mut UsbDeviceBuilder<'d, D>,
 | 
			
		||||
        ep_out: Option<&D::EndpointOut>,
 | 
			
		||||
        ep_in: Option<&D::EndpointIn>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let len = self.report_descriptor.len();
 | 
			
		||||
        let if_num = builder.alloc_interface_with_handler(self);
 | 
			
		||||
 | 
			
		||||
        builder.config_descriptor.interface(
 | 
			
		||||
            if_num,
 | 
			
		||||
            USB_CLASS_HID,
 | 
			
		||||
            USB_SUBCLASS_NONE,
 | 
			
		||||
            USB_PROTOCOL_NONE,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // HID descriptor
 | 
			
		||||
        builder.config_descriptor.write(
 | 
			
		||||
            HID_DESC_DESCTYPE_HID,
 | 
			
		||||
            &[
 | 
			
		||||
                // HID Class spec version
 | 
			
		||||
                HID_DESC_SPEC_1_10[0],
 | 
			
		||||
                HID_DESC_SPEC_1_10[1],
 | 
			
		||||
                // Country code not supported
 | 
			
		||||
                HID_DESC_COUNTRY_UNSPEC,
 | 
			
		||||
                // Number of following descriptors
 | 
			
		||||
                1,
 | 
			
		||||
                // We have a HID report descriptor the host should read
 | 
			
		||||
                HID_DESC_DESCTYPE_HID_REPORT,
 | 
			
		||||
                // HID report descriptor size,
 | 
			
		||||
                (len & 0xFF) as u8,
 | 
			
		||||
                (len >> 8 & 0xFF) as u8,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if let Some(ep) = ep_out {
 | 
			
		||||
            builder.config_descriptor.endpoint(ep.info());
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(ep) = ep_in {
 | 
			
		||||
            builder.config_descriptor.endpoint(ep.info());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d, const OUT_N: usize, const FEATURE_N: usize> ControlHandler
 | 
			
		||||
    for Control<'d, OUT_N, FEATURE_N>
 | 
			
		||||
{
 | 
			
		||||
    fn reset(&mut self) {}
 | 
			
		||||
 | 
			
		||||
    fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse {
 | 
			
		||||
        trace!("HID control_out {:?} {=[u8]:x}", req, data);
 | 
			
		||||
        if let RequestType::Class = req.request_type {
 | 
			
		||||
            match req.request {
 | 
			
		||||
                HID_REQ_SET_IDLE => {
 | 
			
		||||
                    if let Some(handler) = self.request_handler.as_ref() {
 | 
			
		||||
                        let id = req.value as u8;
 | 
			
		||||
                        let id = (id != 0).then(|| ReportId::In(id));
 | 
			
		||||
                        let dur = u64::from(req.value >> 8);
 | 
			
		||||
                        let dur = if dur == 0 {
 | 
			
		||||
                            Duration::MAX
 | 
			
		||||
                        } else {
 | 
			
		||||
                            Duration::from_millis(4 * dur)
 | 
			
		||||
                        };
 | 
			
		||||
                        handler.set_idle(id, dur);
 | 
			
		||||
                    }
 | 
			
		||||
                    OutResponse::Accepted
 | 
			
		||||
                }
 | 
			
		||||
                HID_REQ_SET_REPORT => match ReportId::try_from(req.value) {
 | 
			
		||||
                    Ok(ReportId::In(_)) => OutResponse::Rejected,
 | 
			
		||||
                    Ok(ReportId::Out(_id)) => {
 | 
			
		||||
                        let mut buf = [0; OUT_N];
 | 
			
		||||
                        buf[0..data.len()].copy_from_slice(data);
 | 
			
		||||
                        self.out_signal.signal((data.len(), buf));
 | 
			
		||||
                        OutResponse::Accepted
 | 
			
		||||
                    }
 | 
			
		||||
                    Ok(ReportId::Feature(_id)) => {
 | 
			
		||||
                        let mut buf = [0; FEATURE_N];
 | 
			
		||||
                        buf[0..data.len()].copy_from_slice(data);
 | 
			
		||||
                        self.feature_signal.signal((data.len(), buf));
 | 
			
		||||
                        OutResponse::Accepted
 | 
			
		||||
                    }
 | 
			
		||||
                    Err(_) => OutResponse::Rejected,
 | 
			
		||||
                },
 | 
			
		||||
                HID_REQ_SET_PROTOCOL => {
 | 
			
		||||
                    if req.value == 1 {
 | 
			
		||||
                        OutResponse::Accepted
 | 
			
		||||
                    } else {
 | 
			
		||||
                        warn!("HID Boot Protocol is unsupported.");
 | 
			
		||||
                        OutResponse::Rejected // UNSUPPORTED: Boot Protocol
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                _ => OutResponse::Rejected,
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
 | 
			
		||||
        trace!("HID control_in {:?}", req);
 | 
			
		||||
        match (req.request_type, req.request) {
 | 
			
		||||
            (RequestType::Standard, Request::GET_DESCRIPTOR) => match (req.value >> 8) as u8 {
 | 
			
		||||
                HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor),
 | 
			
		||||
                HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor),
 | 
			
		||||
                _ => InResponse::Rejected,
 | 
			
		||||
            },
 | 
			
		||||
            (RequestType::Class, HID_REQ_GET_REPORT) => {
 | 
			
		||||
                let size = match ReportId::try_from(req.value) {
 | 
			
		||||
                    Ok(id) => self
 | 
			
		||||
                        .request_handler
 | 
			
		||||
                        .as_ref()
 | 
			
		||||
                        .and_then(|x| x.get_report(id, buf)),
 | 
			
		||||
                    Err(_) => None,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if let Some(size) = size {
 | 
			
		||||
                    InResponse::Accepted(&buf[0..size])
 | 
			
		||||
                } else {
 | 
			
		||||
                    InResponse::Rejected
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            (RequestType::Class, HID_REQ_GET_IDLE) => {
 | 
			
		||||
                if let Some(handler) = self.request_handler.as_ref() {
 | 
			
		||||
                    let id = req.value as u8;
 | 
			
		||||
                    let id = (id != 0).then(|| ReportId::In(id));
 | 
			
		||||
                    if let Some(dur) = handler.get_idle(id) {
 | 
			
		||||
                        let dur = u8::try_from(dur.as_millis() / 4).unwrap_or(0);
 | 
			
		||||
                        buf[0] = dur;
 | 
			
		||||
                        InResponse::Accepted(&buf[0..1])
 | 
			
		||||
                    } else {
 | 
			
		||||
                        InResponse::Rejected
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    InResponse::Rejected
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            (RequestType::Class, HID_REQ_GET_PROTOCOL) => {
 | 
			
		||||
                // UNSUPPORTED: Boot Protocol
 | 
			
		||||
                buf[0] = 1;
 | 
			
		||||
                InResponse::Accepted(&buf[0..1])
 | 
			
		||||
            }
 | 
			
		||||
            _ => InResponse::Rejected,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -6,13 +6,14 @@ version = "0.1.0"
 | 
			
		||||
 | 
			
		||||
[features]
 | 
			
		||||
default = ["nightly"]
 | 
			
		||||
nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial"]
 | 
			
		||||
nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
 | 
			
		||||
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
 | 
			
		||||
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
 | 
			
		||||
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
 | 
			
		||||
embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true }
 | 
			
		||||
 | 
			
		||||
defmt = "0.3"
 | 
			
		||||
defmt-rtt = "0.3"
 | 
			
		||||
@ -23,4 +24,5 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
 | 
			
		||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
 | 
			
		||||
rand = { version = "0.8.4", default-features = false }
 | 
			
		||||
embedded-storage = "0.3.0"
 | 
			
		||||
 | 
			
		||||
usbd-hid = "0.5.2"
 | 
			
		||||
serde = { version = "1.0.136", default-features = false }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										131
									
								
								examples/nrf/src/bin/usb_hid.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								examples/nrf/src/bin/usb_hid.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,131 @@
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(generic_associated_types)]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
#[path = "../example_common.rs"]
 | 
			
		||||
mod example_common;
 | 
			
		||||
 | 
			
		||||
use core::mem;
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy::executor::Spawner;
 | 
			
		||||
use embassy::time::{Duration, Timer};
 | 
			
		||||
use embassy_nrf::interrupt;
 | 
			
		||||
use embassy_nrf::pac;
 | 
			
		||||
use embassy_nrf::usb::Driver;
 | 
			
		||||
use embassy_nrf::Peripherals;
 | 
			
		||||
use embassy_usb::{Config, UsbDeviceBuilder};
 | 
			
		||||
use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State};
 | 
			
		||||
use futures::future::join;
 | 
			
		||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
 | 
			
		||||
 | 
			
		||||
#[embassy::main]
 | 
			
		||||
async fn main(_spawner: Spawner, p: Peripherals) {
 | 
			
		||||
    let clock: pac::CLOCK = unsafe { mem::transmute(()) };
 | 
			
		||||
    let power: pac::POWER = unsafe { mem::transmute(()) };
 | 
			
		||||
 | 
			
		||||
    info!("Enabling ext hfosc...");
 | 
			
		||||
    clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
 | 
			
		||||
    while clock.events_hfclkstarted.read().bits() != 1 {}
 | 
			
		||||
 | 
			
		||||
    info!("Waiting for vbus...");
 | 
			
		||||
    while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
 | 
			
		||||
    info!("vbus OK");
 | 
			
		||||
 | 
			
		||||
    // Create the driver, from the HAL.
 | 
			
		||||
    let irq = interrupt::take!(USBD);
 | 
			
		||||
    let driver = Driver::new(p.USBD, irq);
 | 
			
		||||
 | 
			
		||||
    // Create embassy-usb Config
 | 
			
		||||
    let mut config = Config::new(0xc0de, 0xcafe);
 | 
			
		||||
    config.manufacturer = Some("Tactile Engineering");
 | 
			
		||||
    config.product = Some("Testy");
 | 
			
		||||
    config.serial_number = Some("12345678");
 | 
			
		||||
    config.max_power = 100;
 | 
			
		||||
 | 
			
		||||
    // Create embassy-usb DeviceBuilder using the driver and config.
 | 
			
		||||
    // It needs some buffers for building the descriptors.
 | 
			
		||||
    let mut device_descriptor = [0; 256];
 | 
			
		||||
    let mut config_descriptor = [0; 256];
 | 
			
		||||
    let mut bos_descriptor = [0; 256];
 | 
			
		||||
    let mut control_buf = [0; 16];
 | 
			
		||||
    let request_handler = MyRequestHandler {};
 | 
			
		||||
 | 
			
		||||
    let mut state = State::<5, 0, 0>::new();
 | 
			
		||||
 | 
			
		||||
    let mut builder = UsbDeviceBuilder::new(
 | 
			
		||||
        driver,
 | 
			
		||||
        config,
 | 
			
		||||
        &mut device_descriptor,
 | 
			
		||||
        &mut config_descriptor,
 | 
			
		||||
        &mut bos_descriptor,
 | 
			
		||||
        &mut control_buf,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Create classes on the builder.
 | 
			
		||||
    // let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
 | 
			
		||||
    let mut hid = HidClass::new(
 | 
			
		||||
        &mut builder,
 | 
			
		||||
        &mut state,
 | 
			
		||||
        MouseReport::desc(),
 | 
			
		||||
        Some(&request_handler),
 | 
			
		||||
        60,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Build the builder.
 | 
			
		||||
    let mut usb = builder.build();
 | 
			
		||||
 | 
			
		||||
    // Run the USB device.
 | 
			
		||||
    let usb_fut = usb.run();
 | 
			
		||||
 | 
			
		||||
    // Do stuff with the class!
 | 
			
		||||
    let hid_fut = async {
 | 
			
		||||
        loop {
 | 
			
		||||
            Timer::after(Duration::from_millis(500)).await;
 | 
			
		||||
            hid.input()
 | 
			
		||||
                .serialize(&MouseReport {
 | 
			
		||||
                    buttons: 0,
 | 
			
		||||
                    x: 0,
 | 
			
		||||
                    y: 4,
 | 
			
		||||
                    wheel: 0,
 | 
			
		||||
                    pan: 0,
 | 
			
		||||
                })
 | 
			
		||||
                .await
 | 
			
		||||
                .unwrap();
 | 
			
		||||
 | 
			
		||||
            Timer::after(Duration::from_millis(500)).await;
 | 
			
		||||
            hid.input()
 | 
			
		||||
                .serialize(&MouseReport {
 | 
			
		||||
                    buttons: 0,
 | 
			
		||||
                    x: 0,
 | 
			
		||||
                    y: -4,
 | 
			
		||||
                    wheel: 0,
 | 
			
		||||
                    pan: 0,
 | 
			
		||||
                })
 | 
			
		||||
                .await
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Run everything concurrently.
 | 
			
		||||
    // If we had made everything `'static` above instead, we could do this using separate tasks instead.
 | 
			
		||||
    join(usb_fut, hid_fut).await;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct MyRequestHandler {}
 | 
			
		||||
 | 
			
		||||
impl RequestHandler for MyRequestHandler {
 | 
			
		||||
    fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> {
 | 
			
		||||
        info!("Get report for {:?}", id);
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_idle(&self, id: Option<ReportId>, dur: Duration) {
 | 
			
		||||
        info!("Set idle rate for {:?} to {:?}", id, dur);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> {
 | 
			
		||||
        info!("Get idle rate for {:?}", id);
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user