diff options
| author | alexmoon <[email protected]> | 2022-04-01 10:57:37 -0400 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-04-06 05:38:11 +0200 |
| commit | c309531874052bc96ef9ce39dd8698c120cee824 (patch) | |
| tree | 365424e5b37ec66a200a49fd0cd157dd57f278bb | |
| parent | daf2379fa4d4ab8772982847613a09a750ac7ba8 (diff) | |
Remove output() and split() methods from HidClass when there is no out endpoint, and route set_report requests for output reports to RequestHandler::set_report in that case.
| -rw-r--r-- | embassy-usb-hid/src/lib.rs | 125 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_hid.rs | 3 |
2 files changed, 62 insertions, 66 deletions
diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs index 4a7e60320..8bc9efdb0 100644 --- a/embassy-usb-hid/src/lib.rs +++ b/embassy-usb-hid/src/lib.rs | |||
| @@ -74,12 +74,12 @@ impl<'a, const IN_N: usize, const OUT_N: usize> State<'a, IN_N, OUT_N> { | |||
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | pub struct HidClass<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> { | 77 | pub struct HidClass<'d, D: Driver<'d>, T, const IN_N: usize> { |
| 78 | input: ReportWriter<'d, D, IN_N>, | 78 | input: ReportWriter<'d, D, IN_N>, |
| 79 | output: ReportReader<'d, D, OUT_N>, | 79 | output: T, |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> HidClass<'d, D, IN_N, OUT_N> { | 82 | impl<'d, D: Driver<'d>, const IN_N: usize> HidClass<'d, D, (), IN_N> { |
| 83 | /// Creates a new HidClass. | 83 | /// Creates a new HidClass. |
| 84 | /// | 84 | /// |
| 85 | /// 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 |
| @@ -87,60 +87,64 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> HidClass<'d, D, I | |||
| 87 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for | 87 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for |
| 88 | /// high performance uses, and a value of 255 is good for best-effort usecases. | 88 | /// high performance uses, and a value of 255 is good for best-effort usecases. |
| 89 | /// | 89 | /// |
| 90 | /// This allocates two endpoints (IN and OUT). | 90 | /// This allocates an IN endpoint only. |
| 91 | /// See new_ep_in (IN endpoint only) and new_ep_out (OUT endpoint only) to only create a single | ||
| 92 | /// endpoint. | ||
| 93 | pub fn new( | 91 | pub fn new( |
| 94 | builder: &mut UsbDeviceBuilder<'d, D>, | 92 | builder: &mut UsbDeviceBuilder<'d, D>, |
| 95 | state: &'d mut State<'d, IN_N, OUT_N>, | 93 | state: &'d mut State<'d, IN_N, 0>, |
| 96 | report_descriptor: &'static [u8], | 94 | report_descriptor: &'static [u8], |
| 97 | request_handler: Option<&'d dyn RequestHandler>, | 95 | request_handler: Option<&'d dyn RequestHandler>, |
| 98 | poll_ms: u8, | 96 | poll_ms: u8, |
| 97 | max_packet_size: u16, | ||
| 99 | ) -> Self { | 98 | ) -> Self { |
| 100 | let ep_out = Some(builder.alloc_interrupt_endpoint_out(64, poll_ms)); | 99 | let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms); |
| 101 | let ep_in = builder.alloc_interrupt_endpoint_in(64, poll_ms); | 100 | let control = state |
| 102 | Self::new_inner( | 101 | .control |
| 103 | builder, | 102 | .write(Control::new(report_descriptor, None, request_handler)); |
| 104 | state, | 103 | |
| 105 | report_descriptor, | 104 | control.build(builder, None, &ep_in); |
| 106 | request_handler, | 105 | |
| 107 | ep_out, | 106 | Self { |
| 108 | ep_in, | 107 | input: ReportWriter { ep_in }, |
| 109 | ) | 108 | output: (), |
| 109 | } | ||
| 110 | } | 110 | } |
| 111 | } | ||
| 111 | 112 | ||
| 112 | /// Creates a new HidClass with the provided UsbBus & HID report descriptor. | 113 | impl<'d, D: Driver<'d>, T, const IN_N: usize> HidClass<'d, D, T, IN_N> { |
| 113 | /// See new() for more details. | 114 | /// Gets the [`ReportWriter`] for input reports. |
| 114 | pub fn new_ep_in( | 115 | /// |
| 115 | builder: &mut UsbDeviceBuilder<'d, D>, | 116 | /// **Note:** If the `HidClass` was created with [`new_ep_out()`](Self::new_ep_out) |
| 116 | state: &'d mut State<'d, IN_N, OUT_N>, | 117 | /// this writer will be useless as no endpoint is availabe to send reports. |
| 117 | report_descriptor: &'static [u8], | 118 | pub fn input(&mut self) -> &mut ReportWriter<'d, D, IN_N> { |
| 118 | request_handler: Option<&'d dyn RequestHandler>, | 119 | &mut self.input |
| 119 | poll_ms: u8, | ||
| 120 | ) -> Self { | ||
| 121 | let ep_out = None; | ||
| 122 | let ep_in = builder.alloc_interrupt_endpoint_in(64, poll_ms); | ||
| 123 | Self::new_inner( | ||
| 124 | builder, | ||
| 125 | state, | ||
| 126 | report_descriptor, | ||
| 127 | request_handler, | ||
| 128 | ep_out, | ||
| 129 | ep_in, | ||
| 130 | ) | ||
| 131 | } | 120 | } |
| 121 | } | ||
| 132 | 122 | ||
| 133 | fn new_inner( | 123 | impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> |
| 124 | HidClass<'d, D, ReportReader<'d, D, OUT_N>, IN_N> | ||
| 125 | { | ||
| 126 | /// Creates a new HidClass. | ||
| 127 | /// | ||
| 128 | /// poll_ms configures how frequently the host should poll for reading/writing | ||
| 129 | /// HID reports. A lower value means better throughput & latency, at the expense | ||
| 130 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for | ||
| 131 | /// high performance uses, and a value of 255 is good for best-effort usecases. | ||
| 132 | /// | ||
| 133 | /// This allocates two endpoints (IN and OUT). | ||
| 134 | pub fn with_output_ep( | ||
| 134 | builder: &mut UsbDeviceBuilder<'d, D>, | 135 | builder: &mut UsbDeviceBuilder<'d, D>, |
| 135 | state: &'d mut State<'d, IN_N, OUT_N>, | 136 | state: &'d mut State<'d, IN_N, OUT_N>, |
| 136 | report_descriptor: &'static [u8], | 137 | report_descriptor: &'static [u8], |
| 137 | request_handler: Option<&'d dyn RequestHandler>, | 138 | request_handler: Option<&'d dyn RequestHandler>, |
| 138 | ep_out: Option<D::EndpointOut>, | 139 | poll_ms: u8, |
| 139 | ep_in: D::EndpointIn, | 140 | max_packet_size: u16, |
| 140 | ) -> Self { | 141 | ) -> Self { |
| 142 | let ep_out = Some(builder.alloc_interrupt_endpoint_out(max_packet_size, poll_ms)); | ||
| 143 | let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms); | ||
| 144 | |||
| 141 | let control = state.control.write(Control::new( | 145 | let control = state.control.write(Control::new( |
| 142 | report_descriptor, | 146 | report_descriptor, |
| 143 | &state.out_signal, | 147 | Some(&state.out_signal), |
| 144 | request_handler, | 148 | request_handler, |
| 145 | )); | 149 | )); |
| 146 | 150 | ||
| @@ -155,14 +159,6 @@ impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> HidClass<'d, D, I | |||
| 155 | } | 159 | } |
| 156 | } | 160 | } |
| 157 | 161 | ||
| 158 | /// Gets the [`ReportWriter`] for input reports. | ||
| 159 | /// | ||
| 160 | /// **Note:** If the `HidClass` was created with [`new_ep_out()`](Self::new_ep_out) | ||
| 161 | /// this writer will be useless as no endpoint is availabe to send reports. | ||
| 162 | pub fn input(&mut self) -> &mut ReportWriter<'d, D, IN_N> { | ||
| 163 | &mut self.input | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Gets the [`ReportReader`] for output reports. | 162 | /// Gets the [`ReportReader`] for output reports. |
| 167 | pub fn output(&mut self) -> &mut ReportReader<'d, D, OUT_N> { | 163 | pub fn output(&mut self) -> &mut ReportReader<'d, D, OUT_N> { |
| 168 | &mut self.output | 164 | &mut self.output |
| @@ -269,8 +265,9 @@ pub trait RequestHandler { | |||
| 269 | 265 | ||
| 270 | /// Sets the value of report `id` to `data`. | 266 | /// Sets the value of report `id` to `data`. |
| 271 | /// | 267 | /// |
| 272 | /// This is only called for feature or input reports. Output reports | 268 | /// If an output endpoint has been allocated, output reports |
| 273 | /// are routed through [`HidClass::output()`]. | 269 | /// are routed through [`HidClass::output()`]. Otherwise they |
| 270 | /// are sent here, along with input and feature reports. | ||
| 274 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | 271 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { |
| 275 | let _ = (id, data); | 272 | let _ = (id, data); |
| 276 | OutResponse::Rejected | 273 | OutResponse::Rejected |
| @@ -295,9 +292,9 @@ pub trait RequestHandler { | |||
| 295 | } | 292 | } |
| 296 | } | 293 | } |
| 297 | 294 | ||
| 298 | pub struct Control<'d, const OUT_N: usize> { | 295 | struct Control<'d, const OUT_N: usize> { |
| 299 | report_descriptor: &'static [u8], | 296 | report_descriptor: &'static [u8], |
| 300 | out_signal: &'d Signal<(usize, [u8; OUT_N])>, | 297 | out_signal: Option<&'d Signal<(usize, [u8; OUT_N])>>, |
| 301 | request_handler: Option<&'d dyn RequestHandler>, | 298 | request_handler: Option<&'d dyn RequestHandler>, |
| 302 | hid_descriptor: [u8; 9], | 299 | hid_descriptor: [u8; 9], |
| 303 | } | 300 | } |
| @@ -305,7 +302,7 @@ pub struct Control<'d, const OUT_N: usize> { | |||
| 305 | impl<'a, const OUT_N: usize> Control<'a, OUT_N> { | 302 | impl<'a, const OUT_N: usize> Control<'a, OUT_N> { |
| 306 | fn new( | 303 | fn new( |
| 307 | report_descriptor: &'static [u8], | 304 | report_descriptor: &'static [u8], |
| 308 | out_signal: &'a Signal<(usize, [u8; OUT_N])>, | 305 | out_signal: Option<&'a Signal<(usize, [u8; OUT_N])>>, |
| 309 | request_handler: Option<&'a dyn RequestHandler>, | 306 | request_handler: Option<&'a dyn RequestHandler>, |
| 310 | ) -> Self { | 307 | ) -> Self { |
| 311 | Control { | 308 | Control { |
| @@ -396,24 +393,22 @@ impl<'d, const OUT_N: usize> ControlHandler for Control<'d, OUT_N> { | |||
| 396 | } | 393 | } |
| 397 | OutResponse::Accepted | 394 | OutResponse::Accepted |
| 398 | } | 395 | } |
| 399 | HID_REQ_SET_REPORT => match ReportId::try_from(req.value) { | 396 | HID_REQ_SET_REPORT => match ( |
| 400 | Ok(ReportId::Out(_id)) => { | 397 | ReportId::try_from(req.value), |
| 398 | self.out_signal, | ||
| 399 | self.request_handler.as_ref(), | ||
| 400 | ) { | ||
| 401 | (Ok(ReportId::Out(_)), Some(signal), _) => { | ||
| 401 | let mut buf = [0; OUT_N]; | 402 | let mut buf = [0; OUT_N]; |
| 402 | buf[0..data.len()].copy_from_slice(data); | 403 | buf[0..data.len()].copy_from_slice(data); |
| 403 | if self.out_signal.signaled() { | 404 | if signal.signaled() { |
| 404 | warn!("Output report dropped before being read!"); | 405 | warn!("Output report dropped before being read!"); |
| 405 | } | 406 | } |
| 406 | self.out_signal.signal((data.len(), buf)); | 407 | signal.signal((data.len(), buf)); |
| 407 | OutResponse::Accepted | 408 | OutResponse::Accepted |
| 408 | } | 409 | } |
| 409 | Ok(id @ ReportId::Feature(_)) | Ok(id @ ReportId::In(_)) => { | 410 | (Ok(id), _, Some(handler)) => handler.set_report(id, data), |
| 410 | if let Some(handler) = self.request_handler.as_ref() { | 411 | _ => OutResponse::Rejected, |
| 411 | handler.set_report(id, data) | ||
| 412 | } else { | ||
| 413 | OutResponse::Rejected | ||
| 414 | } | ||
| 415 | } | ||
| 416 | Err(_) => OutResponse::Rejected, | ||
| 417 | }, | 412 | }, |
| 418 | HID_REQ_SET_PROTOCOL => { | 413 | HID_REQ_SET_PROTOCOL => { |
| 419 | if req.value == 1 { | 414 | if req.value == 1 { |
diff --git a/examples/nrf/src/bin/usb_hid.rs b/examples/nrf/src/bin/usb_hid.rs index 11c2d71ad..5253f225d 100644 --- a/examples/nrf/src/bin/usb_hid.rs +++ b/examples/nrf/src/bin/usb_hid.rs | |||
| @@ -64,12 +64,13 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 64 | ); | 64 | ); |
| 65 | 65 | ||
| 66 | // Create classes on the builder. | 66 | // Create classes on the builder. |
| 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(), |
| 71 | Some(&request_handler), | 71 | Some(&request_handler), |
| 72 | 60, | 72 | 60, |
| 73 | 8, | ||
| 73 | ); | 74 | ); |
| 74 | 75 | ||
| 75 | // Build the builder. | 76 | // Build the builder. |
