diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-09-26 13:00:21 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-09-26 13:00:21 +0200 |
| commit | f27a47a37b59bf3b9079f4d4d5f43caf7b7872f8 (patch) | |
| tree | 732f73b4da7a2e726203f2876651a2141d9468be /embassy-usb/src/class/hid.rs | |
| parent | f4f58249722bc656a13865e06535d208440c3e4a (diff) | |
usb: move classes into the `embassy-usb` crate.
Diffstat (limited to 'embassy-usb/src/class/hid.rs')
| -rw-r--r-- | embassy-usb/src/class/hid.rs | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs new file mode 100644 index 000000000..4d1fa995f --- /dev/null +++ b/embassy-usb/src/class/hid.rs | |||
| @@ -0,0 +1,504 @@ | |||
| 1 | use core::mem::MaybeUninit; | ||
| 2 | use core::ops::Range; | ||
| 3 | use core::sync::atomic::{AtomicUsize, Ordering}; | ||
| 4 | |||
| 5 | #[cfg(feature = "usbd-hid")] | ||
| 6 | use ssmarshal::serialize; | ||
| 7 | #[cfg(feature = "usbd-hid")] | ||
| 8 | use usbd_hid::descriptor::AsInputReport; | ||
| 9 | |||
| 10 | use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; | ||
| 11 | use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; | ||
| 12 | use crate::Builder; | ||
| 13 | |||
| 14 | const USB_CLASS_HID: u8 = 0x03; | ||
| 15 | const USB_SUBCLASS_NONE: u8 = 0x00; | ||
| 16 | const USB_PROTOCOL_NONE: u8 = 0x00; | ||
| 17 | |||
| 18 | // HID | ||
| 19 | const HID_DESC_DESCTYPE_HID: u8 = 0x21; | ||
| 20 | const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22; | ||
| 21 | const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01]; | ||
| 22 | const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00; | ||
| 23 | |||
| 24 | const HID_REQ_SET_IDLE: u8 = 0x0a; | ||
| 25 | const HID_REQ_GET_IDLE: u8 = 0x02; | ||
| 26 | const HID_REQ_GET_REPORT: u8 = 0x01; | ||
| 27 | const HID_REQ_SET_REPORT: u8 = 0x09; | ||
| 28 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; | ||
| 29 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; | ||
| 30 | |||
| 31 | pub struct Config<'d> { | ||
| 32 | /// HID report descriptor. | ||
| 33 | pub report_descriptor: &'d [u8], | ||
| 34 | |||
| 35 | /// Handler for control requests. | ||
| 36 | pub request_handler: Option<&'d dyn RequestHandler>, | ||
| 37 | |||
| 38 | /// Configures how frequently the host should poll for reading/writing HID reports. | ||
| 39 | /// | ||
| 40 | /// A lower value means better throughput & latency, at the expense | ||
| 41 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for | ||
| 42 | /// high performance uses, and a value of 255 is good for best-effort usecases. | ||
| 43 | pub poll_ms: u8, | ||
| 44 | |||
| 45 | /// Max packet size for both the IN and OUT endpoints. | ||
| 46 | pub max_packet_size: u16, | ||
| 47 | } | ||
| 48 | |||
| 49 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 50 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 51 | pub enum ReportId { | ||
| 52 | In(u8), | ||
| 53 | Out(u8), | ||
| 54 | Feature(u8), | ||
| 55 | } | ||
| 56 | |||
| 57 | impl ReportId { | ||
| 58 | fn try_from(value: u16) -> Result<Self, ()> { | ||
| 59 | match value >> 8 { | ||
| 60 | 1 => Ok(ReportId::In(value as u8)), | ||
| 61 | 2 => Ok(ReportId::Out(value as u8)), | ||
| 62 | 3 => Ok(ReportId::Feature(value as u8)), | ||
| 63 | _ => Err(()), | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | pub struct State<'d> { | ||
| 69 | control: MaybeUninit<Control<'d>>, | ||
| 70 | out_report_offset: AtomicUsize, | ||
| 71 | } | ||
| 72 | |||
| 73 | impl<'d> State<'d> { | ||
| 74 | pub fn new() -> Self { | ||
| 75 | State { | ||
| 76 | control: MaybeUninit::uninit(), | ||
| 77 | out_report_offset: AtomicUsize::new(0), | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { | ||
| 83 | reader: HidReader<'d, D, READ_N>, | ||
| 84 | writer: HidWriter<'d, D, WRITE_N>, | ||
| 85 | } | ||
| 86 | |||
| 87 | fn build<'d, D: Driver<'d>>( | ||
| 88 | builder: &mut Builder<'d, D>, | ||
| 89 | state: &'d mut State<'d>, | ||
| 90 | config: Config<'d>, | ||
| 91 | with_out_endpoint: bool, | ||
| 92 | ) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { | ||
| 93 | let control = state.control.write(Control::new( | ||
| 94 | config.report_descriptor, | ||
| 95 | config.request_handler, | ||
| 96 | &state.out_report_offset, | ||
| 97 | )); | ||
| 98 | |||
| 99 | let len = config.report_descriptor.len(); | ||
| 100 | |||
| 101 | let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); | ||
| 102 | let mut iface = func.interface(); | ||
| 103 | iface.handler(control); | ||
| 104 | let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); | ||
| 105 | |||
| 106 | // HID descriptor | ||
| 107 | alt.descriptor( | ||
| 108 | HID_DESC_DESCTYPE_HID, | ||
| 109 | &[ | ||
| 110 | // HID Class spec version | ||
| 111 | HID_DESC_SPEC_1_10[0], | ||
| 112 | HID_DESC_SPEC_1_10[1], | ||
| 113 | // Country code not supported | ||
| 114 | HID_DESC_COUNTRY_UNSPEC, | ||
| 115 | // Number of following descriptors | ||
| 116 | 1, | ||
| 117 | // We have a HID report descriptor the host should read | ||
| 118 | HID_DESC_DESCTYPE_HID_REPORT, | ||
| 119 | // HID report descriptor size, | ||
| 120 | (len & 0xFF) as u8, | ||
| 121 | (len >> 8 & 0xFF) as u8, | ||
| 122 | ], | ||
| 123 | ); | ||
| 124 | |||
| 125 | let ep_in = alt.endpoint_interrupt_in(config.max_packet_size, config.poll_ms); | ||
| 126 | let ep_out = if with_out_endpoint { | ||
| 127 | Some(alt.endpoint_interrupt_out(config.max_packet_size, config.poll_ms)) | ||
| 128 | } else { | ||
| 129 | None | ||
| 130 | }; | ||
| 131 | |||
| 132 | (ep_out, ep_in, &state.out_report_offset) | ||
| 133 | } | ||
| 134 | |||
| 135 | impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWriter<'d, D, READ_N, WRITE_N> { | ||
| 136 | /// Creates a new HidReaderWriter. | ||
| 137 | /// | ||
| 138 | /// This will allocate one IN and one OUT endpoints. If you only need writing (sending) | ||
| 139 | /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. | ||
| 140 | /// | ||
| 141 | pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { | ||
| 142 | let (ep_out, ep_in, offset) = build(builder, state, config, true); | ||
| 143 | |||
| 144 | Self { | ||
| 145 | reader: HidReader { | ||
| 146 | ep_out: ep_out.unwrap(), | ||
| 147 | offset, | ||
| 148 | }, | ||
| 149 | writer: HidWriter { ep_in }, | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Splits into seperate readers/writers for input and output reports. | ||
| 154 | pub fn split(self) -> (HidReader<'d, D, READ_N>, HidWriter<'d, D, WRITE_N>) { | ||
| 155 | (self.reader, self.writer) | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Waits for both IN and OUT endpoints to be enabled. | ||
| 159 | pub async fn ready(&mut self) -> () { | ||
| 160 | self.reader.ready().await; | ||
| 161 | self.writer.ready().await; | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Writes an input report by serializing the given report structure. | ||
| 165 | #[cfg(feature = "usbd-hid")] | ||
| 166 | pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> { | ||
| 167 | self.writer.write_serialize(r).await | ||
| 168 | } | ||
| 169 | |||
| 170 | /// Writes `report` to its interrupt endpoint. | ||
| 171 | pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> { | ||
| 172 | self.writer.write(report).await | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Reads an output report from the Interrupt Out pipe. | ||
| 176 | /// | ||
| 177 | /// See [`HidReader::read`]. | ||
| 178 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> { | ||
| 179 | self.reader.read(buf).await | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { | ||
| 184 | ep_in: D::EndpointIn, | ||
| 185 | } | ||
| 186 | |||
| 187 | pub struct HidReader<'d, D: Driver<'d>, const N: usize> { | ||
| 188 | ep_out: D::EndpointOut, | ||
| 189 | offset: &'d AtomicUsize, | ||
| 190 | } | ||
| 191 | |||
| 192 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 193 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 194 | pub enum ReadError { | ||
| 195 | BufferOverflow, | ||
| 196 | Disabled, | ||
| 197 | Sync(Range<usize>), | ||
| 198 | } | ||
| 199 | |||
| 200 | impl From<EndpointError> for ReadError { | ||
| 201 | fn from(val: EndpointError) -> Self { | ||
| 202 | use EndpointError::*; | ||
| 203 | match val { | ||
| 204 | BufferOverflow => ReadError::BufferOverflow, | ||
| 205 | Disabled => ReadError::Disabled, | ||
| 206 | } | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> { | ||
| 211 | /// Creates a new HidWriter. | ||
| 212 | /// | ||
| 213 | /// This will allocate one IN endpoint only, so the host won't be able to send | ||
| 214 | /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead. | ||
| 215 | /// | ||
| 216 | /// poll_ms configures how frequently the host should poll for reading/writing | ||
| 217 | /// HID reports. A lower value means better throughput & latency, at the expense | ||
| 218 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for | ||
| 219 | /// high performance uses, and a value of 255 is good for best-effort usecases. | ||
| 220 | pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { | ||
| 221 | let (ep_out, ep_in, _offset) = build(builder, state, config, false); | ||
| 222 | |||
| 223 | assert!(ep_out.is_none()); | ||
| 224 | |||
| 225 | Self { ep_in } | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Waits for the interrupt in endpoint to be enabled. | ||
| 229 | pub async fn ready(&mut self) -> () { | ||
| 230 | self.ep_in.wait_enabled().await | ||
| 231 | } | ||
| 232 | |||
| 233 | /// Writes an input report by serializing the given report structure. | ||
| 234 | #[cfg(feature = "usbd-hid")] | ||
| 235 | pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> { | ||
| 236 | let mut buf: [u8; N] = [0; N]; | ||
| 237 | let size = match serialize(&mut buf, r) { | ||
| 238 | Ok(size) => size, | ||
| 239 | Err(_) => return Err(EndpointError::BufferOverflow), | ||
| 240 | }; | ||
| 241 | self.write(&buf[0..size]).await | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Writes `report` to its interrupt endpoint. | ||
| 245 | pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> { | ||
| 246 | assert!(report.len() <= N); | ||
| 247 | |||
| 248 | let max_packet_size = usize::from(self.ep_in.info().max_packet_size); | ||
| 249 | let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0); | ||
| 250 | for chunk in report.chunks(max_packet_size) { | ||
| 251 | self.ep_in.write(chunk).await?; | ||
| 252 | } | ||
| 253 | |||
| 254 | if zlp_needed { | ||
| 255 | self.ep_in.write(&[]).await?; | ||
| 256 | } | ||
| 257 | |||
| 258 | Ok(()) | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> { | ||
| 263 | /// Waits for the interrupt out endpoint to be enabled. | ||
| 264 | pub async fn ready(&mut self) -> () { | ||
| 265 | self.ep_out.wait_enabled().await | ||
| 266 | } | ||
| 267 | |||
| 268 | /// Delivers output reports from the Interrupt Out pipe to `handler`. | ||
| 269 | /// | ||
| 270 | /// If `use_report_ids` is true, the first byte of the report will be used as | ||
| 271 | /// the `ReportId` value. Otherwise the `ReportId` value will be 0. | ||
| 272 | pub async fn run<T: RequestHandler>(mut self, use_report_ids: bool, handler: &T) -> ! { | ||
| 273 | let offset = self.offset.load(Ordering::Acquire); | ||
| 274 | assert!(offset == 0); | ||
| 275 | let mut buf = [0; N]; | ||
| 276 | loop { | ||
| 277 | match self.read(&mut buf).await { | ||
| 278 | Ok(len) => { | ||
| 279 | let id = if use_report_ids { buf[0] } else { 0 }; | ||
| 280 | handler.set_report(ReportId::Out(id), &buf[..len]); | ||
| 281 | } | ||
| 282 | Err(ReadError::BufferOverflow) => warn!( | ||
| 283 | "Host sent output report larger than the configured maximum output report length ({})", | ||
| 284 | N | ||
| 285 | ), | ||
| 286 | Err(ReadError::Disabled) => self.ep_out.wait_enabled().await, | ||
| 287 | Err(ReadError::Sync(_)) => unreachable!(), | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Reads an output report from the Interrupt Out pipe. | ||
| 293 | /// | ||
| 294 | /// **Note:** Any reports sent from the host over the control pipe will be | ||
| 295 | /// passed to [`RequestHandler::set_report()`] for handling. The application | ||
| 296 | /// is responsible for ensuring output reports from both pipes are handled | ||
| 297 | /// correctly. | ||
| 298 | /// | ||
| 299 | /// **Note:** If `N` > the maximum packet size of the endpoint (i.e. output | ||
| 300 | /// reports may be split across multiple packets) and this method's future | ||
| 301 | /// is dropped after some packets have been read, the next call to `read()` | ||
| 302 | /// will return a [`ReadError::SyncError()`]. The range in the sync error | ||
| 303 | /// indicates the portion `buf` that was filled by the current call to | ||
| 304 | /// `read()`. If the dropped future used the same `buf`, then `buf` will | ||
| 305 | /// contain the full report. | ||
| 306 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> { | ||
| 307 | assert!(N != 0); | ||
| 308 | assert!(buf.len() >= N); | ||
| 309 | |||
| 310 | // Read packets from the endpoint | ||
| 311 | let max_packet_size = usize::from(self.ep_out.info().max_packet_size); | ||
| 312 | let starting_offset = self.offset.load(Ordering::Acquire); | ||
| 313 | let mut total = starting_offset; | ||
| 314 | loop { | ||
| 315 | for chunk in buf[starting_offset..N].chunks_mut(max_packet_size) { | ||
| 316 | match self.ep_out.read(chunk).await { | ||
| 317 | Ok(size) => { | ||
| 318 | total += size; | ||
| 319 | if size < max_packet_size || total == N { | ||
| 320 | self.offset.store(0, Ordering::Release); | ||
| 321 | break; | ||
| 322 | } else { | ||
| 323 | self.offset.store(total, Ordering::Release); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | Err(err) => { | ||
| 327 | self.offset.store(0, Ordering::Release); | ||
| 328 | return Err(err.into()); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | // Some hosts may send ZLPs even when not required by the HID spec, so we'll loop as long as total == 0. | ||
| 334 | if total > 0 { | ||
| 335 | break; | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | if starting_offset > 0 { | ||
| 340 | Err(ReadError::Sync(starting_offset..total)) | ||
| 341 | } else { | ||
| 342 | Ok(total) | ||
| 343 | } | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | pub trait RequestHandler { | ||
| 348 | /// Reads the value of report `id` into `buf` returning the size. | ||
| 349 | /// | ||
| 350 | /// Returns `None` if `id` is invalid or no data is available. | ||
| 351 | fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> { | ||
| 352 | let _ = (id, buf); | ||
| 353 | None | ||
| 354 | } | ||
| 355 | |||
| 356 | /// Sets the value of report `id` to `data`. | ||
| 357 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 358 | let _ = (id, data); | ||
| 359 | OutResponse::Rejected | ||
| 360 | } | ||
| 361 | |||
| 362 | /// Get the idle rate for `id`. | ||
| 363 | /// | ||
| 364 | /// If `id` is `None`, get the idle rate for all reports. Returning `None` | ||
| 365 | /// will reject the control request. Any duration at or above 1.024 seconds | ||
| 366 | /// or below 4ms will be returned as an indefinite idle rate. | ||
| 367 | fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { | ||
| 368 | let _ = id; | ||
| 369 | None | ||
| 370 | } | ||
| 371 | |||
| 372 | /// Set the idle rate for `id` to `dur`. | ||
| 373 | /// | ||
| 374 | /// If `id` is `None`, set the idle rate of all input reports to `dur`. If | ||
| 375 | /// an indefinite duration is requested, `dur` will be set to `u32::MAX`. | ||
| 376 | fn set_idle_ms(&self, id: Option<ReportId>, duration_ms: u32) { | ||
| 377 | let _ = (id, duration_ms); | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | struct Control<'d> { | ||
| 382 | report_descriptor: &'d [u8], | ||
| 383 | request_handler: Option<&'d dyn RequestHandler>, | ||
| 384 | out_report_offset: &'d AtomicUsize, | ||
| 385 | hid_descriptor: [u8; 9], | ||
| 386 | } | ||
| 387 | |||
| 388 | impl<'d> Control<'d> { | ||
| 389 | fn new( | ||
| 390 | report_descriptor: &'d [u8], | ||
| 391 | request_handler: Option<&'d dyn RequestHandler>, | ||
| 392 | out_report_offset: &'d AtomicUsize, | ||
| 393 | ) -> Self { | ||
| 394 | Control { | ||
| 395 | report_descriptor, | ||
| 396 | request_handler, | ||
| 397 | out_report_offset, | ||
| 398 | hid_descriptor: [ | ||
| 399 | // Length of buf inclusive of size prefix | ||
| 400 | 9, | ||
| 401 | // Descriptor type | ||
| 402 | HID_DESC_DESCTYPE_HID, | ||
| 403 | // HID Class spec version | ||
| 404 | HID_DESC_SPEC_1_10[0], | ||
| 405 | HID_DESC_SPEC_1_10[1], | ||
| 406 | // Country code not supported | ||
| 407 | HID_DESC_COUNTRY_UNSPEC, | ||
| 408 | // Number of following descriptors | ||
| 409 | 1, | ||
| 410 | // We have a HID report descriptor the host should read | ||
| 411 | HID_DESC_DESCTYPE_HID_REPORT, | ||
| 412 | // HID report descriptor size, | ||
| 413 | (report_descriptor.len() & 0xFF) as u8, | ||
| 414 | (report_descriptor.len() >> 8 & 0xFF) as u8, | ||
| 415 | ], | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | impl<'d> ControlHandler for Control<'d> { | ||
| 421 | fn reset(&mut self) { | ||
| 422 | self.out_report_offset.store(0, Ordering::Release); | ||
| 423 | } | ||
| 424 | |||
| 425 | fn get_descriptor<'a>(&'a mut self, req: Request, _buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 426 | match (req.value >> 8) as u8 { | ||
| 427 | HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), | ||
| 428 | HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), | ||
| 429 | _ => InResponse::Rejected, | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { | ||
| 434 | trace!("HID control_out {:?} {=[u8]:x}", req, data); | ||
| 435 | if let RequestType::Class = req.request_type { | ||
| 436 | match req.request { | ||
| 437 | HID_REQ_SET_IDLE => { | ||
| 438 | if let Some(handler) = self.request_handler { | ||
| 439 | let id = req.value as u8; | ||
| 440 | let id = (id != 0).then(|| ReportId::In(id)); | ||
| 441 | let dur = u32::from(req.value >> 8); | ||
| 442 | let dur = if dur == 0 { u32::MAX } else { 4 * dur }; | ||
| 443 | handler.set_idle_ms(id, dur); | ||
| 444 | } | ||
| 445 | OutResponse::Accepted | ||
| 446 | } | ||
| 447 | HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { | ||
| 448 | (Ok(id), Some(handler)) => handler.set_report(id, data), | ||
| 449 | _ => OutResponse::Rejected, | ||
| 450 | }, | ||
| 451 | HID_REQ_SET_PROTOCOL => { | ||
| 452 | if req.value == 1 { | ||
| 453 | OutResponse::Accepted | ||
| 454 | } else { | ||
| 455 | warn!("HID Boot Protocol is unsupported."); | ||
| 456 | OutResponse::Rejected // UNSUPPORTED: Boot Protocol | ||
| 457 | } | ||
| 458 | } | ||
| 459 | _ => OutResponse::Rejected, | ||
| 460 | } | ||
| 461 | } else { | ||
| 462 | OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 467 | trace!("HID control_in {:?}", req); | ||
| 468 | match req.request { | ||
| 469 | HID_REQ_GET_REPORT => { | ||
| 470 | let size = match ReportId::try_from(req.value) { | ||
| 471 | Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), | ||
| 472 | Err(_) => None, | ||
| 473 | }; | ||
| 474 | |||
| 475 | if let Some(size) = size { | ||
| 476 | InResponse::Accepted(&buf[0..size]) | ||
| 477 | } else { | ||
| 478 | InResponse::Rejected | ||
| 479 | } | ||
| 480 | } | ||
| 481 | HID_REQ_GET_IDLE => { | ||
| 482 | if let Some(handler) = self.request_handler { | ||
| 483 | let id = req.value as u8; | ||
| 484 | let id = (id != 0).then(|| ReportId::In(id)); | ||
| 485 | if let Some(dur) = handler.get_idle_ms(id) { | ||
| 486 | let dur = u8::try_from(dur / 4).unwrap_or(0); | ||
| 487 | buf[0] = dur; | ||
| 488 | InResponse::Accepted(&buf[0..1]) | ||
| 489 | } else { | ||
| 490 | InResponse::Rejected | ||
| 491 | } | ||
| 492 | } else { | ||
| 493 | InResponse::Rejected | ||
| 494 | } | ||
| 495 | } | ||
| 496 | HID_REQ_GET_PROTOCOL => { | ||
| 497 | // UNSUPPORTED: Boot Protocol | ||
| 498 | buf[0] = 1; | ||
| 499 | InResponse::Accepted(&buf[0..1]) | ||
| 500 | } | ||
| 501 | _ => InResponse::Rejected, | ||
| 502 | } | ||
| 503 | } | ||
| 504 | } | ||
