usbd_hid/
hid_class.rs

1//! Implements HID functionality for a usb-device device.
2use usb_device::class_prelude::*;
3use usb_device::Result;
4
5use crate::descriptor::AsInputReport;
6extern crate ssmarshal;
7use ssmarshal::serialize;
8
9const USB_CLASS_HID: u8 = 0x03;
10
11// HID
12const HID_DESC_DESCTYPE_HID: u8 = 0x21;
13const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
14const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01];
15
16/// Requests the set idle rate from the device
17/// See (7.2.3): <https://www.usb.org/sites/default/files/hid1_11.pdf>
18const HID_REQ_GET_IDLE: u8 = 0x02;
19
20/// Requests device to not send a particular report until a new event occurs
21/// or the specified amount of time passes.
22/// See (7.2.4): <https://www.usb.org/sites/default/files/hid1_11.pdf>
23const HID_REQ_SET_IDLE: u8 = 0x0a;
24
25/// Requests the active protocol on the device (boot or report)
26/// See (7.2.5): <https://www.usb.org/sites/default/files/hid1_11.pdf>
27const HID_REQ_GET_PROTOCOL: u8 = 0x03;
28
29/// Switches the device between boot and report protocols. Devices must default
30/// to report protocol, it is the reponsibility of the host to set the device
31/// to boot protocol (NOTE: Sadly many OSs, BIOSs and bootloaders do not adhere
32/// to the USB spec here).
33/// See (7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf>
34const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
35
36/// Allows a host to receive a report via the Control pipe
37/// See (7.2.1): <https://www.usb.org/sites/default/files/hid1_11.pdf>
38const HID_REQ_GET_REPORT: u8 = 0x01;
39
40/// Allows the host to send a report to the device via the Control pipe
41/// See (7.2.2): <https://www.usb.org/sites/default/files/hid1_11.pdf>
42const HID_REQ_SET_REPORT: u8 = 0x09;
43
44/// See CONTROL_BUF_LEN from usb-device.git src/control_pipe.rs
45/// Will need to revisit how this is set once usb-device has true HiSpeed USB support.
46const CONTROL_BUF_LEN: usize = 128;
47
48#[derive(Copy, Clone, Debug, PartialEq, Eq)]
49#[cfg_attr(feature = "defmt", derive(defmt::Format))]
50pub enum ReportType {
51    Input = 1,
52    Output = 2,
53    Feature = 3,
54    Reserved,
55}
56
57impl From<u8> for ReportType {
58    fn from(rt: u8) -> ReportType {
59        match rt {
60            1 => ReportType::Input,
61            2 => ReportType::Output,
62            3 => ReportType::Feature,
63            _ => ReportType::Reserved,
64        }
65    }
66}
67
68#[derive(Copy, Clone, Debug)]
69#[cfg_attr(feature = "defmt", derive(defmt::Format))]
70pub struct ReportInfo {
71    pub report_type: ReportType,
72    pub report_id: u8,
73    pub len: usize,
74}
75
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77struct Report {
78    info: ReportInfo,
79    buf: [u8; CONTROL_BUF_LEN],
80}
81
82/// List of official USB HID country codes
83/// See (6.2.1): <https://www.usb.org/sites/default/files/hid1_11.pdf>
84#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86#[repr(u8)]
87pub enum HidCountryCode {
88    NotSupported = 0,
89    Arabic = 1,
90    Belgian = 2,
91    CanadianBilingual = 3,
92    CanadianFrench = 4,
93    CzechRepublic = 5,
94    Danish = 6,
95    Finnish = 7,
96    French = 8,
97    German = 9,
98    Greek = 10,
99    Hebrew = 11,
100    Hungary = 12,
101    InternationalISO = 13,
102    Italian = 14,
103    JapanKatakana = 15,
104    Korean = 16,
105    LatinAmerica = 17,
106    NetherlandsDutch = 18,
107    Norwegian = 19,
108    PersianFarsi = 20,
109    Poland = 21,
110    Portuguese = 22,
111    Russia = 23,
112    Slovakia = 24,
113    Spanish = 25,
114    Swedish = 26,
115    SwissFrench = 27,
116    SwissGerman = 28,
117    Switzerland = 29,
118    Taiwan = 30,
119    TurkishQ = 31,
120    UK = 32,
121    US = 33,
122    Yugoslavia = 34,
123    TurkishF = 35,
124}
125
126/// Used to enable Boot mode descriptors for Mouse and Keyboard devices.
127/// See (4.2): <https://www.usb.org/sites/default/files/hid1_11.pdf>
128/// Boot mode descriptors are fixed and must follow a strict format.
129/// See (Appendix F): <https://www.usb.org/sites/default/files/hid1_11.pdf>
130#[derive(Copy, Clone, Debug, PartialEq, Eq)]
131#[cfg_attr(feature = "defmt", derive(defmt::Format))]
132#[repr(u8)]
133pub enum HidSubClass {
134    NoSubClass = 0,
135    Boot = 1,
136}
137
138/// Defines fixed packet format
139/// Only used if HidSubClass::Boot(1) is set
140/// See (4.3): <https://www.usb.org/sites/default/files/hid1_11.pdf>
141#[derive(Copy, Clone, Debug, PartialEq, Eq)]
142#[cfg_attr(feature = "defmt", derive(defmt::Format))]
143#[repr(u8)]
144pub enum HidProtocol {
145    Generic = 0,
146    Keyboard = 1,
147    Mouse = 2,
148}
149
150/// Get/Set Protocol mapping
151/// See (7.2.5 and 7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf>
152#[derive(Copy, Clone, Debug, PartialEq, Eq)]
153#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154#[repr(u8)]
155pub enum HidProtocolMode {
156    Boot = 0,
157    Report = 1,
158}
159
160impl From<u8> for HidProtocolMode {
161    fn from(mode: u8) -> HidProtocolMode {
162        if mode == HidProtocolMode::Boot as u8 {
163            HidProtocolMode::Boot
164        } else {
165            HidProtocolMode::Report
166        }
167    }
168}
169
170/// It is often necessary to override OS behavior in order to get around OS (and application) level
171/// bugs. Forcing either Boot mode (6KRO) and Report mode (NKRO) are often necessary for NKRO
172/// compatible keyboards. Mice that support boot mode are not common and generally only useful for
173/// legacy OSs.
174#[derive(Copy, Clone, Debug, PartialEq, Eq)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176pub enum ProtocolModeConfig {
177    /// Allows the host to define boot or report mode. Defaults to report mode.
178    DefaultBehavior,
179    /// Forces protocol mode to boot mode
180    ForceBoot,
181    /// Forces protocol mode to report mode
182    ForceReport,
183}
184
185/// Used to define specialized HID device settings
186/// Most commonly used to setup Boot Mode (6KRO) or Report Mode (NKRO) keyboards.
187/// Some OSs will also respect the HID locale setting of the keyboard to help choose the OS
188/// keyboard layout.
189#[cfg_attr(feature = "defmt", derive(defmt::Format))]
190pub struct HidClassSettings {
191    pub subclass: HidSubClass,
192    pub protocol: HidProtocol,
193    pub config: ProtocolModeConfig,
194    pub locale: HidCountryCode,
195}
196
197impl Default for HidClassSettings {
198    fn default() -> Self {
199        Self {
200            subclass: HidSubClass::NoSubClass,
201            protocol: HidProtocol::Generic,
202            config: ProtocolModeConfig::DefaultBehavior,
203            locale: HidCountryCode::NotSupported,
204        }
205    }
206}
207
208/// HIDClass provides an interface to declare, read & write HID reports.
209///
210/// Users are expected to provide the report descriptor, as well as pack
211/// and unpack reports which are read or staged for transmission.
212pub struct HIDClass<'a, B: UsbBus> {
213    if_num: InterfaceNumber,
214    /// Low-latency OUT buffer
215    out_ep: Option<EndpointOut<'a, B>>,
216    /// Low-latency IN buffer
217    in_ep: Option<EndpointIn<'a, B>>,
218    report_descriptor: &'static [u8],
219    /// Control endpoint alternative OUT buffer (always used for setting feature reports)
220    /// See: <https://www.usb.org/sites/default/files/documents/hid1_11.pdf> 7.2.1 and 7.2.2
221    set_report_buf: Option<Report>,
222    /// Used only by Keyboard and Mouse to define BIOS (Boot) mode vs Normal (Report) mode.
223    /// This is used to switch between 6KRO (boot) and NKRO (report) endpoints.
224    /// Boot mode configured endpoints may not parse the hid descriptor and expect an exact
225    /// hid packet format. By default a device should start in normal (report) mode and the host
226    /// must request the boot mode protocol if it requires it.
227    ///
228    /// If a device does not request boot mode, this is a host bug. For convenience this API allows
229    /// manually setting the protocol.
230    /// See <https://www.usb.org/sites/default/files/hid1_11.pdf> Section 7.2.6
231    protocol: Option<HidProtocolMode>,
232    settings: HidClassSettings,
233}
234
235fn determine_protocol_setting(settings: &HidClassSettings) -> Option<HidProtocolMode> {
236    if settings.protocol == HidProtocol::Keyboard || settings.protocol == HidProtocol::Mouse {
237        match settings.config {
238            ProtocolModeConfig::DefaultBehavior | ProtocolModeConfig::ForceReport => {
239                Some(HidProtocolMode::Report)
240            }
241            ProtocolModeConfig::ForceBoot => Some(HidProtocolMode::Boot),
242        }
243    } else {
244        None
245    }
246}
247
248impl<B: UsbBus> HIDClass<'_, B> {
249    /// Creates a new HIDClass with the provided UsbBus & HID report descriptor.
250    ///
251    /// poll_ms configures how frequently the host should poll for reading/writing
252    /// HID reports. A lower value means better throughput & latency, at the expense
253    /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
254    /// high performance uses, and a value of 255 is good for best-effort usecases.
255    ///
256    /// This allocates two endpoints (IN and OUT).
257    /// See new_ep_in (IN endpoint only) and new_ep_out (OUT endpoint only) to only create a single
258    /// endpoint.
259    ///
260    /// See new_with_settings() if you need to define protocol or locale settings for a IN/OUT
261    /// HID interface.
262    pub fn new<'a>(
263        alloc: &'a UsbBusAllocator<B>,
264        report_descriptor: &'static [u8],
265        poll_ms: u8,
266    ) -> HIDClass<'a, B> {
267        let settings = HidClassSettings::default();
268        HIDClass {
269            if_num: alloc.interface(),
270            out_ep: Some(alloc.interrupt(64, poll_ms)),
271            in_ep: Some(alloc.interrupt(64, poll_ms)),
272            report_descriptor,
273            set_report_buf: None,
274            protocol: determine_protocol_setting(&settings),
275            settings,
276        }
277    }
278
279    /// Same as new() but includes a settings field.
280    /// The settings field is used to define both locale and protocol settings of the HID
281    /// device (needed for HID keyboard and Mice).
282    pub fn new_with_settings<'a>(
283        alloc: &'a UsbBusAllocator<B>,
284        report_descriptor: &'static [u8],
285        poll_ms: u8,
286        settings: HidClassSettings,
287    ) -> HIDClass<'a, B> {
288        HIDClass {
289            if_num: alloc.interface(),
290            out_ep: Some(alloc.interrupt(64, poll_ms)),
291            in_ep: Some(alloc.interrupt(64, poll_ms)),
292            report_descriptor,
293            set_report_buf: None,
294            protocol: determine_protocol_setting(&settings),
295            settings,
296        }
297    }
298
299    /// Creates a new HIDClass with the provided UsbBus & HID report descriptor.
300    /// See new() for more details.
301    /// Please use new_ep_in_with_settings() if you are creating a keyboard or mouse.
302    pub fn new_ep_in<'a>(
303        alloc: &'a UsbBusAllocator<B>,
304        report_descriptor: &'static [u8],
305        poll_ms: u8,
306    ) -> HIDClass<'a, B> {
307        let settings = HidClassSettings::default();
308        HIDClass {
309            if_num: alloc.interface(),
310            out_ep: None,
311            in_ep: Some(alloc.interrupt(64, poll_ms)),
312            report_descriptor,
313            set_report_buf: None,
314            protocol: determine_protocol_setting(&settings),
315            settings,
316        }
317    }
318
319    /// Same as new_ep_in() but includes a settings field.
320    /// The settings field is used to define both locale and protocol settings of the HID
321    /// device (needed for HID keyboard and Mice).
322    pub fn new_ep_in_with_settings<'a>(
323        alloc: &'a UsbBusAllocator<B>,
324        report_descriptor: &'static [u8],
325        poll_ms: u8,
326        settings: HidClassSettings,
327    ) -> HIDClass<'a, B> {
328        HIDClass {
329            if_num: alloc.interface(),
330            out_ep: None,
331            in_ep: Some(alloc.interrupt(64, poll_ms)),
332            report_descriptor,
333            set_report_buf: None,
334            protocol: determine_protocol_setting(&settings),
335            settings,
336        }
337    }
338
339    /// Creates a new HIDClass with the provided UsbBus & HID report descriptor.
340    /// See new() for more details.
341    /// Please use new_ep_out_with_settings if you need the settings field.
342    pub fn new_ep_out<'a>(
343        alloc: &'a UsbBusAllocator<B>,
344        report_descriptor: &'static [u8],
345        poll_ms: u8,
346    ) -> HIDClass<'a, B> {
347        let settings = HidClassSettings::default();
348        HIDClass {
349            if_num: alloc.interface(),
350            out_ep: Some(alloc.interrupt(64, poll_ms)),
351            in_ep: None,
352            report_descriptor,
353            set_report_buf: None,
354            protocol: determine_protocol_setting(&settings),
355            settings,
356        }
357    }
358
359    /// Same as new_ep_out() but includes a settings field.
360    /// This should be uncommon (non-standard), but is included for completeness as there
361    /// may be cases where setting the locale is useful.
362    pub fn new_ep_out_with_settings<'a>(
363        alloc: &'a UsbBusAllocator<B>,
364        report_descriptor: &'static [u8],
365        poll_ms: u8,
366        settings: HidClassSettings,
367    ) -> HIDClass<'a, B> {
368        HIDClass {
369            if_num: alloc.interface(),
370            out_ep: Some(alloc.interrupt(64, poll_ms)),
371            in_ep: None,
372            report_descriptor,
373            set_report_buf: None,
374            protocol: determine_protocol_setting(&settings),
375            settings,
376        }
377    }
378
379    /// Tries to write an input report by serializing the given report structure.
380    /// A BufferOverflow error is returned if the serialized report is greater than
381    /// 64 bytes in size.
382    pub fn push_input<IR: AsInputReport>(&self, r: &IR) -> Result<usize> {
383        // Do not push data if protocol settings do not match (only for keyboard and mouse)
384        match self.settings.protocol {
385            HidProtocol::Keyboard | HidProtocol::Mouse => {
386                if let Some(protocol) = self.protocol {
387                    if (protocol == HidProtocolMode::Report
388                        && self.settings.subclass != HidSubClass::NoSubClass)
389                        || (protocol == HidProtocolMode::Boot
390                            && self.settings.subclass != HidSubClass::Boot)
391                    {
392                        return Err(UsbError::InvalidState);
393                    }
394                }
395            }
396            _ => {}
397        }
398
399        if let Some(ep) = &self.in_ep {
400            let mut buff: [u8; 64] = [0; 64];
401            let size = match serialize(&mut buff, r) {
402                Ok(l) => l,
403                Err(_) => return Err(UsbError::BufferOverflow),
404            };
405            ep.write(&buff[0..size])
406        } else {
407            Err(UsbError::InvalidEndpoint)
408        }
409    }
410
411    /// Tries to write an input (device-to-host) report from the given raw bytes.
412    /// Data is expected to be a valid HID report for INPUT items. If report ID's
413    /// were used in the descriptor, the report ID corresponding to this report
414    /// must be be present before the contents of the report.
415    pub fn push_raw_input(&self, data: &[u8]) -> Result<usize> {
416        // Do not push data if protocol settings do not match (only for keyboard and mouse)
417        match self.settings.protocol {
418            HidProtocol::Keyboard | HidProtocol::Mouse => {
419                if let Some(protocol) = self.protocol {
420                    if (protocol == HidProtocolMode::Report
421                        && self.settings.subclass != HidSubClass::NoSubClass)
422                        || (protocol == HidProtocolMode::Boot
423                            && self.settings.subclass != HidSubClass::Boot)
424                    {
425                        return Err(UsbError::InvalidState);
426                    }
427                }
428            }
429            _ => {}
430        }
431
432        if let Some(ep) = &self.in_ep {
433            ep.write(data)
434        } else {
435            Err(UsbError::InvalidEndpoint)
436        }
437    }
438
439    /// Tries to read an output (host-to-device) report as raw bytes. Data
440    /// is expected to be sized appropriately to contain any valid HID report
441    /// for OUTPUT items, including the report ID prefix if report IDs are used.
442    pub fn pull_raw_output(&self, data: &mut [u8]) -> Result<usize> {
443        if let Some(ep) = &self.out_ep {
444            ep.read(data)
445        } else {
446            Err(UsbError::InvalidEndpoint)
447        }
448    }
449
450    /// Tries to read an incoming SET_REPORT report as raw bytes.
451    /// Unlike OUT endpoints, report IDs are not prefixed in the buffer. Use the returned tuple
452    /// instead to determine the buffer's usage.
453    ///
454    /// The most common usage of pull_raw_report is for keyboard lock LED status if an OUT endpoint
455    /// is not defined. It is not necessary to call this function if you're not going to be using
456    /// SET_REPORT functionality.
457    pub fn pull_raw_report(&mut self, data: &mut [u8]) -> Result<ReportInfo> {
458        let info = match &self.set_report_buf {
459            Some(set_report_buf) => {
460                let info = set_report_buf.info;
461
462                // Make sure the given buffer is large enough for the stored report
463                if data.len() < info.len {
464                    return Err(UsbError::BufferOverflow);
465                }
466
467                // Copy buffer
468                data[..info.len].copy_from_slice(&set_report_buf.buf[..info.len]);
469                info
470            }
471            None => {
472                return Err(UsbError::WouldBlock);
473            }
474        };
475
476        // Clear the report
477        self.set_report_buf = None;
478        Ok(info)
479    }
480
481    /// Retrieves the currently set device protocol
482    /// This is equivalent to the USB HID GET_PROTOCOL request
483    /// See (7.2.5): <https://www.usb.org/sites/default/files/hid1_11.pdf>
484    pub fn get_protocol_mode(&self) -> Result<HidProtocolMode> {
485        // Protocol mode only has meaning if Keyboard or Mouse Protocol is set
486        match self.settings.protocol {
487            HidProtocol::Keyboard | HidProtocol::Mouse => {}
488            _ => {
489                return Err(UsbError::Unsupported);
490            }
491        }
492
493        if let Some(protocol) = self.protocol {
494            Ok(protocol)
495        } else {
496            Err(UsbError::InvalidState)
497        }
498    }
499
500    /// Forcibly sets the device protocol
501    /// This is equivalent to the USB HID SET_PROTOCOL request.
502    /// NOTE: If the OS does not support the new mode, the device may no longer work correctly.
503    /// See (7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf>
504    ///
505    /// If either, ForceBoot or ForceReport are set in config, the mode argument is ignored.
506    /// In addition, if ForceBoot or ForceReport are set, then any SET_PROTOCOL requests are also
507    /// ignored.
508    pub fn set_protocol_mode(
509        &mut self,
510        mode: HidProtocolMode,
511        config: ProtocolModeConfig,
512    ) -> Result<()> {
513        // Protocol mode only has meaning if Keyboard or Mouse Protocol is set
514        match self.settings.protocol {
515            HidProtocol::Keyboard | HidProtocol::Mouse => {}
516            _ => {
517                return Err(UsbError::Unsupported);
518            }
519        }
520
521        // Update the protocol setting behavior and update the protocol mode
522        match config {
523            ProtocolModeConfig::DefaultBehavior => self.protocol = Some(mode),
524            ProtocolModeConfig::ForceBoot => {
525                self.protocol = Some(HidProtocolMode::Boot);
526            }
527            ProtocolModeConfig::ForceReport => {
528                self.protocol = Some(HidProtocolMode::Report);
529            }
530        }
531        self.settings.config = config;
532        Ok(())
533    }
534}
535
536impl<B: UsbBus> UsbClass<B> for HIDClass<'_, B> {
537    fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
538        writer.interface(
539            self.if_num,
540            USB_CLASS_HID,
541            self.settings.subclass as u8,
542            self.settings.protocol as u8,
543        )?;
544
545        // HID descriptor
546        writer.write(
547            HID_DESC_DESCTYPE_HID,
548            &[
549                // HID Class spec version
550                HID_DESC_SPEC_1_10[0],
551                HID_DESC_SPEC_1_10[1],
552                self.settings.locale as u8,
553                // Number of following descriptors
554                1,
555                // We have a HID report descriptor the host should read
556                HID_DESC_DESCTYPE_HID_REPORT,
557                // HID report descriptor size,
558                (self.report_descriptor.len() & 0xFF) as u8,
559                (self.report_descriptor.len() >> 8 & 0xFF) as u8,
560            ],
561        )?;
562
563        if let Some(ep) = &self.out_ep {
564            writer.endpoint(ep)?;
565        }
566        if let Some(ep) = &self.in_ep {
567            writer.endpoint(ep)?;
568        }
569        Ok(())
570    }
571
572    // Handle control requests to the host.
573    fn control_in(&mut self, xfer: ControlIn<B>) {
574        let req = xfer.request();
575
576        // Bail out if its not relevant to our interface.
577        if req.index != u8::from(self.if_num) as u16 {
578            return;
579        }
580
581        match (req.request_type, req.request) {
582            (control::RequestType::Standard, control::Request::GET_DESCRIPTOR) => {
583                match (req.value >> 8) as u8 {
584                    HID_DESC_DESCTYPE_HID_REPORT => {
585                        xfer.accept_with_static(self.report_descriptor).ok();
586                    }
587                    HID_DESC_DESCTYPE_HID => {
588                        let buf = &[
589                            // Length of buf inclusive of size prefix
590                            9,
591                            // Descriptor type
592                            HID_DESC_DESCTYPE_HID,
593                            // HID Class spec version
594                            HID_DESC_SPEC_1_10[0],
595                            HID_DESC_SPEC_1_10[1],
596                            self.settings.locale as u8,
597                            // Number of following descriptors
598                            1,
599                            // We have a HID report descriptor the host should read
600                            HID_DESC_DESCTYPE_HID_REPORT,
601                            // HID report descriptor size,
602                            (self.report_descriptor.len() & 0xFF) as u8,
603                            (self.report_descriptor.len() >> 8 & 0xFF) as u8,
604                        ];
605                        xfer.accept_with(buf).ok();
606                    }
607                    _ => {}
608                }
609            }
610            (control::RequestType::Class, HID_REQ_GET_REPORT) => {
611                // To support GET_REPORT correctly each request must be serviced immediately.
612                // This complicates the current API and may require a standing copy of each
613                // of the possible IN reports (as well as any FEATURE reports as well).
614                // For most projects, GET_REPORT won't be necessary so until a project comes along
615                // with a need for it, I think it's safe to leave unsupported.
616                // See: https://www.usb.org/sites/default/files/documents/hid1_11.pdf 7.2.1
617                xfer.reject().ok(); // Not supported for now
618            }
619            (control::RequestType::Class, HID_REQ_GET_IDLE) => {
620                // XXX (HaaTa): As a note for future readers
621                // GET/SET_IDLE tends to be rather buggy on the host side
622                // macOS is known to set SET_IDLE for keyboards but most other OSs do not.
623                // I haven't had much success in the past trying to enable GET/SET_IDLE for
624                // macOS (it seems to expose other bugs in the macOS hid stack).
625                // The interesting part is that SET_IDLE is not called for official Apple
626                // keyboards. So beyond getting 100% compliance from the USB compliance tools
627                // IDLE is useless (at least with respect to keyboards). Modern USB host
628                // controllers should never have a problem keeping up with slow HID devices.
629                //
630                // To implement this correctly it would require integration with higher-level
631                // functions to handle report expiry.
632                // See https://www.usb.org/sites/default/files/documents/hid1_11.pdf 7.2.4
633                //
634                // Each Report ID can be configured independently.
635                xfer.reject().ok(); // Not supported for now
636            }
637            (control::RequestType::Class, HID_REQ_GET_PROTOCOL) => {
638                // Only accept in supported configurations
639                if let Some(protocol) = self.protocol {
640                    xfer.accept_with(&[protocol as u8]).ok();
641                } else {
642                    xfer.reject().ok();
643                }
644            }
645            _ => {}
646        }
647    }
648
649    // Handle a control request from the host.
650    fn control_out(&mut self, xfer: ControlOut<B>) {
651        let req = xfer.request();
652
653        // Bail out if its not relevant to our interface.
654        if !(req.recipient == control::Recipient::Interface
655            && req.index == u8::from(self.if_num) as u16)
656        {
657            return;
658        }
659
660        match req.request {
661            HID_REQ_SET_IDLE => {
662                xfer.accept().ok();
663            }
664            HID_REQ_SET_PROTOCOL => {
665                // Only accept in supported configurations
666                if let Some(_protocol) = self.protocol {
667                    // Only set if configured to
668                    if self.settings.config == ProtocolModeConfig::DefaultBehavior {
669                        self.protocol = Some(((req.value & 0xFF) as u8).into());
670                    }
671                    xfer.accept().ok();
672                } else {
673                    xfer.reject().ok();
674                }
675            }
676            HID_REQ_SET_REPORT => {
677                let report_type = ((req.value >> 8) as u8).into();
678                let report_id = (req.value & 0xFF) as u8;
679                let len = req.length as usize;
680
681                // Validate that the incoming data isn't too large for the buffer
682                if len > CONTROL_BUF_LEN {
683                    self.set_report_buf = None;
684                    xfer.reject().ok();
685                } else {
686                    let mut buf: [u8; CONTROL_BUF_LEN] = [0; CONTROL_BUF_LEN];
687                    buf[..len].copy_from_slice(&xfer.data()[..len]);
688
689                    // Overwrite previous buffer even if unused
690                    self.set_report_buf = Some(Report {
691                        info: ReportInfo {
692                            report_type,
693                            report_id,
694                            len,
695                        },
696                        buf,
697                    });
698                    xfer.accept().ok();
699                }
700            }
701            _ => {
702                xfer.reject().ok();
703            }
704        }
705    }
706}