aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-usb')
-rw-r--r--embassy-usb/CHANGELOG.md2
-rw-r--r--embassy-usb/src/class/hid.rs101
2 files changed, 92 insertions, 11 deletions
diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md
index 0a30bc24b..cfb1bf021 100644
--- a/embassy-usb/CHANGELOG.md
+++ b/embassy-usb/CHANGELOG.md
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Add support for USB HID Boot Protocol Mode
12
11## 0.5.1 - 2025-08-26 13## 0.5.1 - 2025-08-26
12 14
13## 0.5.0 - 2025-07-16 15## 0.5.0 - 2025-07-16
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs
index 182e1f83f..64e8fd59f 100644
--- a/embassy-usb/src/class/hid.rs
+++ b/embassy-usb/src/class/hid.rs
@@ -15,8 +15,6 @@ use crate::types::InterfaceNumber;
15use crate::{Builder, Handler}; 15use crate::{Builder, Handler};
16 16
17const USB_CLASS_HID: u8 = 0x03; 17const USB_CLASS_HID: u8 = 0x03;
18const USB_SUBCLASS_NONE: u8 = 0x00;
19const USB_PROTOCOL_NONE: u8 = 0x00;
20 18
21// HID 19// HID
22const HID_DESC_DESCTYPE_HID: u8 = 0x21; 20const HID_DESC_DESCTYPE_HID: u8 = 0x21;
@@ -31,6 +29,52 @@ const HID_REQ_SET_REPORT: u8 = 0x09;
31const HID_REQ_GET_PROTOCOL: u8 = 0x03; 29const HID_REQ_GET_PROTOCOL: u8 = 0x03;
32const HID_REQ_SET_PROTOCOL: u8 = 0x0b; 30const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
33 31
32/// Get/Set Protocol mapping
33/// See (7.2.5 and 7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf>
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[repr(u8)]
37pub enum HidProtocolMode {
38 /// Hid Boot Protocol Mode
39 Boot = 0,
40 /// Hid Report Protocol Mode
41 Report = 1,
42}
43
44impl From<u8> for HidProtocolMode {
45 fn from(mode: u8) -> HidProtocolMode {
46 if mode == HidProtocolMode::Boot as u8 {
47 HidProtocolMode::Boot
48 } else {
49 HidProtocolMode::Report
50 }
51 }
52}
53
54/// USB HID interface subclass values.
55#[derive(Copy, Clone, Debug, PartialEq, Eq)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57#[repr(u8)]
58pub enum HidSubclass {
59 /// No subclass, standard HID device.
60 No = 0,
61 /// Boot interface subclass, supports BIOS boot protocol.
62 Boot = 1,
63}
64
65/// USB HID protocol values.
66#[derive(Copy, Clone, Debug, PartialEq, Eq)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68#[repr(u8)]
69pub enum HidBootProtocol {
70 /// No boot protocol.
71 None = 0,
72 /// Keyboard boot protocol.
73 Keyboard = 1,
74 /// Mouse boot protocol.
75 Mouse = 2,
76}
77
34/// Configuration for the HID class. 78/// Configuration for the HID class.
35pub struct Config<'d> { 79pub struct Config<'d> {
36 /// HID report descriptor. 80 /// HID report descriptor.
@@ -48,6 +92,12 @@ pub struct Config<'d> {
48 92
49 /// Max packet size for both the IN and OUT endpoints. 93 /// Max packet size for both the IN and OUT endpoints.
50 pub max_packet_size: u16, 94 pub max_packet_size: u16,
95
96 /// The HID subclass of this interface
97 pub hid_subclass: HidSubclass,
98
99 /// The HID boot protocol of this interface
100 pub hid_boot_protocol: HidBootProtocol,
51} 101}
52 102
53/// Report ID 103/// Report ID
@@ -109,10 +159,15 @@ fn build<'d, D: Driver<'d>>(
109) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { 159) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {
110 let len = config.report_descriptor.len(); 160 let len = config.report_descriptor.len();
111 161
112 let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); 162 let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8);
113 let mut iface = func.interface(); 163 let mut iface = func.interface();
114 let if_num = iface.interface_number(); 164 let if_num = iface.interface_number();
115 let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); 165 let mut alt = iface.alt_setting(
166 USB_CLASS_HID,
167 config.hid_subclass as u8,
168 config.hid_boot_protocol as u8,
169 None,
170 );
116 171
117 // HID descriptor 172 // HID descriptor
118 alt.descriptor( 173 alt.descriptor(
@@ -389,6 +444,23 @@ pub trait RequestHandler {
389 OutResponse::Rejected 444 OutResponse::Rejected
390 } 445 }
391 446
447 /// Gets the current hid protocol.
448 ///
449 /// Returns `Report` protocol by default.
450 fn get_protocol(&self) -> HidProtocolMode {
451 HidProtocolMode::Report
452 }
453
454 /// Sets the current hid protocol to `protocol`.
455 ///
456 /// Accepts only `Report` protocol by default.
457 fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {
458 match protocol {
459 HidProtocolMode::Report => OutResponse::Accepted,
460 HidProtocolMode::Boot => OutResponse::Rejected,
461 }
462 }
463
392 /// Get the idle rate for `id`. 464 /// Get the idle rate for `id`.
393 /// 465 ///
394 /// If `id` is `None`, get the idle rate for all reports. Returning `None` 466 /// If `id` is `None`, get the idle rate for all reports. Returning `None`
@@ -482,11 +554,14 @@ impl<'d> Handler for Control<'d> {
482 _ => Some(OutResponse::Rejected), 554 _ => Some(OutResponse::Rejected),
483 }, 555 },
484 HID_REQ_SET_PROTOCOL => { 556 HID_REQ_SET_PROTOCOL => {
485 if req.value == 1 { 557 let hid_protocol = HidProtocolMode::from(req.value as u8);
486 Some(OutResponse::Accepted) 558 match (self.request_handler.as_mut(), hid_protocol) {
487 } else { 559 (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)),
488 warn!("HID Boot Protocol is unsupported."); 560 (None, HidProtocolMode::Report) => Some(OutResponse::Accepted),
489 Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol 561 (None, HidProtocolMode::Boot) => {
562 info!("Received request to switch to Boot protocol mode, but it is disabled by default.");
563 Some(OutResponse::Rejected)
564 }
490 } 565 }
491 } 566 }
492 _ => Some(OutResponse::Rejected), 567 _ => Some(OutResponse::Rejected),
@@ -539,8 +614,12 @@ impl<'d> Handler for Control<'d> {
539 } 614 }
540 } 615 }
541 HID_REQ_GET_PROTOCOL => { 616 HID_REQ_GET_PROTOCOL => {
542 // UNSUPPORTED: Boot Protocol 617 if let Some(request_handler) = self.request_handler.as_mut() {
543 buf[0] = 1; 618 buf[0] = request_handler.get_protocol() as u8;
619 } else {
620 // Return `Report` protocol mode by default
621 buf[0] = HidProtocolMode::Report as u8;
622 }
544 Some(InResponse::Accepted(&buf[0..1])) 623 Some(InResponse::Accepted(&buf[0..1]))
545 } 624 }
546 _ => Some(InResponse::Rejected), 625 _ => Some(InResponse::Rejected),