aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-04-16 01:59:40 +0200
committerDario Nieuwenhuis <[email protected]>2022-04-16 01:59:40 +0200
commitc0de54a341dc40b600574a5009a3d4c54e52d5c8 (patch)
treea26059f90bfd8a2a1d204414415951593c6822a2
parent94090e068ec8c56e4fd6676330d87305c5dfee3e (diff)
usb-hid: Simplify API.
- Renamed structs to HidReaderWriter, HidReader, HidWriter. - Removed unused const generics on `State`. - Simplified generics on `HidReaderWriter`. The class type previously was `HidClass<D, Driver<'d, USBD>, ReportReader<'d, Driver<'d, USBD>, OUT_N>, IN_N>` It's now `HidClass<D, Driver<'d, USBD>, IN_N, OUT_N>`. Note that the driver type `Driver<'d, USBD>` is no longer repeated. - Constructors are now: `HidWriter::new()` for IN-only, `HidReaderWriter::new()` for IN+OUT. No complicated bounds. - HidReaderWriter has all the methods from HidReader, HidWriter.
-rw-r--r--embassy-usb-hid/src/lib.rs272
-rw-r--r--examples/nrf/src/bin/usb_hid_keyboard.rs14
-rw-r--r--examples/nrf/src/bin/usb_hid_mouse.rs8
3 files changed, 160 insertions, 134 deletions
diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs
index e870becf5..1035c2792 100644
--- a/embassy-usb-hid/src/lib.rs
+++ b/embassy-usb-hid/src/lib.rs
@@ -60,12 +60,12 @@ impl ReportId {
60 } 60 }
61} 61}
62 62
63pub struct State<'a, const IN_N: usize, const OUT_N: usize> { 63pub struct State<'d> {
64 control: MaybeUninit<Control<'a>>, 64 control: MaybeUninit<Control<'d>>,
65 out_report_offset: AtomicUsize, 65 out_report_offset: AtomicUsize,
66} 66}
67 67
68impl<'a, const IN_N: usize, const OUT_N: usize> State<'a, IN_N, OUT_N> { 68impl<'d> State<'d> {
69 pub fn new() -> Self { 69 pub fn new() -> Self {
70 State { 70 State {
71 control: MaybeUninit::uninit(), 71 control: MaybeUninit::uninit(),
@@ -74,107 +74,146 @@ impl<'a, const IN_N: usize, const OUT_N: usize> State<'a, IN_N, OUT_N> {
74 } 74 }
75} 75}
76 76
77pub struct HidClass<'d, D: Driver<'d>, T, const IN_N: usize> { 77pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
78 input: ReportWriter<'d, D, IN_N>, 78 reader: HidReader<'d, D, READ_N>,
79 output: T, 79 writer: HidWriter<'d, D, WRITE_N>,
80} 80}
81 81
82impl<'d, D: Driver<'d>, const IN_N: usize> HidClass<'d, D, (), IN_N> { 82fn build<'d, D: Driver<'d>>(
83 /// Creates a new HidClass. 83 builder: &mut UsbDeviceBuilder<'d, D>,
84 /// 84 state: &'d mut State<'d>,
85 /// poll_ms configures how frequently the host should poll for reading/writing 85 report_descriptor: &'static [u8],
86 /// HID reports. A lower value means better throughput & latency, at the expense 86 request_handler: Option<&'d dyn RequestHandler>,
87 /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for 87 poll_ms: u8,
88 /// high performance uses, and a value of 255 is good for best-effort usecases. 88 max_packet_size: u16,
89 /// 89 with_out_endpoint: bool,
90 /// This allocates an IN endpoint only. 90) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {
91 pub fn new<const OUT_N: usize>( 91 let control = state.control.write(Control::new(
92 builder: &mut UsbDeviceBuilder<'d, D>, 92 report_descriptor,
93 state: &'d mut State<'d, IN_N, OUT_N>, 93 request_handler,
94 report_descriptor: &'static [u8], 94 &state.out_report_offset,
95 request_handler: Option<&'d dyn RequestHandler>, 95 ));
96 poll_ms: u8, 96
97 max_packet_size: u16, 97 let len = report_descriptor.len();
98 ) -> Self { 98 let if_num = builder.alloc_interface_with_handler(control);
99 let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms); 99 let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms);
100 let control = state.control.write(Control::new( 100 let ep_out = if with_out_endpoint {
101 report_descriptor, 101 Some(builder.alloc_interrupt_endpoint_out(max_packet_size, poll_ms))
102 request_handler, 102 } else {
103 &state.out_report_offset, 103 None
104 )); 104 };
105 control.build(builder, None, &ep_in); 105
106 106 builder.config_descriptor.interface(
107 Self { 107 if_num,
108 input: ReportWriter { ep_in }, 108 USB_CLASS_HID,
109 output: (), 109 USB_SUBCLASS_NONE,
110 } 110 USB_PROTOCOL_NONE,
111 );
112
113 // HID descriptor
114 builder.config_descriptor.write(
115 HID_DESC_DESCTYPE_HID,
116 &[
117 // HID Class spec version
118 HID_DESC_SPEC_1_10[0],
119 HID_DESC_SPEC_1_10[1],
120 // Country code not supported
121 HID_DESC_COUNTRY_UNSPEC,
122 // Number of following descriptors
123 1,
124 // We have a HID report descriptor the host should read
125 HID_DESC_DESCTYPE_HID_REPORT,
126 // HID report descriptor size,
127 (len & 0xFF) as u8,
128 (len >> 8 & 0xFF) as u8,
129 ],
130 );
131
132 builder.config_descriptor.endpoint(ep_in.info());
133 if let Some(ep_out) = &ep_out {
134 builder.config_descriptor.endpoint(ep_out.info());
111 } 135 }
112}
113 136
114impl<'d, D: Driver<'d>, T, const IN_N: usize> HidClass<'d, D, T, IN_N> { 137 (ep_out, ep_in, &state.out_report_offset)
115 /// Gets the [`ReportWriter`] for input reports.
116 ///
117 /// **Note:** If the `HidClass` was created with [`new_ep_out()`](Self::new_ep_out)
118 /// this writer will be useless as no endpoint is availabe to send reports.
119 pub fn input(&mut self) -> &mut ReportWriter<'d, D, IN_N> {
120 &mut self.input
121 }
122} 138}
123 139
124impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> 140impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize>
125 HidClass<'d, D, ReportReader<'d, D, OUT_N>, IN_N> 141 HidReaderWriter<'d, D, READ_N, WRITE_N>
126{ 142{
127 /// Creates a new HidClass. 143 /// Creates a new HidReaderWriter.
144 ///
145 /// This will allocate one IN and one OUT endpoints. If you only need writing (sending)
146 /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only.
128 /// 147 ///
129 /// poll_ms configures how frequently the host should poll for reading/writing 148 /// poll_ms configures how frequently the host should poll for reading/writing
130 /// HID reports. A lower value means better throughput & latency, at the expense 149 /// HID reports. A lower value means better throughput & latency, at the expense
131 /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for 150 /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
132 /// high performance uses, and a value of 255 is good for best-effort usecases. 151 /// high performance uses, and a value of 255 is good for best-effort usecases.
133 /// 152 pub fn new(
134 /// This allocates two endpoints (IN and OUT).
135 pub fn with_output_ep(
136 builder: &mut UsbDeviceBuilder<'d, D>, 153 builder: &mut UsbDeviceBuilder<'d, D>,
137 state: &'d mut State<'d, IN_N, OUT_N>, 154 state: &'d mut State<'d>,
138 report_descriptor: &'static [u8], 155 report_descriptor: &'static [u8],
139 request_handler: Option<&'d dyn RequestHandler>, 156 request_handler: Option<&'d dyn RequestHandler>,
140 poll_ms: u8, 157 poll_ms: u8,
141 max_packet_size: u16, 158 max_packet_size: u16,
142 ) -> Self { 159 ) -> Self {
143 let ep_out = builder.alloc_interrupt_endpoint_out(max_packet_size, poll_ms); 160 let (ep_out, ep_in, offset) = build(
144 let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms); 161 builder,
145 162 state,
146 let control = state.control.write(Control::new(
147 report_descriptor, 163 report_descriptor,
148 request_handler, 164 request_handler,
149 &state.out_report_offset, 165 poll_ms,
150 )); 166 max_packet_size,
151 control.build(builder, Some(&ep_out), &ep_in); 167 true,
168 );
152 169
153 Self { 170 Self {
154 input: ReportWriter { ep_in }, 171 reader: HidReader {
155 output: ReportReader { 172 ep_out: ep_out.unwrap(),
156 ep_out, 173 offset,
157 offset: &state.out_report_offset,
158 }, 174 },
175 writer: HidWriter { ep_in },
159 } 176 }
160 } 177 }
161 178
162 /// Gets the [`ReportReader`] for output reports. 179 /// Splits into seperate readers/writers for input and output reports.
163 pub fn output(&mut self) -> &mut ReportReader<'d, D, OUT_N> { 180 pub fn split(self) -> (HidReader<'d, D, READ_N>, HidWriter<'d, D, WRITE_N>) {
164 &mut self.output 181 (self.reader, self.writer)
182 }
183
184 /// Waits for both IN and OUT endpoints to be enabled.
185 pub async fn ready(&mut self) -> () {
186 self.reader.ready().await;
187 self.writer.ready().await;
188 }
189
190 /// Writes an input report by serializing the given report structure.
191 #[cfg(feature = "usbd-hid")]
192 pub async fn write_serialize<IR: AsInputReport>(
193 &mut self,
194 r: &IR,
195 ) -> Result<(), EndpointError> {
196 self.writer.write_serialize(r).await
197 }
198
199 /// Writes `report` to its interrupt endpoint.
200 pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
201 self.writer.write(report).await
165 } 202 }
166 203
167 /// Splits this `HidClass` into seperate readers/writers for input and output reports. 204 /// Reads an output report from the Interrupt Out pipe.
168 pub fn split(self) -> (ReportWriter<'d, D, IN_N>, ReportReader<'d, D, OUT_N>) { 205 ///
169 (self.input, self.output) 206 /// See [`HidReader::read`].
207 pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
208 self.reader.read(buf).await
170 } 209 }
171} 210}
172 211
173pub struct ReportWriter<'d, D: Driver<'d>, const N: usize> { 212pub struct HidWriter<'d, D: Driver<'d>, const N: usize> {
174 ep_in: D::EndpointIn, 213 ep_in: D::EndpointIn,
175} 214}
176 215
177pub struct ReportReader<'d, D: Driver<'d>, const N: usize> { 216pub struct HidReader<'d, D: Driver<'d>, const N: usize> {
178 ep_out: D::EndpointOut, 217 ep_out: D::EndpointOut,
179 offset: &'d AtomicUsize, 218 offset: &'d AtomicUsize,
180} 219}
@@ -197,17 +236,50 @@ impl From<embassy_usb::driver::EndpointError> for ReadError {
197 } 236 }
198} 237}
199 238
200impl<'d, D: Driver<'d>, const N: usize> ReportWriter<'d, D, N> { 239impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {
240 /// Creates a new HidWriter.
241 ///
242 /// This will allocate one IN endpoint only, so the host won't be able to send
243 /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead.
244 ///
245 /// poll_ms configures how frequently the host should poll for reading/writing
246 /// HID reports. A lower value means better throughput & latency, at the expense
247 /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for
248 /// high performance uses, and a value of 255 is good for best-effort usecases.
249 pub fn new(
250 builder: &mut UsbDeviceBuilder<'d, D>,
251 state: &'d mut State<'d>,
252 report_descriptor: &'static [u8],
253 request_handler: Option<&'d dyn RequestHandler>,
254 poll_ms: u8,
255 max_packet_size: u16,
256 ) -> Self {
257 let (ep_out, ep_in, _offset) = build(
258 builder,
259 state,
260 report_descriptor,
261 request_handler,
262 poll_ms,
263 max_packet_size,
264 false,
265 );
266
267 assert!(ep_out.is_none());
268
269 Self { ep_in }
270 }
271
201 /// Waits for the interrupt in endpoint to be enabled. 272 /// Waits for the interrupt in endpoint to be enabled.
202 pub async fn ready(&mut self) -> () { 273 pub async fn ready(&mut self) -> () {
203 self.ep_in.wait_enabled().await 274 self.ep_in.wait_enabled().await
204 } 275 }
205 276
206 /// Tries to write an input report by serializing the given report structure. 277 /// Writes an input report by serializing the given report structure.
207 ///
208 /// Panics if no endpoint is available.
209 #[cfg(feature = "usbd-hid")] 278 #[cfg(feature = "usbd-hid")]
210 pub async fn serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> { 279 pub async fn write_serialize<IR: AsInputReport>(
280 &mut self,
281 r: &IR,
282 ) -> Result<(), EndpointError> {
211 let mut buf: [u8; N] = [0; N]; 283 let mut buf: [u8; N] = [0; N];
212 let size = match serialize(&mut buf, r) { 284 let size = match serialize(&mut buf, r) {
213 Ok(size) => size, 285 Ok(size) => size,
@@ -217,8 +289,6 @@ impl<'d, D: Driver<'d>, const N: usize> ReportWriter<'d, D, N> {
217 } 289 }
218 290
219 /// Writes `report` to its interrupt endpoint. 291 /// Writes `report` to its interrupt endpoint.
220 ///
221 /// Panics if no endpoint is available.
222 pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> { 292 pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
223 assert!(report.len() <= N); 293 assert!(report.len() <= N);
224 294
@@ -236,16 +306,13 @@ impl<'d, D: Driver<'d>, const N: usize> ReportWriter<'d, D, N> {
236 } 306 }
237} 307}
238 308
239impl<'d, D: Driver<'d>, const N: usize> ReportReader<'d, D, N> { 309impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
240 /// Waits for the interrupt out endpoint to be enabled. 310 /// Waits for the interrupt out endpoint to be enabled.
241 pub async fn ready(&mut self) -> () { 311 pub async fn ready(&mut self) -> () {
242 self.ep_out.wait_enabled().await 312 self.ep_out.wait_enabled().await
243 } 313 }
244 314
245 /// Starts a task to deliver output reports from the Interrupt Out pipe to 315 /// Delivers output reports from the Interrupt Out pipe to `handler`.
246 /// `handler`.
247 ///
248 /// Terminates when the interface becomes disabled.
249 /// 316 ///
250 /// If `use_report_ids` is true, the first byte of the report will be used as 317 /// If `use_report_ids` is true, the first byte of the report will be used as
251 /// the `ReportId` value. Otherwise the `ReportId` value will be 0. 318 /// the `ReportId` value. Otherwise the `ReportId` value will be 0.
@@ -391,47 +458,6 @@ impl<'a> Control<'a> {
391 ], 458 ],
392 } 459 }
393 } 460 }
394
395 fn build<'d, D: Driver<'d>>(
396 &'d mut self,
397 builder: &mut UsbDeviceBuilder<'d, D>,
398 ep_out: Option<&D::EndpointOut>,
399 ep_in: &D::EndpointIn,
400 ) {
401 let len = self.report_descriptor.len();
402 let if_num = builder.alloc_interface_with_handler(self);
403
404 builder.config_descriptor.interface(
405 if_num,
406 USB_CLASS_HID,
407 USB_SUBCLASS_NONE,
408 USB_PROTOCOL_NONE,
409 );
410
411 // HID descriptor
412 builder.config_descriptor.write(
413 HID_DESC_DESCTYPE_HID,
414 &[
415 // HID Class spec version
416 HID_DESC_SPEC_1_10[0],
417 HID_DESC_SPEC_1_10[1],
418 // Country code not supported
419 HID_DESC_COUNTRY_UNSPEC,
420 // Number of following descriptors
421 1,
422 // We have a HID report descriptor the host should read
423 HID_DESC_DESCTYPE_HID_REPORT,
424 // HID report descriptor size,
425 (len & 0xFF) as u8,
426 (len >> 8 & 0xFF) as u8,
427 ],
428 );
429
430 builder.config_descriptor.endpoint(ep_in.info());
431 if let Some(ep) = ep_out {
432 builder.config_descriptor.endpoint(ep.info());
433 }
434 }
435} 461}
436 462
437impl<'d> ControlHandler for Control<'d> { 463impl<'d> ControlHandler for Control<'d> {
diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs
index 5f03f5126..4ed49d375 100644
--- a/examples/nrf/src/bin/usb_hid_keyboard.rs
+++ b/examples/nrf/src/bin/usb_hid_keyboard.rs
@@ -18,7 +18,7 @@ use embassy_nrf::usb::Driver;
18use embassy_nrf::Peripherals; 18use embassy_nrf::Peripherals;
19use embassy_usb::control::OutResponse; 19use embassy_usb::control::OutResponse;
20use embassy_usb::{Config, DeviceStateHandler, UsbDeviceBuilder}; 20use embassy_usb::{Config, DeviceStateHandler, UsbDeviceBuilder};
21use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; 21use embassy_usb_hid::{HidReaderWriter, ReportId, RequestHandler, State};
22use futures::future::join; 22use futures::future::join;
23use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; 23use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
24 24
@@ -75,7 +75,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
75 let request_handler = MyRequestHandler {}; 75 let request_handler = MyRequestHandler {};
76 let device_state_handler = MyDeviceStateHandler::new(); 76 let device_state_handler = MyDeviceStateHandler::new();
77 77
78 let mut state = State::<8, 1>::new(); 78 let mut state = State::new();
79 79
80 let mut builder = UsbDeviceBuilder::new( 80 let mut builder = UsbDeviceBuilder::new(
81 driver, 81 driver,
@@ -88,7 +88,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
88 ); 88 );
89 89
90 // Create classes on the builder. 90 // Create classes on the builder.
91 let hid = HidClass::with_output_ep( 91 let hid = HidReaderWriter::<_, 1, 8>::new(
92 &mut builder, 92 &mut builder,
93 &mut state, 93 &mut state,
94 KeyboardReport::desc(), 94 KeyboardReport::desc(),
@@ -135,7 +135,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
135 135
136 let mut button = Input::new(p.P0_11.degrade(), Pull::Up); 136 let mut button = Input::new(p.P0_11.degrade(), Pull::Up);
137 137
138 let (mut hid_in, hid_out) = hid.split(); 138 let (reader, mut writer) = hid.split();
139 139
140 // Do stuff with the class! 140 // Do stuff with the class!
141 let in_fut = async { 141 let in_fut = async {
@@ -153,7 +153,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
153 modifier: 0, 153 modifier: 0,
154 reserved: 0, 154 reserved: 0,
155 }; 155 };
156 match hid_in.serialize(&report).await { 156 match writer.write_serialize(&report).await {
157 Ok(()) => {} 157 Ok(()) => {}
158 Err(e) => warn!("Failed to send report: {:?}", e), 158 Err(e) => warn!("Failed to send report: {:?}", e),
159 }; 159 };
@@ -167,7 +167,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
167 modifier: 0, 167 modifier: 0,
168 reserved: 0, 168 reserved: 0,
169 }; 169 };
170 match hid_in.serialize(&report).await { 170 match writer.write_serialize(&report).await {
171 Ok(()) => {} 171 Ok(()) => {}
172 Err(e) => warn!("Failed to send report: {:?}", e), 172 Err(e) => warn!("Failed to send report: {:?}", e),
173 }; 173 };
@@ -175,7 +175,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
175 }; 175 };
176 176
177 let out_fut = async { 177 let out_fut = async {
178 hid_out.run(false, &request_handler).await; 178 reader.run(false, &request_handler).await;
179 }; 179 };
180 180
181 let power_irq = interrupt::take!(POWER_CLOCK); 181 let power_irq = interrupt::take!(POWER_CLOCK);
diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs
index fe27e76fb..bbdced745 100644
--- a/examples/nrf/src/bin/usb_hid_mouse.rs
+++ b/examples/nrf/src/bin/usb_hid_mouse.rs
@@ -13,7 +13,7 @@ use embassy_nrf::usb::Driver;
13use embassy_nrf::Peripherals; 13use embassy_nrf::Peripherals;
14use embassy_usb::control::OutResponse; 14use embassy_usb::control::OutResponse;
15use embassy_usb::{Config, UsbDeviceBuilder}; 15use embassy_usb::{Config, UsbDeviceBuilder};
16use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; 16use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State};
17use futures::future::join; 17use futures::future::join;
18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; 18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
19 19
@@ -52,7 +52,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
52 let mut control_buf = [0; 16]; 52 let mut control_buf = [0; 16];
53 let request_handler = MyRequestHandler {}; 53 let request_handler = MyRequestHandler {};
54 54
55 let mut control = State::<5, 0>::new(); 55 let mut control = State::new();
56 56
57 let mut builder = UsbDeviceBuilder::new( 57 let mut builder = UsbDeviceBuilder::new(
58 driver, 58 driver,
@@ -65,7 +65,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
65 ); 65 );
66 66
67 // Create classes on the builder. 67 // Create classes on the builder.
68 let mut hid = HidClass::new( 68 let mut writer = HidWriter::<_, 5>::new(
69 &mut builder, 69 &mut builder,
70 &mut control, 70 &mut control,
71 MouseReport::desc(), 71 MouseReport::desc(),
@@ -94,7 +94,7 @@ async fn main(_spawner: Spawner, p: Peripherals) {
94 wheel: 0, 94 wheel: 0,
95 pan: 0, 95 pan: 0,
96 }; 96 };
97 match hid.input().serialize(&report).await { 97 match writer.write_serialize(&report).await {
98 Ok(()) => {} 98 Ok(()) => {}
99 Err(e) => warn!("Failed to send report: {:?}", e), 99 Err(e) => warn!("Failed to send report: {:?}", e),
100 } 100 }