rpk_firmware/usb.rs
1use core::{
2 mem::MaybeUninit,
3 sync::atomic::{AtomicUsize, Ordering},
4};
5use embassy_usb::{
6 class::hid::{ReportId, RequestHandler},
7 control::{InResponse, OutResponse, Recipient, Request, RequestType},
8 driver::Driver,
9 types::InterfaceNumber,
10 Builder, Config, Handler,
11};
12
13use crate::hid::{HidReader, HidWriter};
14
15// HID
16const HID_DESC_DESCTYPE_HID: u8 = 0x21;
17const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
18const HID_DESC_SPEC_1_11: [u8; 2] = [0x11, 0x01];
19const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00;
20
21const HID_REQ_SET_IDLE: u8 = 0x0a;
22const HID_REQ_GET_IDLE: u8 = 0x02;
23const HID_REQ_GET_REPORT: u8 = 0x01;
24const HID_REQ_SET_REPORT: u8 = 0x09;
25const HID_REQ_GET_PROTOCOL: u8 = 0x03;
26const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
27
28#[rustfmt::skip]
29pub const SHARED_REPORT_DESC: [u8; 59 + 73 + 25 + 25] = [
30 // NKRO_DESC [u8; 59]
31 0x05, 0x01, // (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
32 0x09, 0x06, // (LOCAL) USAGE 0x00010006 Keyboard (Application Collection)
33 0xA1, 0x01, // (MAIN) COLLECTION 0x01 Application (Usage=0x00010006: Page=Generic Desktop Page,
34 // Usage=Keyboard, Type=Application Collection)
35 0x85, 0x06, // (GLOBAL) REPORT_ID 0x06 (6)
36 0x05, 0x07, // (GLOBAL) USAGE_PAGE 0x0007 Keyboard/Keypad Page
37 0x19, 0xE0, // (LOCAL) USAGE_MINIMUM 0x000700E0 Keyboard LeftControl (Dynamic Value)
38 0x29, 0xE7, // (LOCAL) USAGE_MAXIMUM 0x000700E7 Keyboard Right GUI (Dynamic Value)
39 0x15, 0x00, // (GLOBAL) LOGICAL_MINIMUM 0x00 (0)
40 0x25, 0x01, // (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
41 0x95, 0x08, // (GLOBAL) REPORT_COUNT 0x08 (8) Number of fields
42 0x75, 0x01, // (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
43 0x81, 0x02, // (MAIN) INPUT 0x00000002 (8 fields x 1 bit) 0=Data 1=Variable 0=Absolute
44 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
45 0x05, 0x07, // (GLOBAL) USAGE_PAGE 0x0007 Keyboard/Keypad Page
46 0x19, 0x00, // (LOCAL) USAGE_MINIMUM 0x00070000 Keyboard No event indicated (Selector)
47 0x29, 0xFE, // (LOCAL) USAGE_MAXIMUM 0x000700FE
48 0x15, 0x00, // (GLOBAL) LOGICAL_MINIMUM 0x00 (0)
49 0x25, 0x01, // (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
50 0x95, 0xFF, // (GLOBAL) REPORT_COUNT 0xFF (255) Number of fields
51 0x75, 0x01, // (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
52 0x81, 0x02, // (MAIN) INPUT 0x00000002 (255 fields x 1 bit) 0=Data 1=Variable 0=Absolute
53 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
54 0x05, 0x08, // (GLOBAL) USAGE_PAGE 0x0008 LED Page
55 0x19, 0x01, // (LOCAL) USAGE_MINIMUM 0x00080001 Num Lock (On/Off Control)
56 0x29, 0x05, // (LOCAL) USAGE_MAXIMUM 0x00080005 Kana (On/Off Control)
57 0x95, 0x05, // (GLOBAL) REPORT_COUNT 0x05 (5) Number of fields
58 0x75, 0x01, // (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
59 0x91, 0x02, // (MAIN) OUTPUT 0x00000002 (5 fields x 1 bit) 0=Data 1=Variable 0=Absolute
60 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
61 0x95, 0x01, // (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
62 0x75, 0x03, // (GLOBAL) REPORT_SIZE 0x03 (3) Number of bits per field
63 0x91, 0x01, // (MAIN) OUTPUT 0x00000001 (1 field x 3 bits) 1=Constant 0=Array 0=Absolute
64 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
65 0xC0, // (MAIN) END_COLLECTION Application
66
67 // MOUSE_DESC [u8; 73]
68 0x05, 0x01, // (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
69 0x09, 0x02, // (LOCAL) USAGE 0x00010002 Mouse (Application Collection)
70 0xA1, 0x01, // (MAIN) COLLECTION 0x01 Application (Usage=0x00010002: Page=Generic Desktop Page,
71 // Usage=Mouse, Type=Application Collection)
72 0x85, 0x02, // (GLOBAL) REPORT_ID 0x02 (2)
73 0x09, 0x01, // (LOCAL) USAGE 0x00010001 Pointer (Physical Collection)
74 0xA1, 0x00, // (MAIN) COLLECTION 0x00 Physical (Usage=0x00010001: Page=Generic Desktop Page,
75 // Usage=Pointer, Type=Physical Collection)
76 0x05, 0x09, // (GLOBAL) USAGE_PAGE 0x0009 Button Page
77 0x19, 0x01, // (LOCAL) USAGE_MINIMUM 0x00090001 Button 1 Primary/trigger (Selector, On/Off
78 // Control, Momentary Control, or One Shot Control)
79 0x29, 0x08, // (LOCAL) USAGE_MAXIMUM 0x00090008 Button 8 (Selector, On/Off Control,
80 // Momentary Control, or One Shot Control)
81 0x15, 0x00, // (GLOBAL) LOGICAL_MINIMUM 0x00 (0)
82 0x25, 0x01, // (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
83 0x95, 0x08, // (GLOBAL) REPORT_COUNT 0x08 (8) Number of fields
84 0x75, 0x01, // (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
85 0x81, 0x02, // (MAIN) INPUT 0x00000002 (8 fields x 1 bit) 0=Data 1=Variable 0=Absolute
86 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
87 0x05, 0x01, // (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
88 0x09, 0x30, // (LOCAL) USAGE 0x00010030 X (Dynamic Value)
89 0x09, 0x31, // (LOCAL) USAGE 0x00010031 Y (Dynamic Value)
90 0x15, 0x81, // (GLOBAL) LOGICAL_MINIMUM 0x81 (-127)
91 0x25, 0x7F, // (GLOBAL) LOGICAL_MAXIMUM 0x7F (127)
92 0x95, 0x02, // (GLOBAL) REPORT_COUNT 0x02 (2) Number of fields
93 0x75, 0x08, // (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
94 0x81, 0x06, // (MAIN) INPUT 0x00000006 (2 fields x 8 bits) 0=Data 1=Variable 1=Relative
95 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
96 0x09, 0x38, // (LOCAL) USAGE 0x00010038 Wheel (Dynamic Value)
97 0x15, 0x81, // (GLOBAL) LOGICAL_MINIMUM 0x81 (-127)
98 0x25, 0x7F, // (GLOBAL) LOGICAL_MAXIMUM 0x7F (127)
99 0x95, 0x01, // (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
100 0x75, 0x08, // (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
101 0x81, 0x06, // (MAIN) INPUT 0x00000006 (1 field x 8 bits) 0=Data 1=Variable 1=Relative
102 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
103 0x05, 0x0C, // (GLOBAL) USAGE_PAGE 0x000C Consumer Page
104 0x0A, 0x38, 0x02,//(LOCAL) USAGE 0x000C0238 AC Pan (Linear Control)
105 0x15, 0x81, // (GLOBAL) LOGICAL_MINIMUM 0x81 (-127)
106 0x25, 0x7F, // (GLOBAL) LOGICAL_MAXIMUM 0x7F (127)
107 0x95, 0x01, // (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
108 0x75, 0x08, // (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
109 0x81, 0x06, // (MAIN) INPUT 0x00000006 (1 field x 8 bits) 0=Data 1=Variable 1=Relative
110 // 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
111 0xC0, // (MAIN) END_COLLECTION Physical
112 0xC0, // (MAIN) END_COLLECTION Application
113
114 // SYS_CTL_DESC: [u8; 25]
115 0x05, 0x01, // (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
116 0x09, 0x80, // (LOCAL) USAGE 0x00010080 System Control (Application Collection)
117 0xA1, 0x01, // (MAIN) COLLECTION 0x01 Application (Usage=0x00010080: Page=Generic Desktop Page,
118 // Usage=System Control, Type=Application Collection)
119 0x85, 0x03, // (GLOBAL) REPORT_ID 0x03 (3)
120 0x19, 0x01, // (LOCAL) USAGE_MINIMUM 0x00010001 Pointer (Physical Collection)
121 0x2A, 0xB7,0,// (LOCAL) USAGE_MAXIMUM 0x000100B7 System Display Toggle LCD Autoscale (One Shot Control)
122 0x15, 0x01, // (GLOBAL) LOGICAL_MINIMUM 0x01 (1)
123 0x26, 0xB7,0,// (GLOBAL) LOGICAL_MAXIMUM 0x00B7 (183)
124 0x95, 0x01, // (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
125 0x75, 0x10, // (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
126 0x81, 0x00, // (MAIN) INPUT 0x00000000 (1 field x 16 bits) 0=Data 0=Array 0=Absolute
127 0xC0, // (MAIN) END_COLLECTION Application
128
129
130 // CONSUMER_CTL_DESC: [u8; 25]
131 0x05, 0x0C, // (GLOBAL) USAGE_PAGE 0x000C Consumer Page
132 0x09, 0x01, // (LOCAL) USAGE 0x000C0001 Consumer Control (Application Collection)
133 0xA1, 0x01, // (MAIN) COLLECTION 0x01 Application (Usage=0x000C0001: Page=Consumer Page,
134 // Usage=Consumer Control, Type=Application Collection)
135 0x85, 0x04, // (GLOBAL) REPORT_ID 0x04 (4)
136 0x19, 0x01, // (LOCAL) USAGE_MINIMUM 0x000C0001 Consumer Control (Application Collection)
137 0x2A, 0xA0,2,// (LOCAL) USAGE_MAXIMUM 0x000C02A0 AC Soft Key Left (Selector)
138 0x15, 0x01, // (GLOBAL) LOGICAL_MINIMUM 0x01 (1)
139 0x26, 0xA0,2,// (GLOBAL) LOGICAL_MAXIMUM 0x02A0 (672)
140 0x95, 0x01, // (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
141 0x75, 0x10, // (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
142 0x81, 0x00, // (MAIN) INPUT 0x00000000 (1 field x 16 bits) 0=Data 0=Array 0=Absolute
143 0xC0, // (MAIN) END_COLLECTION Application
144];
145
146/// Internal state for USB HID.
147pub struct State<'d> {
148 control: MaybeUninit<Control<'d>>,
149 out_report_offset: AtomicUsize,
150}
151impl Default for State<'_> {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156impl State<'_> {
157 /// Create a new `State`.
158 pub const fn new() -> Self {
159 State {
160 control: MaybeUninit::uninit(),
161 out_report_offset: AtomicUsize::new(0),
162 }
163 }
164}
165
166const CONFIG_SIZE: usize = 128;
167const BOS_SIZE: usize = 32;
168const MSOS_SIZE: usize = 0;
169const CONTROL_SIZE: usize = 256;
170
171pub struct UsbBuffers {
172 config_descriptor_buf: [u8; CONFIG_SIZE],
173 bos_descriptor_buf: [u8; BOS_SIZE],
174 msos_descriptor_buf: [u8; MSOS_SIZE],
175 control_buf: [u8; CONTROL_SIZE],
176}
177
178impl Default for UsbBuffers {
179 fn default() -> Self {
180 Self {
181 config_descriptor_buf: [0; CONFIG_SIZE],
182 bos_descriptor_buf: [0; BOS_SIZE],
183 msos_descriptor_buf: [0; MSOS_SIZE],
184 control_buf: [0; CONTROL_SIZE],
185 }
186 }
187}
188
189pub struct Configurator<'d> {
190 device_config: Option<Config<'d>>,
191 max_packet_size: u16,
192 poll_ms: u8,
193}
194
195impl<'d> Configurator<'d> {
196 pub fn new(device_config: Config<'d>) -> Self {
197 Self {
198 device_config: Some(device_config),
199 max_packet_size: device_config.max_packet_size_0 as u16,
200 poll_ms: 1,
201 }
202 }
203
204 pub fn usb_builder<D: Driver<'d>>(
205 &mut self,
206 driver: D,
207 buffers: &'d mut UsbBuffers,
208 ) -> Option<Builder<'d, D>> {
209 // return a tuple with the builder and the buffers which we create here; not in new func
210
211 self.device_config.take().map(|device_config| {
212 Builder::new(
213 driver,
214 device_config,
215 &mut buffers.config_descriptor_buf,
216 &mut buffers.bos_descriptor_buf,
217 &mut buffers.msos_descriptor_buf,
218 &mut buffers.control_buf,
219 )
220 })
221 }
222
223 pub fn add_iface<'a, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize>(
224 &'d self,
225 builder: &'a mut Builder<'d, D>,
226 descriptor: &'static [u8],
227 need_reader: bool,
228 subclass: u8,
229 protocol: u8,
230 state: &'d mut State<'d>,
231 ) -> (HidWriter<'d, D, WRITE_N>, Option<HidReader<'d, D, READ_N>>) {
232 let mut func = builder.function(3, subclass, protocol);
233 let mut iface = func.interface();
234 let if_num = iface.interface_number();
235 let mut alt = iface.alt_setting(3, subclass, protocol, None);
236
237 let len = descriptor.len();
238 alt.descriptor(
239 HID_DESC_DESCTYPE_HID,
240 &[
241 0x11, // HID Class spec Version
242 0x01, //
243 0, // Country code not supported
244 1, // Number of following descriptors
245 34, // We have a HID report descriptor the host should read
246 (len & 0xFF) as u8, // HID report descriptor size,
247 (len >> 8 & 0xFF) as u8,
248 ],
249 );
250
251 let ep_in = alt.endpoint_interrupt_in(self.max_packet_size, self.poll_ms);
252 let ep_out = if need_reader {
253 Some(alt.endpoint_interrupt_out(self.max_packet_size, self.poll_ms))
254 } else {
255 None
256 };
257
258 drop(func);
259
260 let control = Control::new(
261 if_num,
262 descriptor,
263 None, // TODO &self.request_handler,
264 &state.out_report_offset,
265 );
266 let control = state.control.write(control);
267 builder.handler(control);
268 (
269 HidWriter::new(ep_in),
270 ep_out.map(|ep_out| HidReader::new(ep_out, &state.out_report_offset)),
271 )
272 }
273}
274
275struct Control<'d> {
276 if_num: InterfaceNumber,
277 report_descriptor: &'d [u8],
278 request_handler: Option<&'d mut dyn RequestHandler>,
279 out_report_offset: &'d AtomicUsize,
280 hid_descriptor: [u8; 9],
281}
282impl<'d> Control<'d> {
283 fn new(
284 if_num: InterfaceNumber,
285 report_descriptor: &'d [u8],
286 request_handler: Option<&'d mut dyn RequestHandler>,
287 out_report_offset: &'d AtomicUsize,
288 ) -> Self {
289 Control {
290 if_num,
291 report_descriptor,
292 request_handler,
293 out_report_offset,
294 hid_descriptor: [
295 9, // Length of buf inclusive of size prefix
296 HID_DESC_DESCTYPE_HID, // Descriptor type
297 HID_DESC_SPEC_1_11[0], // HID Class spec version
298 HID_DESC_SPEC_1_11[1], //
299 HID_DESC_COUNTRY_UNSPEC, // Country code not supported
300 1, // Number of following descriptors
301 HID_DESC_DESCTYPE_HID_REPORT, // We have a HID report descriptor the host should read
302 (report_descriptor.len() & 0xFF) as u8, // HID report descriptor size,
303 (report_descriptor.len() >> 8 & 0xFF) as u8, //
304 ],
305 }
306 }
307}
308impl Handler for Control<'_> {
309 fn reset(&mut self) {
310 self.out_report_offset.store(0, Ordering::Release);
311 }
312
313 fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {
314 if (req.request_type, req.recipient, req.index)
315 != (
316 RequestType::Class,
317 Recipient::Interface,
318 self.if_num.0 as u16,
319 )
320 {
321 return None;
322 }
323
324 match req.request {
325 HID_REQ_SET_IDLE => {
326 // How often we should send the keyboard state
327 if let Some(handler) = self.request_handler.as_mut() {
328 let id = req.value as u8;
329 let id = (id != 0).then_some(ReportId::In(id));
330 let dur = u32::from(req.value >> 8);
331 let dur = if dur == 0 { u32::MAX } else { 4 * dur };
332 handler.set_idle_ms(id, dur);
333 }
334 Some(OutResponse::Accepted)
335 }
336 HID_REQ_SET_REPORT => {
337 match (report_id_try_from(req.value), self.request_handler.as_mut()) {
338 (Ok(id), Some(handler)) => Some(handler.set_report(id, data)),
339 _ => Some(OutResponse::Rejected),
340 }
341 }
342 HID_REQ_SET_PROTOCOL => {
343 if req.value == 1 {
344 Some(OutResponse::Accepted)
345 } else {
346 crate::warn!("HID Boot Protocol is unsupported.");
347 Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol
348 }
349 }
350 _ => Some(OutResponse::Rejected),
351 }
352 }
353
354 fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
355 if req.index != self.if_num.0 as u16 {
356 return None;
357 }
358
359 match (req.request_type, req.recipient) {
360 (RequestType::Standard, Recipient::Interface) => match req.request {
361 Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 {
362 HID_DESC_DESCTYPE_HID_REPORT => {
363 Some(InResponse::Accepted(self.report_descriptor))
364 }
365 HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)),
366 _ => Some(InResponse::Rejected),
367 },
368
369 _ => Some(InResponse::Rejected),
370 },
371 (RequestType::Class, Recipient::Interface) => {
372 match req.request {
373 HID_REQ_GET_REPORT => {
374 let size = match report_id_try_from(req.value) {
375 Ok(id) => self
376 .request_handler
377 .as_mut()
378 .and_then(|x| x.get_report(id, buf)),
379 Err(_) => None,
380 };
381
382 if let Some(size) = size {
383 Some(InResponse::Accepted(&buf[0..size]))
384 } else {
385 Some(InResponse::Rejected)
386 }
387 }
388 HID_REQ_GET_IDLE => {
389 if let Some(handler) = self.request_handler.as_mut() {
390 let id = req.value as u8;
391 let id = (id != 0).then_some(ReportId::In(id));
392 if let Some(dur) = handler.get_idle_ms(id) {
393 let dur = u8::try_from(dur / 4).unwrap_or(0);
394 buf[0] = dur;
395 Some(InResponse::Accepted(&buf[0..1]))
396 } else {
397 Some(InResponse::Rejected)
398 }
399 } else {
400 Some(InResponse::Rejected)
401 }
402 }
403 HID_REQ_GET_PROTOCOL => {
404 // UNSUPPORTED: Boot Protocol
405 buf[0] = 1;
406 Some(InResponse::Accepted(&buf[0..1]))
407 }
408 _ => Some(InResponse::Rejected),
409 }
410 }
411 _ => None,
412 }
413 }
414}
415
416const fn report_id_try_from(value: u16) -> Result<ReportId, ()> {
417 match value >> 8 {
418 1 => Ok(ReportId::In(value as u8)),
419 2 => Ok(ReportId::Out(value as u8)),
420 3 => Ok(ReportId::Feature(value as u8)),
421 _ => Err(()),
422 }
423}