usbd_human_interface_device/device/
consumer.rs

1//!HID consumer control
2
3use fugit::ExtU32;
4use packed_struct::prelude::*;
5#[allow(clippy::wildcard_imports)]
6use usb_device::class_prelude::*;
7
8use crate::page::Consumer;
9use crate::usb_class::prelude::*;
10
11///Consumer control report descriptor - Four `u16` consumer control usage codes as an array (8 bytes)
12#[rustfmt::skip]
13pub const MULTIPLE_CODE_REPORT_DESCRIPTOR: &[u8] = &[
14    0x05, 0x0C, // Usage Page (Consumer),
15    0x09, 0x01, // Usage (Consumer Control),
16    0xA1, 0x01, // Collection (Application),
17    0x75, 0x10, //     Report Size(16)
18    0x95, 0x04, //     Report Count(4)
19    0x15, 0x00, //     Logical Minimum(0)
20    0x26, 0x9C, 0x02, //     Logical Maximum(0x029C)
21    0x19, 0x00, //     Usage Minimum(0)
22    0x2A, 0x9C, 0x02, //     Usage Maximum(0x029C)
23    0x81, 0x00, //     Input (Array, Data, Variable)
24    0xC0, // End Collection
25];
26
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
28#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
29#[packed_struct(endian = "lsb", size_bytes = "8")]
30pub struct MultipleConsumerReport {
31    #[packed_field(ty = "enum", element_size_bytes = "2")]
32    pub codes: [Consumer; 4],
33}
34
35#[allow(clippy::doc_markdown)]
36///Fixed functionality consumer control report descriptor
37/// 
38/// Based on [Logitech Gaming Keyboard]
39/// 
40/// Single bit packed `u8` report
41/// * Bit 0 - Scan Next Track
42/// * Bit 1 - Scan Previous Track
43/// * Bit 2 - Stop
44/// * Bit 3 - Play/Pause
45/// * Bit 4 - Mute
46/// * Bit 5 - Volume Increment
47/// * Bit 6 - Volume Decrement
48/// * Bit 7 - Reserved
49#[rustfmt::skip]
50pub const FIXED_FUNCTION_REPORT_DESCRIPTOR: &[u8] = &[
51    0x05, 0x0C, //        Usage Page (Consumer Devices)  
52    0x09, 0x01, //        Usage (Consumer Control)  
53    0xA1, 0x01, //        Collection (Application)  
54    0x05, 0x0C, //            Usage Page (Consumer Devices)  
55    0x15, 0x00, //            Logical Minimum (0)  
56    0x25, 0x01, //            Logical Maximum (1)  
57    0x75, 0x01, //            Report Size (1)  
58    0x95, 0x07, //            Report Count (7)  
59    0x09, 0xB5, //            Usage (Scan Next Track)  
60    0x09, 0xB6, //            Usage (Scan Previous Track)  
61    0x09, 0xB7, //            Usage (Stop)  
62    0x09, 0xCD, //            Usage (Play/Pause)  
63    0x09, 0xE2, //            Usage (Mute)  
64    0x09, 0xE9, //            Usage (Volume Increment)  
65    0x09, 0xEA, //            Usage (Volume Decrement)  
66    0x81, 0x02, //            Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)  
67    0x95, 0x01, //            Report Count (1)  
68    0x81, 0x01, //            Input (Const,Ary,Abs)  
69    0xC0, //        End Collection
70];
71
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73#[derive(Clone, Copy, Debug, Eq, PartialEq, PackedStruct)]
74#[packed_struct(endian = "lsb", bit_numbering = "lsb0", size_bytes = "1")]
75pub struct FixedFunctionReport {
76    #[packed_field(bits = "0")]
77    pub next: bool,
78    #[packed_field(bits = "1")]
79    pub previous: bool,
80    #[packed_field(bits = "2")]
81    pub stop: bool,
82    #[packed_field(bits = "3")]
83    pub play_pause: bool,
84    #[packed_field(bits = "4")]
85    pub mute: bool,
86    #[packed_field(bits = "5")]
87    pub volume_increment: bool,
88    #[packed_field(bits = "6")]
89    pub volume_decrement: bool,
90}
91
92pub struct ConsumerControl<'a, B: UsbBus> {
93    interface: Interface<'a, B, InBytes8, OutNone, ReportSingle>,
94}
95
96impl<B: UsbBus> ConsumerControl<'_, B> {
97    pub fn write_report(&mut self, report: &MultipleConsumerReport) -> usb_device::Result<usize> {
98        let data = report.pack().map_err(|_| {
99            error!("Error packing MultipleConsumerReport");
100            UsbError::ParseError
101        })?;
102        self.interface.write_report(&data)
103    }
104}
105
106impl<'a, B: UsbBus> DeviceClass<'a> for ConsumerControl<'a, B> {
107    type I = Interface<'a, B, InBytes8, OutNone, ReportSingle>;
108
109    fn interface(&mut self) -> &mut Self::I {
110        &mut self.interface
111    }
112
113    fn reset(&mut self) {}
114
115    fn tick(&mut self) -> Result<(), crate::UsbHidError> {
116        Ok(())
117    }
118}
119
120pub struct ConsumerControlConfig<'a> {
121    interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>,
122}
123
124impl<'a> ConsumerControlConfig<'a> {
125    #[must_use]
126    pub fn new(interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>) -> Self {
127        Self { interface }
128    }
129}
130
131impl Default for ConsumerControlConfig<'_> {
132    fn default() -> Self {
133        Self::new(
134            unwrap!(
135                unwrap!(InterfaceBuilder::new(MULTIPLE_CODE_REPORT_DESCRIPTOR))
136                    .description("Consumer Control")
137                    .in_endpoint(50.millis())
138            )
139            .without_out_endpoint()
140            .build(),
141        )
142    }
143}
144
145impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for ConsumerControlConfig<'a> {
146    type Allocated = ConsumerControl<'a, B>;
147
148    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
149        Self::Allocated {
150            interface: Interface::new(usb_alloc, self.interface),
151        }
152    }
153}
154
155pub struct ConsumerControlFixed<'a, B: UsbBus> {
156    interface: Interface<'a, B, InBytes8, OutNone, ReportSingle>,
157}
158
159impl<B: UsbBus> ConsumerControlFixed<'_, B> {
160    pub fn write_report(&mut self, report: &FixedFunctionReport) -> usb_device::Result<usize> {
161        let data = report.pack().map_err(|_| {
162            error!("Error packing MultipleConsumerReport");
163            UsbError::ParseError
164        })?;
165        self.interface.write_report(&data)
166    }
167}
168
169impl<'a, B: UsbBus> DeviceClass<'a> for ConsumerControlFixed<'a, B> {
170    type I = Interface<'a, B, InBytes8, OutNone, ReportSingle>;
171
172    fn interface(&mut self) -> &mut Self::I {
173        &mut self.interface
174    }
175
176    fn reset(&mut self) {}
177
178    fn tick(&mut self) -> Result<(), crate::UsbHidError> {
179        Ok(())
180    }
181}
182
183pub struct ConsumerControlFixedConfig<'a> {
184    interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>,
185}
186impl<'a> ConsumerControlFixedConfig<'a> {
187    #[must_use]
188    pub fn new(interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>) -> Self {
189        Self { interface }
190    }
191}
192
193impl Default for ConsumerControlFixedConfig<'_> {
194    fn default() -> Self {
195        Self::new(
196            unwrap!(
197                unwrap!(InterfaceBuilder::new(FIXED_FUNCTION_REPORT_DESCRIPTOR))
198                    .description("Consumer Control")
199                    .in_endpoint(50.millis())
200            )
201            .without_out_endpoint()
202            .build(),
203        )
204    }
205}
206
207impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for ConsumerControlFixedConfig<'a> {
208    type Allocated = ConsumerControlFixed<'a, B>;
209
210    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
211        Self::Allocated {
212            interface: Interface::new(usb_alloc, self.interface),
213        }
214    }
215}