usbd_human_interface_device/
usb_class.rs

1//! USB Class for implementing Human Interface Devices
2
3use crate::descriptor::{DescriptorType, HidProtocol, HidRequest};
4use crate::device::{DeviceClass, DeviceHList};
5use crate::interface::{InterfaceClass, ReportDescriptor, UsbAllocatable};
6use crate::UsbHidError;
7use core::cell::RefCell;
8use core::default::Default;
9use core::marker::PhantomData;
10use frunk::hlist::{HList, Selector};
11use frunk::{HCons, HNil, ToMut};
12#[allow(clippy::wildcard_imports)]
13use usb_device::class_prelude::*;
14use usb_device::control::{Recipient, Request};
15use usb_device::descriptor::lang_id::LangID;
16use usb_device::{control::RequestType, Result};
17
18pub mod prelude {
19    //! Prelude for implementing Human Interface Devices
20    //!
21    //! The purpose of this module is to alleviate imports of structs and enums
22    //! required to build and instance a device based on [`UsbHidClass`]:
23    //!
24    //! ```
25    //! # #![allow(unused_imports)]
26    //! use usbd_human_interface_device::usb_class::prelude::*;
27    //! ```
28
29    pub use crate::descriptor::{HidProtocol, InterfaceProtocol};
30    pub use crate::device::DeviceClass;
31    pub use crate::interface::{
32        InBytes16, InBytes32, InBytes64, InBytes8, InNone, Interface, InterfaceBuilder,
33        InterfaceConfig, OutBytes16, OutBytes32, OutBytes64, OutBytes8, OutNone, ReportSingle,
34        Reports128, Reports16, Reports32, Reports64, Reports8, UsbAllocatable,
35    };
36    pub use crate::interface::{ManagedIdleInterface, ManagedIdleInterfaceConfig};
37    pub use crate::usb_class::{UsbHidClass, UsbHidClassBuilder};
38    pub use crate::UsbHidError;
39}
40
41/// [`UsbHidClassBuilder`] error
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum UsbHidBuilderError {
45    /// A value is greater than the acceptable range of input values
46    ValueOverflow,
47    /// A slice of data is longer than permitted
48    SliceLengthOverflow,
49}
50
51/// Builder for [`UsbHidClass`]
52#[must_use = "this `UsbHidClassBuilder` must be assigned or consumed by `::build()`"]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54#[derive(Debug, Clone, Copy, Eq, PartialEq)]
55pub struct UsbHidClassBuilder<'a, B, Devices> {
56    devices: Devices,
57    marker: PhantomData<&'a B>,
58}
59
60impl<B> UsbHidClassBuilder<'_, B, HNil> {
61    pub fn new() -> Self {
62        Self {
63            devices: HNil,
64            marker: PhantomData,
65        }
66    }
67}
68
69impl<B> Default for UsbHidClassBuilder<'_, B, HNil> {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl<'a, B: UsbBus, Tail: HList> UsbHidClassBuilder<'a, B, Tail> {
76    pub fn add_device<Config, Device>(
77        self,
78        config: Config,
79    ) -> UsbHidClassBuilder<'a, B, HCons<Config, Tail>>
80    where
81        Config: UsbAllocatable<'a, B, Allocated = Device>,
82        Device: DeviceClass<'a>,
83    {
84        UsbHidClassBuilder {
85            devices: self.devices.prepend(config),
86            marker: PhantomData,
87        }
88    }
89}
90
91impl<'a, B, Config, Tail> UsbHidClassBuilder<'a, B, HCons<Config, Tail>>
92where
93    B: UsbBus,
94    Tail: UsbAllocatable<'a, B>,
95    Config: UsbAllocatable<'a, B>,
96{
97    pub fn build(
98        self,
99        usb_alloc: &'a UsbBusAllocator<B>,
100    ) -> UsbHidClass<'a, B, HCons<Config::Allocated, Tail::Allocated>> {
101        UsbHidClass {
102            devices: RefCell::new(self.devices.allocate(usb_alloc)),
103            _marker: PhantomData,
104        }
105    }
106}
107
108pub type BuilderResult<B> = core::result::Result<B, UsbHidBuilderError>;
109
110/// USB Human Interface Device class
111pub struct UsbHidClass<'a, B, Devices> {
112    // Using a RefCell makes it simpler to implement devices as all calls to interfaces are mut
113    // this could be removed, but then each usb device would need to implement a non mut borrow
114    // of its `RawInterface`.
115    devices: RefCell<Devices>,
116    _marker: PhantomData<&'a B>,
117}
118
119impl<'a, B, Devices: DeviceHList<'a>> UsbHidClass<'a, B, Devices> {
120    /// Borrow a single device selected by `T`
121    pub fn device<T, Index>(&mut self) -> &mut T
122    where
123        Devices: Selector<T, Index>,
124    {
125        self.devices.get_mut().get_mut()
126    }
127
128    /// Borrow an [`HList`] of all devices
129    pub fn devices(&'a mut self) -> <Devices as ToMut<'a>>::Output {
130        self.devices.get_mut().to_mut()
131    }
132
133    /// Provide a clock tick to allow the tracking of time. Call this every 1ms / at 1KHz
134    pub fn tick(&mut self) -> core::result::Result<(), UsbHidError> {
135        self.devices.get_mut().tick()
136    }
137}
138
139impl<'a, B: UsbBus + 'a, Devices> UsbHidClass<'a, B, Devices> {
140    fn get_descriptor(transfer: ControlIn<B>, interface: &mut dyn InterfaceClass<'a>) {
141        let request: &Request = transfer.request();
142        match DescriptorType::try_from((request.value >> 8) as u8) {
143            Ok(DescriptorType::Report) => {
144                let result = match interface.report_descriptor() {
145                    ReportDescriptor::DynamicDescriptor(desc) => transfer.accept_with(desc),
146                    ReportDescriptor::StaticDescriptor(desc) => transfer.accept_with_static(desc),
147                };
148
149                match result {
150                    Err(e) => error!("Failed to send report descriptor - {:?}", e),
151                    Ok(()) => {
152                        trace!("Sent report descriptor");
153                    }
154                }
155            }
156            Ok(DescriptorType::Hid) => {
157                let transfer_result = transfer.accept(|buffer| {
158                    if buffer.len() < 9 {
159                        return Err(UsbError::BufferOverflow);
160                    }
161
162                    buffer[0] = 9;
163                    buffer[1] = u8::from(DescriptorType::Hid);
164                    (buffer[2..9]).copy_from_slice(&interface.hid_descriptor_body());
165                    Ok(9)
166                });
167                match transfer_result {
168                    Err(e) => {
169                        error!("Failed to send Hid descriptor - {:?}", e);
170                    }
171                    Ok(()) => {
172                        trace!("Sent hid descriptor");
173                    }
174                }
175            }
176            _ => {
177                warn!(
178                    "Unsupported descriptor type, request type:{:?}, request:{}, value:{}",
179                    request.request_type, request.request, request.value
180                );
181            }
182        }
183    }
184}
185
186impl<'a, B, Devices> UsbClass<B> for UsbHidClass<'a, B, Devices>
187where
188    B: UsbBus + 'a,
189    Devices: DeviceHList<'a>,
190{
191    fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
192        self.devices.borrow_mut().write_descriptors(writer)?;
193        info!("wrote class config descriptor");
194        Ok(())
195    }
196
197    fn get_string(&self, index: StringIndex, lang_id: LangID) -> Option<&str> {
198        self.devices.borrow_mut().get_string(index, lang_id)
199    }
200
201    fn reset(&mut self) {
202        info!("Reset");
203        self.devices.get_mut().reset();
204    }
205
206    fn control_out(&mut self, transfer: ControlOut<B>) {
207        let request: &Request = transfer.request();
208
209        //only respond to Class requests for this interface
210        if !(request.request_type == RequestType::Class
211            && request.recipient == Recipient::Interface)
212        {
213            return;
214        }
215
216        let Some(interface) = u8::try_from(request.index)
217            .ok()
218            .and_then(|id| self.devices.get_mut().get(id))
219        else {
220            return;
221        };
222
223        trace!(
224            "ctrl_out: request type: {:?}, request: {}, value: {}",
225            request.request_type,
226            request.request,
227            request.value
228        );
229
230        match HidRequest::try_from(request.request) {
231            Ok(HidRequest::SetReport) => {
232                interface.set_report(transfer.data()).ok();
233                transfer.accept().ok();
234            }
235            Ok(HidRequest::SetIdle) => {
236                if request.length != 0 {
237                    warn!(
238                        "Expected SetIdle to have length 0, received {}",
239                        request.length
240                    );
241                }
242
243                interface.set_idle((request.value & 0xFF) as u8, (request.value >> 8) as u8);
244                transfer.accept().ok();
245            }
246            Ok(HidRequest::SetProtocol) => {
247                if request.length != 0 {
248                    warn!(
249                        "Expected SetProtocol to have length 0, received {}",
250                        request.length
251                    );
252                }
253                if let Ok(protocol) = HidProtocol::try_from((request.value & 0xFF) as u8) {
254                    interface.set_protocol(protocol);
255                    transfer.accept().ok();
256                } else {
257                    error!(
258                        "Unable to set protocol, unsupported value:{}",
259                        request.value
260                    );
261                }
262            }
263            _ => {
264                warn!(
265                    "Unsupported control_out request type: {:?}, request: {}, value: {}",
266                    request.request_type, request.request, request.value
267                );
268            }
269        }
270    }
271
272    fn control_in(&mut self, transfer: ControlIn<B>) {
273        let request: &Request = transfer.request();
274        //only respond to requests for this interface
275        if !(request.recipient == Recipient::Interface) {
276            return;
277        }
278
279        let Ok(interface_id) = u8::try_from(request.index) else {
280            return;
281        };
282
283        trace!(
284            "ctrl_in: request type: {:?}, request: {}, value: {}",
285            request.request_type,
286            request.request,
287            request.value
288        );
289
290        match request.request_type {
291            RequestType::Standard => {
292                if let Some(interface) = self.devices.get_mut().get(interface_id) {
293                    if request.request == Request::GET_DESCRIPTOR {
294                        info!("Get descriptor");
295                        Self::get_descriptor(transfer, interface);
296                    }
297                }
298            }
299
300            RequestType::Class => {
301                let Some(interface) = self.devices.get_mut().get(interface_id) else {
302                    return;
303                };
304
305                match HidRequest::try_from(request.request) {
306                    Ok(HidRequest::GetReport) => {
307                        let requested_n = transfer.request().length.into();
308                        if let Err(e) = transfer.accept(|buffer| {
309                            interface.get_report(buffer).inspect(|&n| {
310                                if n != requested_n {
311                                    warn!(
312                                        "GetReport requested {} bytes, got {} bytes",
313                                        requested_n, n
314                                    );
315                                }
316                            })
317                        }) {
318                            error!("Failed to send report - {:?}", e);
319                        } else {
320                            trace!("Sent report");
321                            unwrap!(interface.get_report_ack());
322                        }
323                    }
324                    Ok(HidRequest::GetIdle) => {
325                        if request.length != 1 {
326                            warn!(
327                                "Expected GetIdle to have length 1, received {}",
328                                request.length
329                            );
330                        }
331
332                        let report_id = (request.value & 0xFF) as u8;
333                        let idle = interface.get_idle(report_id);
334                        if let Err(e) = transfer.accept(|buffer| {
335                            if buffer.is_empty() {
336                                return Err(UsbError::BufferOverflow);
337                            }
338                            buffer[0] = idle;
339                            Ok(1)
340                        }) {
341                            error!("Failed to send idle data - {:?}", e);
342                        } else {
343                            info!("Get Idle for ID{}: {}", report_id, idle);
344                        }
345                    }
346                    Ok(HidRequest::GetProtocol) => {
347                        if request.length != 1 {
348                            warn!(
349                                "Expected GetProtocol to have length 1, received {}",
350                                request.length
351                            );
352                        }
353
354                        let protocol = interface.get_protocol();
355                        if let Err(e) = transfer.accept(|buffer| {
356                            if buffer.is_empty() {
357                                return Err(UsbError::BufferOverflow);
358                            }
359                            buffer[0] = protocol.into();
360                            Ok(1)
361                        }) {
362                            error!("Failed to send protocol data - {:?}", e);
363                        } else {
364                            info!("Get protocol: {:?}", protocol);
365                        }
366                    }
367                    _ => {
368                        warn!(
369                            "Unsupported control_in request type: {:?}, request: {}, value: {}",
370                            request.request_type, request.request, request.value
371                        );
372                    }
373                }
374            }
375            _ => {}
376        }
377    }
378}
379
380#[cfg(test)]
381mod test {
382    #![allow(clippy::unwrap_used)]
383    #![allow(clippy::expect_used)]
384
385    use std::cell::RefCell;
386    use std::sync::Mutex;
387    use std::vec::Vec;
388
389    use crate::descriptor::USB_CLASS_HID;
390    use crate::interface::{InBytes64, InterfaceBuilder, OutBytes64, ReportSingle, Reports8};
391    use env_logger::Env;
392    use fugit::MillisDurationU32;
393    use log::SetLoggerError;
394    use packed_struct::prelude::*;
395    use usb_device::bus::PollResult;
396    use usb_device::prelude::*;
397    use usb_device::UsbDirection;
398
399    use super::*;
400
401    fn init_logging() {
402        let _: core::result::Result<(), SetLoggerError> =
403            env_logger::Builder::from_env(Env::default().default_filter_or("trace"))
404                .is_test(true)
405                .try_init();
406    }
407
408    #[derive(Default)]
409    struct UsbTestManager {
410        in_buf: Mutex<RefCell<Vec<u8>>>,
411        setup_buf: Mutex<RefCell<Vec<u8>>>,
412    }
413
414    impl UsbTestManager {
415        fn host_write_setup(&self, data: &[u8]) -> Result<()> {
416            let buf = self.setup_buf.lock().unwrap();
417            if buf.borrow().is_empty() {
418                buf.borrow_mut().extend_from_slice(data);
419                Ok(())
420            } else {
421                Err(UsbError::WouldBlock)
422            }
423        }
424
425        fn host_read_in(&self) -> Vec<u8> {
426            self.in_buf.lock().unwrap().take()
427        }
428
429        fn has_setup_data(&self) -> bool {
430            !self.setup_buf.lock().unwrap().borrow().is_empty()
431        }
432
433        fn device_read_setup(&self, data: &mut [u8]) -> Result<usize> {
434            let buf = self.setup_buf.lock().unwrap();
435            if buf.borrow().is_empty() {
436                Err(UsbError::WouldBlock)
437            } else {
438                let tmp = buf.take();
439                data[..tmp.len()].copy_from_slice(&tmp);
440                Ok(tmp.len())
441            }
442        }
443
444        fn device_write(&self, data: &[u8]) -> Result<usize> {
445            let buf = self.in_buf.lock().unwrap();
446            if buf.borrow().is_empty() {
447                buf.borrow_mut().extend_from_slice(data);
448                Ok(data.len())
449            } else {
450                Err(UsbError::WouldBlock)
451            }
452        }
453    }
454
455    struct TestUsbBus<'a> {
456        next_ep_index: usize,
457        manager: &'a UsbTestManager,
458    }
459
460    impl<'a> TestUsbBus<'a> {
461        fn new(manager: &'a UsbTestManager) -> Self {
462            TestUsbBus {
463                next_ep_index: 0,
464                manager,
465            }
466        }
467    }
468
469    impl UsbBus for TestUsbBus<'_> {
470        fn alloc_ep(
471            &mut self,
472            ep_dir: UsbDirection,
473            _ep_addr: Option<EndpointAddress>,
474            _ep_type: EndpointType,
475            _max_packet_size: u16,
476            _interval: u8,
477        ) -> Result<EndpointAddress> {
478            let ep = EndpointAddress::from_parts(self.next_ep_index, ep_dir);
479            self.next_ep_index += 1;
480            Ok(ep)
481        }
482
483        fn enable(&mut self) {}
484        fn reset(&self) {
485            todo!()
486        }
487        fn set_device_address(&self, _addr: u8) {
488            todo!()
489        }
490        fn write(&self, _ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize> {
491            self.manager.device_write(buf)
492        }
493        fn read(&self, _ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize> {
494            self.manager.device_read_setup(buf)
495        }
496        fn set_stalled(&self, _ep_addr: EndpointAddress, _stalled: bool) {}
497        fn is_stalled(&self, _ep_addr: EndpointAddress) -> bool {
498            todo!()
499        }
500        fn suspend(&self) {
501            todo!()
502        }
503        fn resume(&self) {
504            todo!()
505        }
506        fn poll(&self) -> PollResult {
507            PollResult::Data {
508                ep_out: 0,
509                ep_in_complete: 1,
510                ep_setup: u16::from(self.manager.has_setup_data()),
511            }
512        }
513    }
514
515    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
516    #[derive(Clone, Copy, Debug, PartialEq, Eq, PackedStruct)]
517    #[packed_struct(endian = "lsb", bit_numbering = "msb0", size_bytes = "8")]
518    struct UsbRequest {
519        #[packed_field(bits = "0")]
520        direction: bool,
521        #[packed_field(bits = "1:2")]
522        request_type: u8,
523        #[packed_field(bits = "4:7")]
524        recipient: u8,
525        request: u8,
526        value: u16,
527        index: u16,
528        length: u16,
529    }
530
531    #[test]
532    fn descriptor_ordering_satisfies_boot_spec() {
533        init_logging();
534
535        let manager = UsbTestManager::default();
536
537        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
538
539        let mut hid = UsbHidClassBuilder::new()
540            .add_device(
541                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
542                    .unwrap()
543                    .build(),
544            )
545            .build(&usb_alloc);
546
547        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
548            .device_class(USB_CLASS_HID)
549            .build();
550
551        // Get Configuration
552        manager
553            .host_write_setup(
554                &UsbRequest {
555                    direction: UsbDirection::In != UsbDirection::Out,
556                    request_type: RequestType::Standard as u8,
557                    recipient: Recipient::Device as u8,
558                    request: Request::GET_DESCRIPTOR,
559                    value: u16::from(usb_device::descriptor::descriptor_type::CONFIGURATION) << 8,
560                    index: 0,
561                    length: 0xFFFF,
562                }
563                .pack()
564                .unwrap(),
565            )
566            .unwrap();
567
568        assert!(usb_dev.poll(&mut [&mut hid]));
569
570        // read multiple transfers then validate config
571        let mut data = Vec::new();
572
573        loop {
574            let read = manager.host_read_in();
575            if read.is_empty() {
576                break;
577            }
578            data.extend_from_slice(&read);
579            assert!(usb_dev.poll(&mut [&mut hid]));
580        }
581
582        /*
583          Expected descriptor order (<https://www.usb.org/sites/default/files/hid1_11.pdf> Appendix F.3):
584            Configuration descriptor (other Interface, Endpoint, and Vendor Specific descriptors if required)
585            Interface descriptor (with Subclass and Protocol specifying Boot Keyboard)
586                Hid descriptor (associated with this Interface)
587                Endpoint descriptor (Hid Interrupt In Endpoint)
588                    (other Interface, Endpoint, and Vendor Specific descriptors if required)
589        */
590
591        let mut it = data.iter();
592
593        let len = *it.next().unwrap();
594        assert_eq!(
595            *(it.next().unwrap()),
596            0x02,
597            "Expected Configuration descriptor"
598        );
599        for _ in 0..(len - 2) {
600            it.next().unwrap();
601        }
602
603        let len = it.next().unwrap();
604        assert_eq!(*it.next().unwrap(), 0x04, "Expected Interface descriptor");
605        for _ in 0..(len - 2) {
606            it.next().unwrap();
607        }
608
609        let len = it.next().unwrap();
610        assert_eq!(*(it.next().unwrap()), 0x21, "Expected Hid descriptor");
611        for _ in 0..(len - 2) {
612            it.next().unwrap();
613        }
614
615        while let Some(&len) = it.next() {
616            assert_eq!(*(it.next().unwrap()), 0x05, "Expected Endpoint descriptor");
617
618            for _ in 0..(len - 2) {
619                it.next().unwrap();
620            }
621        }
622
623        // Check there isn't any more data
624        assert!(it.next().is_none());
625    }
626
627    #[test]
628    fn get_protocol_default_to_report() {
629        init_logging();
630
631        let manager = UsbTestManager::default();
632        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
633
634        let mut hid = UsbHidClassBuilder::new()
635            .add_device(
636                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
637                    .unwrap()
638                    .build(),
639            )
640            .build(&usb_alloc);
641
642        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
643            .device_class(USB_CLASS_HID)
644            .build();
645
646        // Get protocol
647        manager
648            .host_write_setup(
649                &UsbRequest {
650                    direction: UsbDirection::In != UsbDirection::Out,
651                    request_type: RequestType::Class as u8,
652                    recipient: Recipient::Interface as u8,
653                    request: HidRequest::GetProtocol.into(),
654                    value: 0x0,
655                    index: 0x0,
656                    length: 0x1,
657                }
658                .pack()
659                .unwrap(),
660            )
661            .unwrap();
662
663        assert!(usb_dev.poll(&mut [&mut hid]));
664
665        // read and validate hid_protocol report
666        let data = manager.host_read_in();
667        assert_eq!(
668            data,
669            [HidProtocol::Report.into()],
670            "Expected protocol to be Report by default"
671        );
672    }
673
674    #[test]
675    fn set_protocol() {
676        init_logging();
677
678        let manager = UsbTestManager::default();
679
680        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
681
682        let mut hid = UsbHidClassBuilder::new()
683            .add_device(
684                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
685                    .unwrap()
686                    .build(),
687            )
688            .build(&usb_alloc);
689
690        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
691            .device_class(USB_CLASS_HID)
692            .build();
693
694        // Set protocol to boot
695        manager
696            .host_write_setup(
697                &UsbRequest {
698                    direction: UsbDirection::In != UsbDirection::In,
699                    request_type: RequestType::Class as u8,
700                    recipient: Recipient::Interface as u8,
701                    request: HidRequest::SetProtocol.into(),
702                    value: HidProtocol::Boot as u16,
703                    index: 0x0,
704                    length: 0x0,
705                }
706                .pack()
707                .unwrap(),
708            )
709            .unwrap();
710
711        assert!(usb_dev.poll(&mut [&mut hid]));
712
713        // Get protocol
714        manager
715            .host_write_setup(
716                &UsbRequest {
717                    direction: UsbDirection::In != UsbDirection::Out,
718                    request_type: RequestType::Class as u8,
719                    recipient: Recipient::Interface as u8,
720                    request: HidRequest::GetProtocol.into(),
721                    value: 0x0,
722                    index: 0x0,
723                    length: 0x1,
724                }
725                .pack()
726                .unwrap(),
727            )
728            .unwrap();
729
730        assert!(usb_dev.poll(&mut [&mut hid]));
731
732        // read and validate hid_protocol report
733        let data = &manager.host_read_in();
734        assert_eq!(
735            data,
736            &[HidProtocol::Boot.into()],
737            "Expected protocol to be Boot"
738        );
739    }
740
741    #[test]
742    fn get_protocol_default_post_reset() {
743        init_logging();
744
745        let manager = UsbTestManager::default();
746
747        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
748
749        let mut hid = UsbHidClassBuilder::new()
750            .add_device(
751                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
752                    .unwrap()
753                    .build(),
754            )
755            .build(&usb_alloc);
756
757        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
758            .device_class(USB_CLASS_HID)
759            .build();
760
761        // Set protocol to boot
762        manager
763            .host_write_setup(
764                &UsbRequest {
765                    direction: UsbDirection::In != UsbDirection::In,
766                    request_type: RequestType::Class as u8,
767                    recipient: Recipient::Interface as u8,
768                    request: HidRequest::SetProtocol.into(),
769                    value: HidProtocol::Boot as u16,
770                    index: 0x0,
771                    length: 0x0,
772                }
773                .pack()
774                .unwrap(),
775            )
776            .unwrap();
777
778        assert!(usb_dev.poll(&mut [&mut hid]));
779
780        // simulate a bus reset after setting protocol to boot
781        hid.reset();
782
783        // Get protocol
784        manager
785            .host_write_setup(
786                &UsbRequest {
787                    direction: UsbDirection::In != UsbDirection::Out,
788                    request_type: RequestType::Class as u8,
789                    recipient: Recipient::Interface as u8,
790                    request: HidRequest::GetProtocol.into(),
791                    value: 0x0,
792                    index: 0x0,
793                    length: 0x1,
794                }
795                .pack()
796                .unwrap(),
797            )
798            .unwrap();
799
800        assert!(usb_dev.poll(&mut [&mut hid]));
801
802        // read and validate hid_protocol report
803        let data = &manager.host_read_in();
804        assert_eq!(
805            data,
806            &[HidProtocol::Report.into()],
807            "Expected protocol to be Report post reset"
808        );
809    }
810
811    #[test]
812    fn get_global_idle_default() {
813        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
814
815        init_logging();
816
817        let manager = UsbTestManager::default();
818
819        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
820
821        let mut hid = UsbHidClassBuilder::new()
822            .add_device(
823                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
824                    .unwrap()
825                    .idle_default(IDLE_DEFAULT)
826                    .unwrap()
827                    .build(),
828            )
829            .build(&usb_alloc);
830
831        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
832            .device_class(USB_CLASS_HID)
833            .build();
834
835        // Get idle
836        manager
837            .host_write_setup(
838                &UsbRequest {
839                    direction: UsbDirection::In != UsbDirection::Out,
840                    request_type: RequestType::Class as u8,
841                    recipient: Recipient::Interface as u8,
842                    request: HidRequest::GetIdle.into(),
843                    value: 0x0,
844                    index: 0x0,
845                    length: 0x1,
846                }
847                .pack()
848                .unwrap(),
849            )
850            .unwrap();
851
852        assert!(usb_dev.poll(&mut [&mut hid]));
853
854        // read and validate hid_protocol report
855        let data = manager.host_read_in();
856        assert_eq!(
857            data,
858            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
859            "Unexpected idle value"
860        );
861    }
862
863    #[test]
864    fn set_global_idle() {
865        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
866        const IDLE_NEW: MillisDurationU32 = MillisDurationU32::millis(88);
867
868        init_logging();
869
870        let manager = UsbTestManager::default();
871
872        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
873
874        let mut hid = UsbHidClassBuilder::new()
875            .add_device(
876                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
877                    .unwrap()
878                    .idle_default(IDLE_DEFAULT)
879                    .unwrap()
880                    .build(),
881            )
882            .build(&usb_alloc);
883
884        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
885            .device_class(USB_CLASS_HID)
886            .build();
887
888        // Set idle
889        manager
890            .host_write_setup(
891                &UsbRequest {
892                    direction: UsbDirection::In != UsbDirection::In,
893                    request_type: RequestType::Class as u8,
894                    recipient: Recipient::Interface as u8,
895                    request: HidRequest::SetIdle.into(),
896                    value: (u16::try_from(IDLE_NEW.to_millis()).unwrap() / 4) << 8,
897                    index: 0x0,
898                    length: 0x0,
899                }
900                .pack()
901                .unwrap(),
902            )
903            .unwrap();
904
905        assert!(usb_dev.poll(&mut [&mut hid]));
906
907        // Get idle
908        manager
909            .host_write_setup(
910                &UsbRequest {
911                    direction: UsbDirection::In != UsbDirection::Out,
912                    request_type: RequestType::Class as u8,
913                    recipient: Recipient::Interface as u8,
914                    request: HidRequest::GetIdle.into(),
915                    value: 0x0,
916                    index: 0x0,
917                    length: 0x1,
918                }
919                .pack()
920                .unwrap(),
921            )
922            .unwrap();
923
924        assert!(usb_dev.poll(&mut [&mut hid]));
925
926        // read and validate hid_protocol report
927        let data = manager.host_read_in();
928        assert_eq!(
929            data,
930            [u8::try_from(IDLE_NEW.ticks()).unwrap() / 4],
931            "Unexpected idle value"
932        );
933    }
934
935    #[test]
936    fn get_global_idle_default_post_reset() {
937        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
938        const IDLE_NEW: MillisDurationU32 = MillisDurationU32::millis(88);
939
940        init_logging();
941
942        let manager = UsbTestManager::default();
943
944        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
945
946        let mut hid = UsbHidClassBuilder::new()
947            .add_device(
948                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
949                    .unwrap()
950                    .idle_default(IDLE_DEFAULT)
951                    .unwrap()
952                    .build(),
953            )
954            .build(&usb_alloc);
955
956        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
957            .device_class(USB_CLASS_HID)
958            .build();
959
960        // Set idle
961        manager
962            .host_write_setup(
963                &UsbRequest {
964                    direction: UsbDirection::In != UsbDirection::In,
965                    request_type: RequestType::Class as u8,
966                    recipient: Recipient::Interface as u8,
967                    request: HidRequest::SetIdle.into(),
968                    value: (u16::try_from(IDLE_NEW.to_millis()).unwrap() / 4) << 8,
969                    index: 0x0,
970                    length: 0x0,
971                }
972                .pack()
973                .unwrap(),
974            )
975            .unwrap();
976
977        assert!(usb_dev.poll(&mut [&mut hid]));
978
979        hid.reset();
980
981        // Get idle
982        manager
983            .host_write_setup(
984                &UsbRequest {
985                    direction: UsbDirection::In != UsbDirection::Out,
986                    request_type: RequestType::Class as u8,
987                    recipient: Recipient::Interface as u8,
988                    request: HidRequest::GetIdle.into(),
989                    value: 0x0,
990                    index: 0x0,
991                    length: 0x1,
992                }
993                .pack()
994                .unwrap(),
995            )
996            .unwrap();
997
998        assert!(usb_dev.poll(&mut [&mut hid]));
999
1000        // read and validate hid_protocol report
1001        let data = manager.host_read_in();
1002        assert_eq!(
1003            data,
1004            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
1005            "Unexpected idle value"
1006        );
1007    }
1008
1009    #[test]
1010    fn get_report_idle_default() {
1011        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
1012        const REPORT_ID: u8 = 0xAB;
1013
1014        init_logging();
1015
1016        let manager = UsbTestManager::default();
1017
1018        let usb_bus = TestUsbBus::new(&manager);
1019
1020        let usb_alloc = UsbBusAllocator::new(usb_bus);
1021
1022        let mut hid = UsbHidClassBuilder::new()
1023            .add_device(
1024                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
1025                    .unwrap()
1026                    .idle_default(IDLE_DEFAULT)
1027                    .unwrap()
1028                    .build(),
1029            )
1030            .build(&usb_alloc);
1031
1032        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
1033            .device_class(USB_CLASS_HID)
1034            .build();
1035
1036        // Get idle
1037        manager
1038            .host_write_setup(
1039                &UsbRequest {
1040                    direction: UsbDirection::In != UsbDirection::Out,
1041                    request_type: RequestType::Class as u8,
1042                    recipient: Recipient::Interface as u8,
1043                    request: HidRequest::GetIdle.into(),
1044                    value: u16::from(REPORT_ID),
1045                    index: 0x0,
1046                    length: 0x1,
1047                }
1048                .pack()
1049                .unwrap(),
1050            )
1051            .unwrap();
1052
1053        assert!(usb_dev.poll(&mut [&mut hid]));
1054
1055        // read and validate hid_protocol report
1056        let data = manager.host_read_in();
1057        assert_eq!(
1058            data,
1059            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
1060            "Unexpected idle value"
1061        );
1062    }
1063
1064    #[test]
1065    fn set_report_idle() {
1066        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
1067        const IDLE_NEW: MillisDurationU32 = MillisDurationU32::millis(88);
1068        const REPORT_ID: u8 = 0x4;
1069
1070        init_logging();
1071
1072        let manager = UsbTestManager::default();
1073
1074        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
1075
1076        let mut hid = UsbHidClassBuilder::new()
1077            .add_device(
1078                InterfaceBuilder::<InBytes64, OutBytes64, Reports8>::new(&[])
1079                    .unwrap()
1080                    .idle_default(IDLE_DEFAULT)
1081                    .unwrap()
1082                    .build(),
1083            )
1084            .build(&usb_alloc);
1085
1086        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
1087            .device_class(USB_CLASS_HID)
1088            .build();
1089
1090        // Set report idle
1091        manager
1092            .host_write_setup(
1093                &UsbRequest {
1094                    direction: UsbDirection::In != UsbDirection::In,
1095                    request_type: RequestType::Class as u8,
1096                    recipient: Recipient::Interface as u8,
1097                    request: HidRequest::SetIdle.into(),
1098                    value: ((u16::try_from(IDLE_NEW.to_millis()).unwrap() / 4) << 8)
1099                        | u16::from(REPORT_ID),
1100                    index: 0x0,
1101                    length: 0x0,
1102                }
1103                .pack()
1104                .unwrap(),
1105            )
1106            .unwrap();
1107
1108        assert!(usb_dev.poll(&mut [&mut hid]));
1109
1110        // Get report idle
1111        manager
1112            .host_write_setup(
1113                &UsbRequest {
1114                    direction: UsbDirection::In != UsbDirection::Out,
1115                    request_type: RequestType::Class as u8,
1116                    recipient: Recipient::Interface as u8,
1117                    request: HidRequest::GetIdle.into(),
1118                    value: u16::from(REPORT_ID),
1119                    index: 0x0,
1120                    length: 0x1,
1121                }
1122                .pack()
1123                .unwrap(),
1124            )
1125            .unwrap();
1126
1127        assert!(usb_dev.poll(&mut [&mut hid]));
1128
1129        // read and validate hid_protocol report
1130        let data = manager.host_read_in();
1131        assert_eq!(
1132            data,
1133            [u8::try_from(IDLE_NEW.ticks()).unwrap() / 4],
1134            "Unexpected report idle value"
1135        );
1136
1137        // Get global idle
1138        manager
1139            .host_write_setup(
1140                &UsbRequest {
1141                    direction: UsbDirection::In != UsbDirection::Out,
1142                    request_type: RequestType::Class as u8,
1143                    recipient: Recipient::Interface as u8,
1144                    request: HidRequest::GetIdle.into(),
1145                    value: 0x0,
1146                    index: 0x0,
1147                    length: 0x1,
1148                }
1149                .pack()
1150                .unwrap(),
1151            )
1152            .unwrap();
1153
1154        assert!(usb_dev.poll(&mut [&mut hid]));
1155
1156        // read and validate hid_protocol report
1157        let data = manager.host_read_in();
1158        assert_eq!(
1159            data,
1160            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
1161            "Unexpected global idle value"
1162        );
1163    }
1164
1165    #[test]
1166    fn set_report_idle_no_reports() {
1167        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
1168        const IDLE_NEW: MillisDurationU32 = MillisDurationU32::millis(88);
1169        const REPORT_ID: u16 = 0x4;
1170
1171        init_logging();
1172
1173        let manager = UsbTestManager::default();
1174
1175        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
1176
1177        let mut hid = UsbHidClassBuilder::new()
1178            .add_device(
1179                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
1180                    .unwrap()
1181                    .idle_default(IDLE_DEFAULT)
1182                    .unwrap()
1183                    .build(),
1184            )
1185            .build(&usb_alloc);
1186
1187        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
1188            .device_class(USB_CLASS_HID)
1189            .build();
1190
1191        // Set report idle - this is a nop due to lack of report storage in the device (`SingleReport`)
1192        manager
1193            .host_write_setup(
1194                &UsbRequest {
1195                    direction: UsbDirection::In != UsbDirection::In,
1196                    request_type: RequestType::Class as u8,
1197                    recipient: Recipient::Interface as u8,
1198                    request: HidRequest::SetIdle as u8,
1199                    value: ((u16::try_from(IDLE_NEW.to_millis()).unwrap() / 4) << 8) | REPORT_ID,
1200                    index: 0x0,
1201                    length: 0x0,
1202                }
1203                .pack()
1204                .unwrap(),
1205            )
1206            .unwrap();
1207
1208        assert!(usb_dev.poll(&mut [&mut hid]));
1209
1210        // Get report idle
1211        manager
1212            .host_write_setup(
1213                &UsbRequest {
1214                    direction: UsbDirection::In != UsbDirection::Out,
1215                    request_type: RequestType::Class as u8,
1216                    recipient: Recipient::Interface as u8,
1217                    request: HidRequest::GetIdle as u8,
1218                    value: REPORT_ID,
1219                    index: 0x0,
1220                    length: 0x1,
1221                }
1222                .pack()
1223                .unwrap(),
1224            )
1225            .unwrap();
1226
1227        assert!(usb_dev.poll(&mut [&mut hid]));
1228
1229        // read and validate hid_protocol report
1230        let data = manager.host_read_in();
1231        assert_eq!(
1232            data,
1233            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
1234            "Unexpected report idle value"
1235        );
1236
1237        // Get global idle
1238        manager
1239            .host_write_setup(
1240                &UsbRequest {
1241                    direction: UsbDirection::In != UsbDirection::Out,
1242                    request_type: RequestType::Class as u8,
1243                    recipient: Recipient::Interface as u8,
1244                    request: HidRequest::GetIdle as u8,
1245                    value: 0x0,
1246                    index: 0x0,
1247                    length: 0x1,
1248                }
1249                .pack()
1250                .unwrap(),
1251            )
1252            .unwrap();
1253
1254        assert!(usb_dev.poll(&mut [&mut hid]));
1255
1256        // read and validate hid_protocol report
1257        let data = manager.host_read_in();
1258        assert_eq!(
1259            data,
1260            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
1261            "Unexpected global idle value"
1262        );
1263    }
1264
1265    #[test]
1266    fn get_report_idle_default_post_reset() {
1267        const REPORT_ID: u8 = 0x4;
1268        const IDLE_DEFAULT: MillisDurationU32 = MillisDurationU32::millis(40);
1269        const IDLE_NEW: MillisDurationU32 = MillisDurationU32::millis(88);
1270
1271        init_logging();
1272
1273        let manager = UsbTestManager::default();
1274
1275        let usb_alloc = UsbBusAllocator::new(TestUsbBus::new(&manager));
1276
1277        let mut hid = UsbHidClassBuilder::new()
1278            .add_device(
1279                InterfaceBuilder::<InBytes64, OutBytes64, ReportSingle>::new(&[])
1280                    .unwrap()
1281                    .idle_default(IDLE_DEFAULT)
1282                    .unwrap()
1283                    .build(),
1284            )
1285            .build(&usb_alloc);
1286
1287        let mut usb_dev = UsbDeviceBuilder::new(&usb_alloc, UsbVidPid(0x1209, 0x0001))
1288            .device_class(USB_CLASS_HID)
1289            .build();
1290
1291        // Set report idle
1292        manager
1293            .host_write_setup(
1294                &UsbRequest {
1295                    direction: UsbDirection::In != UsbDirection::In,
1296                    request_type: RequestType::Class as u8,
1297                    recipient: Recipient::Interface as u8,
1298                    request: HidRequest::SetIdle.into(),
1299                    value: ((u16::try_from(IDLE_NEW.to_millis()).unwrap() / 4) << 8)
1300                        | u16::from(REPORT_ID),
1301                    index: 0x0,
1302                    length: 0x0,
1303                }
1304                .pack()
1305                .unwrap(),
1306            )
1307            .unwrap();
1308
1309        assert!(usb_dev.poll(&mut [&mut hid]));
1310
1311        hid.reset();
1312
1313        // Get report idle
1314        manager
1315            .host_write_setup(
1316                &UsbRequest {
1317                    direction: UsbDirection::In != UsbDirection::Out,
1318                    request_type: RequestType::Class as u8,
1319                    recipient: Recipient::Interface as u8,
1320                    request: HidRequest::GetIdle.into(),
1321                    value: u16::from(REPORT_ID),
1322                    index: 0x0,
1323                    length: 0x1,
1324                }
1325                .pack()
1326                .unwrap(),
1327            )
1328            .unwrap();
1329
1330        assert!(usb_dev.poll(&mut [&mut hid]));
1331
1332        // read and validate hid_protocol report
1333        let data = manager.host_read_in();
1334        assert_eq!(
1335            data,
1336            [u8::try_from(IDLE_DEFAULT.ticks()).unwrap() / 4],
1337            "Unexpected report idle value"
1338        );
1339    }
1340}