diff options
| -rw-r--r-- | embassy-usb-hid/src/lib.rs | 98 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_hid.rs | 11 |
2 files changed, 43 insertions, 66 deletions
diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index 14aadbc8c..0c449fa26 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs | |||
| @@ -60,39 +60,26 @@ impl ReportId { | |||
| 60 | } | 60 | } |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | pub struct State<'a, const IN_N: usize, const OUT_N: usize, const FEATURE_N: usize> { | 63 | pub struct State<'a, const IN_N: usize, const OUT_N: usize> { |
| 64 | control: MaybeUninit<Control<'a, OUT_N, FEATURE_N>>, | 64 | control: MaybeUninit<Control<'a, OUT_N>>, |
| 65 | out_signal: Signal<(usize, [u8; OUT_N])>, | 65 | out_signal: Signal<(usize, [u8; OUT_N])>, |
| 66 | feature_signal: Signal<(usize, [u8; FEATURE_N])>, | ||
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | impl<'a, const IN_N: usize, const OUT_N: usize, const FEATURE_N: usize> | 68 | impl<'a, const IN_N: usize, const OUT_N: usize> State<'a, IN_N, OUT_N> { |
| 70 | State<'a, IN_N, OUT_N, FEATURE_N> | ||
| 71 | { | ||
| 72 | pub fn new() -> Self { | 69 | pub fn new() -> Self { |
| 73 | State { | 70 | State { |
| 74 | control: MaybeUninit::uninit(), | 71 | control: MaybeUninit::uninit(), |
| 75 | out_signal: Signal::new(), | 72 | out_signal: Signal::new(), |
| 76 | feature_signal: Signal::new(), | ||
| 77 | } | 73 | } |
| 78 | } | 74 | } |
| 79 | } | 75 | } |
| 80 | 76 | ||
| 81 | pub struct HidClass< | 77 | pub struct HidClass<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> { |
| 82 | 'd, | ||
| 83 | D: Driver<'d>, | ||
| 84 | const IN_N: usize, | ||
| 85 | const OUT_N: usize, | ||
| 86 | const FEATURE_N: usize, | ||
| 87 | > { | ||
| 88 | input: ReportWriter<'d, D, IN_N>, | 78 | input: ReportWriter<'d, D, IN_N>, |
| 89 | output: ReportReader<'d, D, OUT_N>, | 79 | output: ReportReader<'d, D, OUT_N>, |
| 90 | feature: ReportReader<'d, D, FEATURE_N>, | ||
| 91 | } | 80 | } |
| 92 | 81 | ||
| 93 | impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: usize> | 82 | impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> HidClass<'d, D, IN_N, OUT_N> { |
| 94 | HidClass<'d, D, IN_N, OUT_N, FEATURE_N> | ||
| 95 | { | ||
| 96 | /// Creates a new HidClass. | 83 | /// Creates a new HidClass. |
| 97 | /// | 84 | /// |
| 98 | /// poll_ms configures how frequently the host should poll for reading/writing | 85 | /// poll_ms configures how frequently the host should poll for reading/writing |
| @@ -105,7 +92,7 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 105 | /// endpoint. | 92 | /// endpoint. |
| 106 | pub fn new( | 93 | pub fn new( |
| 107 | builder: &mut UsbDeviceBuilder<'d, D>, | 94 | builder: &mut UsbDeviceBuilder<'d, D>, |
| 108 | state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>, | 95 | state: &'d mut State<'d, IN_N, OUT_N>, |
| 109 | report_descriptor: &'static [u8], | 96 | report_descriptor: &'static [u8], |
| 110 | request_handler: Option<&'d dyn RequestHandler>, | 97 | request_handler: Option<&'d dyn RequestHandler>, |
| 111 | poll_ms: u8, | 98 | poll_ms: u8, |
| @@ -126,7 +113,7 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 126 | /// See new() for more details. | 113 | /// See new() for more details. |
| 127 | pub fn new_ep_in( | 114 | pub fn new_ep_in( |
| 128 | builder: &mut UsbDeviceBuilder<'d, D>, | 115 | builder: &mut UsbDeviceBuilder<'d, D>, |
| 129 | state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>, | 116 | state: &'d mut State<'d, IN_N, OUT_N>, |
| 130 | report_descriptor: &'static [u8], | 117 | report_descriptor: &'static [u8], |
| 131 | request_handler: Option<&'d dyn RequestHandler>, | 118 | request_handler: Option<&'d dyn RequestHandler>, |
| 132 | poll_ms: u8, | 119 | poll_ms: u8, |
| @@ -147,7 +134,7 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 147 | /// See new() for more details. | 134 | /// See new() for more details. |
| 148 | pub fn new_ep_out( | 135 | pub fn new_ep_out( |
| 149 | builder: &mut UsbDeviceBuilder<'d, D>, | 136 | builder: &mut UsbDeviceBuilder<'d, D>, |
| 150 | state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>, | 137 | state: &'d mut State<'d, IN_N, OUT_N>, |
| 151 | report_descriptor: &'static [u8], | 138 | report_descriptor: &'static [u8], |
| 152 | request_handler: Option<&'d dyn RequestHandler>, | 139 | request_handler: Option<&'d dyn RequestHandler>, |
| 153 | poll_ms: u8, | 140 | poll_ms: u8, |
| @@ -166,7 +153,7 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 166 | 153 | ||
| 167 | fn new_inner( | 154 | fn new_inner( |
| 168 | builder: &mut UsbDeviceBuilder<'d, D>, | 155 | builder: &mut UsbDeviceBuilder<'d, D>, |
| 169 | state: &'d mut State<'d, IN_N, OUT_N, FEATURE_N>, | 156 | state: &'d mut State<'d, IN_N, OUT_N>, |
| 170 | report_descriptor: &'static [u8], | 157 | report_descriptor: &'static [u8], |
| 171 | request_handler: Option<&'d dyn RequestHandler>, | 158 | request_handler: Option<&'d dyn RequestHandler>, |
| 172 | ep_out: Option<D::EndpointOut>, | 159 | ep_out: Option<D::EndpointOut>, |
| @@ -175,7 +162,6 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 175 | let control = state.control.write(Control::new( | 162 | let control = state.control.write(Control::new( |
| 176 | report_descriptor, | 163 | report_descriptor, |
| 177 | &state.out_signal, | 164 | &state.out_signal, |
| 178 | &state.feature_signal, | ||
| 179 | request_handler, | 165 | request_handler, |
| 180 | )); | 166 | )); |
| 181 | 167 | ||
| @@ -187,10 +173,6 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 187 | ep_out, | 173 | ep_out, |
| 188 | receiver: &state.out_signal, | 174 | receiver: &state.out_signal, |
| 189 | }, | 175 | }, |
| 190 | feature: ReportReader { | ||
| 191 | ep_out: None, | ||
| 192 | receiver: &state.feature_signal, | ||
| 193 | }, | ||
| 194 | } | 176 | } |
| 195 | } | 177 | } |
| 196 | 178 | ||
| @@ -207,20 +189,9 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize, const FEATURE_N: | |||
| 207 | &mut self.output | 189 | &mut self.output |
| 208 | } | 190 | } |
| 209 | 191 | ||
| 210 | /// Gets the [`ReportReader`] for feature reports. | 192 | /// Splits this `HidClass` into seperate readers/writers for input and output reports. |
| 211 | pub fn feature(&mut self) -> &mut ReportReader<'d, D, FEATURE_N> { | 193 | pub fn split(self) -> (ReportWriter<'d, D, IN_N>, ReportReader<'d, D, OUT_N>) { |
| 212 | &mut self.feature | 194 | (self.input, self.output) |
| 213 | } | ||
| 214 | |||
| 215 | /// Splits this `HidClass` into seperate readers/writers for each report type. | ||
| 216 | pub fn split( | ||
| 217 | self, | ||
| 218 | ) -> ( | ||
| 219 | ReportWriter<'d, D, IN_N>, | ||
| 220 | ReportReader<'d, D, OUT_N>, | ||
| 221 | ReportReader<'d, D, FEATURE_N>, | ||
| 222 | ) { | ||
| 223 | (self.input, self.output, self.feature) | ||
| 224 | } | 195 | } |
| 225 | } | 196 | } |
| 226 | 197 | ||
| @@ -314,7 +285,7 @@ impl<'d, D: Driver<'d>, const N: usize> ReportReader<'d, D, N> { | |||
| 314 | } | 285 | } |
| 315 | 286 | ||
| 316 | pub trait RequestHandler { | 287 | pub trait RequestHandler { |
| 317 | /// Read the value of report `id` into `buf` returning the size. | 288 | /// Reads the value of report `id` into `buf` returning the size. |
| 318 | /// | 289 | /// |
| 319 | /// Returns `None` if `id` is invalid or no data is available. | 290 | /// Returns `None` if `id` is invalid or no data is available. |
| 320 | fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> { | 291 | fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> { |
| @@ -322,12 +293,13 @@ pub trait RequestHandler { | |||
| 322 | None | 293 | None |
| 323 | } | 294 | } |
| 324 | 295 | ||
| 325 | /// Set the idle rate for `id` to `dur`. | 296 | /// Sets the value of report `id` to `data`. |
| 326 | /// | 297 | /// |
| 327 | /// If `id` is `None`, set the idle rate of all input reports to `dur`. If | 298 | /// This is only called for feature or input reports. Output reports |
| 328 | /// an indefinite duration is requested, `dur` will be set to `Duration::MAX`. | 299 | /// are routed through [`HidClass::output()`]. |
| 329 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | 300 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { |
| 330 | let _ = (id, dur); | 301 | let _ = (id, data); |
| 302 | OutResponse::Rejected | ||
| 331 | } | 303 | } |
| 332 | 304 | ||
| 333 | /// Get the idle rate for `id`. | 305 | /// Get the idle rate for `id`. |
| @@ -339,27 +311,32 @@ pub trait RequestHandler { | |||
| 339 | let _ = id; | 311 | let _ = id; |
| 340 | None | 312 | None |
| 341 | } | 313 | } |
| 314 | |||
| 315 | /// Set the idle rate for `id` to `dur`. | ||
| 316 | /// | ||
| 317 | /// If `id` is `None`, set the idle rate of all input reports to `dur`. If | ||
| 318 | /// an indefinite duration is requested, `dur` will be set to `Duration::MAX`. | ||
| 319 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | ||
| 320 | let _ = (id, dur); | ||
| 321 | } | ||
| 342 | } | 322 | } |
| 343 | 323 | ||
| 344 | pub struct Control<'d, const OUT_N: usize, const FEATURE_N: usize> { | 324 | pub struct Control<'d, const OUT_N: usize> { |
| 345 | report_descriptor: &'static [u8], | 325 | report_descriptor: &'static [u8], |
| 346 | out_signal: &'d Signal<(usize, [u8; OUT_N])>, | 326 | out_signal: &'d Signal<(usize, [u8; OUT_N])>, |
| 347 | feature_signal: &'d Signal<(usize, [u8; FEATURE_N])>, | ||
| 348 | request_handler: Option<&'d dyn RequestHandler>, | 327 | request_handler: Option<&'d dyn RequestHandler>, |
| 349 | hid_descriptor: [u8; 9], | 328 | hid_descriptor: [u8; 9], |
| 350 | } | 329 | } |
| 351 | 330 | ||
| 352 | impl<'a, const OUT_N: usize, const FEATURE_N: usize> Control<'a, OUT_N, FEATURE_N> { | 331 | impl<'a, const OUT_N: usize> Control<'a, OUT_N> { |
| 353 | fn new( | 332 | fn new( |
| 354 | report_descriptor: &'static [u8], | 333 | report_descriptor: &'static [u8], |
| 355 | out_signal: &'a Signal<(usize, [u8; OUT_N])>, | 334 | out_signal: &'a Signal<(usize, [u8; OUT_N])>, |
| 356 | feature_signal: &'a Signal<(usize, [u8; FEATURE_N])>, | ||
| 357 | request_handler: Option<&'a dyn RequestHandler>, | 335 | request_handler: Option<&'a dyn RequestHandler>, |
| 358 | ) -> Self { | 336 | ) -> Self { |
| 359 | Control { | 337 | Control { |
| 360 | report_descriptor, | 338 | report_descriptor, |
| 361 | out_signal, | 339 | out_signal, |
| 362 | feature_signal, | ||
| 363 | request_handler, | 340 | request_handler, |
| 364 | hid_descriptor: [ | 341 | hid_descriptor: [ |
| 365 | // Length of buf inclusive of size prefix | 342 | // Length of buf inclusive of size prefix |
| @@ -426,9 +403,7 @@ impl<'a, const OUT_N: usize, const FEATURE_N: usize> Control<'a, OUT_N, FEATURE_ | |||
| 426 | } | 403 | } |
| 427 | } | 404 | } |
| 428 | 405 | ||
| 429 | impl<'d, const OUT_N: usize, const FEATURE_N: usize> ControlHandler | 406 | impl<'d, const OUT_N: usize> ControlHandler for Control<'d, OUT_N> { |
| 430 | for Control<'d, OUT_N, FEATURE_N> | ||
| 431 | { | ||
| 432 | fn reset(&mut self) {} | 407 | fn reset(&mut self) {} |
| 433 | 408 | ||
| 434 | fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse { | 409 | fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse { |
| @@ -450,7 +425,6 @@ impl<'d, const OUT_N: usize, const FEATURE_N: usize> ControlHandler | |||
| 450 | OutResponse::Accepted | 425 | OutResponse::Accepted |
| 451 | } | 426 | } |
| 452 | HID_REQ_SET_REPORT => match ReportId::try_from(req.value) { | 427 | HID_REQ_SET_REPORT => match ReportId::try_from(req.value) { |
| 453 | Ok(ReportId::In(_)) => OutResponse::Rejected, | ||
| 454 | Ok(ReportId::Out(_id)) => { | 428 | Ok(ReportId::Out(_id)) => { |
| 455 | let mut buf = [0; OUT_N]; | 429 | let mut buf = [0; OUT_N]; |
| 456 | buf[0..data.len()].copy_from_slice(data); | 430 | buf[0..data.len()].copy_from_slice(data); |
| @@ -460,14 +434,12 @@ impl<'d, const OUT_N: usize, const FEATURE_N: usize> ControlHandler | |||
| 460 | self.out_signal.signal((data.len(), buf)); | 434 | self.out_signal.signal((data.len(), buf)); |
| 461 | OutResponse::Accepted | 435 | OutResponse::Accepted |
| 462 | } | 436 | } |
| 463 | Ok(ReportId::Feature(_id)) => { | 437 | Ok(id @ ReportId::Feature(_)) | Ok(id @ ReportId::In(_)) => { |
| 464 | let mut buf = [0; FEATURE_N]; | 438 | if let Some(handler) = self.request_handler.as_ref() { |
| 465 | buf[0..data.len()].copy_from_slice(data); | 439 | handler.set_report(id, data) |
| 466 | if self.feature_signal.signaled() { | 440 | } else { |
| 467 | warn!("Feature report dropped before being read!"); | 441 | OutResponse::Rejected |
| 468 | } | 442 | } |
| 469 | self.feature_signal.signal((data.len(), buf)); | ||
| 470 | OutResponse::Accepted | ||
| 471 | } | 443 | } |
| 472 | Err(_) => OutResponse::Rejected, | 444 | Err(_) => OutResponse::Rejected, |
| 473 | }, | 445 | }, |
diff --git a/examples/nrf/src/bin/usb_hid.rs b/examples/nrf/src/bin/usb_hid.rs index 1fd056d00..11c2d71ad 100644 --- a/examples/nrf/src/bin/usb_hid.rs +++ b/examples/nrf/src/bin/usb_hid.rs | |||
| @@ -14,6 +14,7 @@ use embassy_nrf::interrupt; | |||
| 14 | use embassy_nrf::pac; | 14 | use embassy_nrf::pac; |
| 15 | use embassy_nrf::usb::Driver; | 15 | use embassy_nrf::usb::Driver; |
| 16 | use embassy_nrf::Peripherals; | 16 | use embassy_nrf::Peripherals; |
| 17 | use embassy_usb::control::OutResponse; | ||
| 17 | use embassy_usb::{Config, UsbDeviceBuilder}; | 18 | use embassy_usb::{Config, UsbDeviceBuilder}; |
| 18 | use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; | 19 | use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; |
| 19 | use futures::future::join; | 20 | use futures::future::join; |
| @@ -51,7 +52,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 51 | let mut control_buf = [0; 16]; | 52 | let mut control_buf = [0; 16]; |
| 52 | let request_handler = MyRequestHandler {}; | 53 | let request_handler = MyRequestHandler {}; |
| 53 | 54 | ||
| 54 | let mut state = State::<5, 0, 0>::new(); | 55 | let mut state = State::<5, 0>::new(); |
| 55 | 56 | ||
| 56 | let mut builder = UsbDeviceBuilder::new( | 57 | let mut builder = UsbDeviceBuilder::new( |
| 57 | driver, | 58 | driver, |
| @@ -63,8 +64,7 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 63 | ); | 64 | ); |
| 64 | 65 | ||
| 65 | // Create classes on the builder. | 66 | // Create classes on the builder. |
| 66 | // let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | 67 | let mut hid = HidClass::new_ep_in( |
| 67 | let mut hid = HidClass::new( | ||
| 68 | &mut builder, | 68 | &mut builder, |
| 69 | &mut state, | 69 | &mut state, |
| 70 | MouseReport::desc(), | 70 | MouseReport::desc(), |
| @@ -120,6 +120,11 @@ impl RequestHandler for MyRequestHandler { | |||
| 120 | None | 120 | None |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 124 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 125 | OutResponse::Accepted | ||
| 126 | } | ||
| 127 | |||
| 123 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | 128 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { |
| 124 | info!("Set idle rate for {:?} to {:?}", id, dur); | 129 | info!("Set idle rate for {:?} to {:?}", id, dur); |
| 125 | } | 130 | } |
