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}