aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/usb.rs8
-rw-r--r--embassy-rp/src/usb.rs16
-rw-r--r--embassy-stm32/src/usb/usb.rs16
-rw-r--r--embassy-stm32/src/usb_otg/usb.rs16
-rw-r--r--embassy-usb-driver/README.md32
-rw-r--r--embassy-usb-driver/src/lib.rs116
-rw-r--r--embassy-usb/README.md22
-rw-r--r--embassy-usb/src/builder.rs24
-rw-r--r--embassy-usb/src/class/cdc_acm.rs9
-rw-r--r--embassy-usb/src/class/cdc_ncm/embassy_net.rs11
-rw-r--r--embassy-usb/src/class/cdc_ncm/mod.rs51
-rw-r--r--embassy-usb/src/class/hid.rs21
-rw-r--r--embassy-usb/src/class/mod.rs1
-rw-r--r--embassy-usb/src/control.rs7
-rw-r--r--embassy-usb/src/descriptor.rs4
-rw-r--r--embassy-usb/src/lib.rs8
-rw-r--r--embassy-usb/src/types.rs2
17 files changed, 267 insertions, 97 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs
index 6be4fec8c..f030b909f 100644
--- a/embassy-nrf/src/usb.rs
+++ b/embassy-nrf/src/usb.rs
@@ -235,7 +235,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
235 &mut self, 235 &mut self,
236 ep_type: EndpointType, 236 ep_type: EndpointType,
237 packet_size: u16, 237 packet_size: u16,
238 interval: u8, 238 interval_ms: u8,
239 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 239 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
240 let index = self.alloc_in.allocate(ep_type)?; 240 let index = self.alloc_in.allocate(ep_type)?;
241 let ep_addr = EndpointAddress::from_parts(index, Direction::In); 241 let ep_addr = EndpointAddress::from_parts(index, Direction::In);
@@ -243,7 +243,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
243 addr: ep_addr, 243 addr: ep_addr,
244 ep_type, 244 ep_type,
245 max_packet_size: packet_size, 245 max_packet_size: packet_size,
246 interval, 246 interval_ms,
247 })) 247 }))
248 } 248 }
249 249
@@ -251,7 +251,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
251 &mut self, 251 &mut self,
252 ep_type: EndpointType, 252 ep_type: EndpointType,
253 packet_size: u16, 253 packet_size: u16,
254 interval: u8, 254 interval_ms: u8,
255 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 255 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
256 let index = self.alloc_out.allocate(ep_type)?; 256 let index = self.alloc_out.allocate(ep_type)?;
257 let ep_addr = EndpointAddress::from_parts(index, Direction::Out); 257 let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
@@ -259,7 +259,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
259 addr: ep_addr, 259 addr: ep_addr,
260 ep_type, 260 ep_type,
261 max_packet_size: packet_size, 261 max_packet_size: packet_size,
262 interval, 262 interval_ms,
263 })) 263 }))
264 } 264 }
265 265
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 8eec55b49..2e3708eff 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -194,13 +194,13 @@ impl<'d, T: Instance> Driver<'d, T> {
194 &mut self, 194 &mut self,
195 ep_type: EndpointType, 195 ep_type: EndpointType,
196 max_packet_size: u16, 196 max_packet_size: u16,
197 interval: u8, 197 interval_ms: u8,
198 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { 198 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
199 trace!( 199 trace!(
200 "allocating type={:?} mps={:?} interval={}, dir={:?}", 200 "allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
201 ep_type, 201 ep_type,
202 max_packet_size, 202 max_packet_size,
203 interval, 203 interval_ms,
204 D::dir() 204 D::dir()
205 ); 205 );
206 206
@@ -281,7 +281,7 @@ impl<'d, T: Instance> Driver<'d, T> {
281 addr: EndpointAddress::from_parts(index, D::dir()), 281 addr: EndpointAddress::from_parts(index, D::dir()),
282 ep_type, 282 ep_type,
283 max_packet_size, 283 max_packet_size,
284 interval, 284 interval_ms,
285 }, 285 },
286 buf, 286 buf,
287 }) 287 })
@@ -298,18 +298,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
298 &mut self, 298 &mut self,
299 ep_type: EndpointType, 299 ep_type: EndpointType,
300 max_packet_size: u16, 300 max_packet_size: u16,
301 interval: u8, 301 interval_ms: u8,
302 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 302 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
303 self.alloc_endpoint(ep_type, max_packet_size, interval) 303 self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
304 } 304 }
305 305
306 fn alloc_endpoint_out( 306 fn alloc_endpoint_out(
307 &mut self, 307 &mut self,
308 ep_type: EndpointType, 308 ep_type: EndpointType,
309 max_packet_size: u16, 309 max_packet_size: u16,
310 interval: u8, 310 interval_ms: u8,
311 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 311 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
312 self.alloc_endpoint(ep_type, max_packet_size, interval) 312 self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
313 } 313 }
314 314
315 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 315 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 03e792a21..0355c5f14 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -268,13 +268,13 @@ impl<'d, T: Instance> Driver<'d, T> {
268 &mut self, 268 &mut self,
269 ep_type: EndpointType, 269 ep_type: EndpointType,
270 max_packet_size: u16, 270 max_packet_size: u16,
271 interval: u8, 271 interval_ms: u8,
272 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { 272 ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
273 trace!( 273 trace!(
274 "allocating type={:?} mps={:?} interval={}, dir={:?}", 274 "allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
275 ep_type, 275 ep_type,
276 max_packet_size, 276 max_packet_size,
277 interval, 277 interval_ms,
278 D::dir() 278 D::dir()
279 ); 279 );
280 280
@@ -345,7 +345,7 @@ impl<'d, T: Instance> Driver<'d, T> {
345 addr: EndpointAddress::from_parts(index, D::dir()), 345 addr: EndpointAddress::from_parts(index, D::dir()),
346 ep_type, 346 ep_type,
347 max_packet_size, 347 max_packet_size,
348 interval, 348 interval_ms,
349 }, 349 },
350 buf, 350 buf,
351 }) 351 })
@@ -362,18 +362,18 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
362 &mut self, 362 &mut self,
363 ep_type: EndpointType, 363 ep_type: EndpointType,
364 max_packet_size: u16, 364 max_packet_size: u16,
365 interval: u8, 365 interval_ms: u8,
366 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { 366 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
367 self.alloc_endpoint(ep_type, max_packet_size, interval) 367 self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
368 } 368 }
369 369
370 fn alloc_endpoint_out( 370 fn alloc_endpoint_out(
371 &mut self, 371 &mut self,
372 ep_type: EndpointType, 372 ep_type: EndpointType,
373 max_packet_size: u16, 373 max_packet_size: u16,
374 interval: u8, 374 interval_ms: u8,
375 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { 375 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
376 self.alloc_endpoint(ep_type, max_packet_size, interval) 376 self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
377 } 377 }
378 378
379 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 379 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs
index fd2f67073..96c574ca8 100644
--- a/embassy-stm32/src/usb_otg/usb.rs
+++ b/embassy-stm32/src/usb_otg/usb.rs
@@ -217,13 +217,13 @@ impl<'d, T: Instance> Driver<'d, T> {
217 &mut self, 217 &mut self,
218 ep_type: EndpointType, 218 ep_type: EndpointType,
219 max_packet_size: u16, 219 max_packet_size: u16,
220 interval: u8, 220 interval_ms: u8,
221 ) -> Result<Endpoint<'d, T, D>, EndpointAllocError> { 221 ) -> Result<Endpoint<'d, T, D>, EndpointAllocError> {
222 trace!( 222 trace!(
223 "allocating type={:?} mps={:?} interval={}, dir={:?}", 223 "allocating type={:?} mps={:?} interval_ms={}, dir={:?}",
224 ep_type, 224 ep_type,
225 max_packet_size, 225 max_packet_size,
226 interval, 226 interval_ms,
227 D::dir() 227 D::dir()
228 ); 228 );
229 229
@@ -292,7 +292,7 @@ impl<'d, T: Instance> Driver<'d, T> {
292 addr: EndpointAddress::from_parts(index, D::dir()), 292 addr: EndpointAddress::from_parts(index, D::dir()),
293 ep_type, 293 ep_type,
294 max_packet_size, 294 max_packet_size,
295 interval, 295 interval_ms,
296 }, 296 },
297 }) 297 })
298 } 298 }
@@ -308,18 +308,18 @@ impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
308 &mut self, 308 &mut self,
309 ep_type: EndpointType, 309 ep_type: EndpointType,
310 max_packet_size: u16, 310 max_packet_size: u16,
311 interval: u8, 311 interval_ms: u8,
312 ) -> Result<Self::EndpointIn, EndpointAllocError> { 312 ) -> Result<Self::EndpointIn, EndpointAllocError> {
313 self.alloc_endpoint(ep_type, max_packet_size, interval) 313 self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
314 } 314 }
315 315
316 fn alloc_endpoint_out( 316 fn alloc_endpoint_out(
317 &mut self, 317 &mut self,
318 ep_type: EndpointType, 318 ep_type: EndpointType,
319 max_packet_size: u16, 319 max_packet_size: u16,
320 interval: u8, 320 interval_ms: u8,
321 ) -> Result<Self::EndpointOut, EndpointAllocError> { 321 ) -> Result<Self::EndpointOut, EndpointAllocError> {
322 self.alloc_endpoint(ep_type, max_packet_size, interval) 322 self.alloc_endpoint(ep_type, max_packet_size, interval_ms)
323 } 323 }
324 324
325 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { 325 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
diff --git a/embassy-usb-driver/README.md b/embassy-usb-driver/README.md
new file mode 100644
index 000000000..012663c3f
--- /dev/null
+++ b/embassy-usb-driver/README.md
@@ -0,0 +1,32 @@
1# embassy-usb-driver
2
3This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these
4traits to add support for using `embassy-usb` for a given chip/platform.
5
6The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]
7APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used
8with the newer `embassy-usb` without needing updates.
9
10If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate
11instead of this one.
12
13[`embassy-usb`]: https://crates.io/crates/embassy-usb
14
15## Interoperability
16
17This crate can run on any executor.
18
19## Minimum supported Rust version (MSRV)
20
21This crate requires nightly Rust, due to using "async fn in trait" support.
22
23## License
24
25This work is licensed under either of
26
27- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
28 <http://www.apache.org/licenses/LICENSE-2.0>)
29- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
30
31at your option.
32
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs
index 64d647351..e8f71a2dc 100644
--- a/embassy-usb-driver/src/lib.rs
+++ b/embassy-usb-driver/src/lib.rs
@@ -1,6 +1,8 @@
1#![no_std] 1#![no_std]
2#![feature(async_fn_in_trait)] 2#![feature(async_fn_in_trait)]
3#![allow(incomplete_features)] 3#![allow(incomplete_features)]
4#![doc = include_str!("../README.md")]
5#![warn(missing_docs)]
4 6
5/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from 7/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
6/// the perspective of the host, which is backward for devices, but the standard directions are used 8/// the perspective of the host, which is backward for devices, but the standard directions are used
@@ -95,46 +97,65 @@ impl EndpointAddress {
95 } 97 }
96} 98}
97 99
100/// Infomation for an endpoint.
98#[derive(Copy, Clone, Eq, PartialEq, Debug)] 101#[derive(Copy, Clone, Eq, PartialEq, Debug)]
99#[cfg_attr(feature = "defmt", derive(defmt::Format))] 102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
100pub struct EndpointInfo { 103pub struct EndpointInfo {
104 /// Endpoint's address.
101 pub addr: EndpointAddress, 105 pub addr: EndpointAddress,
106 /// Endpoint's type.
102 pub ep_type: EndpointType, 107 pub ep_type: EndpointType,
108 /// Max packet size, in bytes.
103 pub max_packet_size: u16, 109 pub max_packet_size: u16,
104 pub interval: u8, 110 /// Polling interval, in milliseconds.
111 pub interval_ms: u8,
105} 112}
106 113
107/// Driver for a specific USB peripheral. Implement this to add support for a new hardware 114/// Main USB driver trait.
108/// platform. 115///
116/// Implement this to add support for a new hardware platform.
109pub trait Driver<'a> { 117pub trait Driver<'a> {
118 /// Type of the OUT endpoints for this driver.
110 type EndpointOut: EndpointOut + 'a; 119 type EndpointOut: EndpointOut + 'a;
120 /// Type of the IN endpoints for this driver.
111 type EndpointIn: EndpointIn + 'a; 121 type EndpointIn: EndpointIn + 'a;
122 /// Type of the control pipe for this driver.
112 type ControlPipe: ControlPipe + 'a; 123 type ControlPipe: ControlPipe + 'a;
124 /// Type for bus control for this driver.
113 type Bus: Bus + 'a; 125 type Bus: Bus + 'a;
114 126
115 /// Allocates an endpoint and specified endpoint parameters. This method is called by the device 127 /// Allocates an OUT endpoint.
116 /// and class implementations to allocate endpoints, and can only be called before 128 ///
117 /// [`start`](Self::start) is called. 129 /// This method is called by the USB stack to allocate endpoints.
130 /// It can only be called before [`start`](Self::start) is called.
118 /// 131 ///
119 /// # Arguments 132 /// # Arguments
120 /// 133 ///
121 /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should 134 /// * `ep_type` - the endpoint's type.
122 /// attempt to return an endpoint with the specified address. If None, the implementation
123 /// should return the next available one.
124 /// * `max_packet_size` - Maximum packet size in bytes. 135 /// * `max_packet_size` - Maximum packet size in bytes.
125 /// * `interval` - Polling interval parameter for interrupt endpoints. 136 /// * `interval_ms` - Polling interval parameter for interrupt endpoints.
126 fn alloc_endpoint_out( 137 fn alloc_endpoint_out(
127 &mut self, 138 &mut self,
128 ep_type: EndpointType, 139 ep_type: EndpointType,
129 max_packet_size: u16, 140 max_packet_size: u16,
130 interval: u8, 141 interval_ms: u8,
131 ) -> Result<Self::EndpointOut, EndpointAllocError>; 142 ) -> Result<Self::EndpointOut, EndpointAllocError>;
132 143
144 /// Allocates an IN endpoint.
145 ///
146 /// This method is called by the USB stack to allocate endpoints.
147 /// It can only be called before [`start`](Self::start) is called.
148 ///
149 /// # Arguments
150 ///
151 /// * `ep_type` - the endpoint's type.
152 /// * `max_packet_size` - Maximum packet size in bytes.
153 /// * `interval_ms` - Polling interval parameter for interrupt endpoints.
133 fn alloc_endpoint_in( 154 fn alloc_endpoint_in(
134 &mut self, 155 &mut self,
135 ep_type: EndpointType, 156 ep_type: EndpointType,
136 max_packet_size: u16, 157 max_packet_size: u16,
137 interval: u8, 158 interval_ms: u8,
138 ) -> Result<Self::EndpointIn, EndpointAllocError>; 159 ) -> Result<Self::EndpointIn, EndpointAllocError>;
139 160
140 /// Start operation of the USB device. 161 /// Start operation of the USB device.
@@ -146,35 +167,37 @@ pub trait Driver<'a> {
146 /// This consumes the `Driver` instance, so it's no longer possible to allocate more 167 /// This consumes the `Driver` instance, so it's no longer possible to allocate more
147 /// endpoints. 168 /// endpoints.
148 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe); 169 fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe);
149
150 /// Indicates that `set_device_address` must be called before accepting the corresponding
151 /// control transfer, not after.
152 ///
153 /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
154 const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
155} 170}
156 171
172/// USB bus trait.
173///
174/// This trait provides methods that act on the whole bus. It is kept owned by
175/// the main USB task, and used to manage the bus.
157pub trait Bus { 176pub trait Bus {
158 /// Enables the USB peripheral. Soon after enabling the device will be reset, so 177 /// Enable the USB peripheral.
159 /// there is no need to perform a USB reset in this method.
160 async fn enable(&mut self); 178 async fn enable(&mut self);
161 179
162 /// Disables and powers down the USB peripheral. 180 /// Disable and powers down the USB peripheral.
163 async fn disable(&mut self); 181 async fn disable(&mut self);
164 182
183 /// Wait for a bus-related event.
184 ///
185 /// This method should asynchronously wait for an event to happen, then
186 /// return it. See [`Event`] for the list of events this method should return.
165 async fn poll(&mut self) -> Event; 187 async fn poll(&mut self) -> Event;
166 188
167 /// Enables or disables an endpoint. 189 /// Enable or disable an endpoint.
168 fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); 190 fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool);
169 191
170 /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it 192 /// Set or clear the STALL condition for an endpoint.
171 /// should be prepared to receive data again. Only used during control transfers. 193 ///
194 /// If the endpoint is an OUT endpoint, it should be prepared to receive data again.
172 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); 195 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
173 196
174 /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. 197 /// Get whether the STALL condition is set for an endpoint.
175 fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; 198 fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;
176 199
177 /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the 200 /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the
178 /// device. 201 /// device.
179 /// 202 ///
180 /// The default implementation just returns `Unsupported`. 203 /// The default implementation just returns `Unsupported`.
@@ -187,7 +210,7 @@ pub trait Bus {
187 Err(Unsupported) 210 Err(Unsupported)
188 } 211 }
189 212
190 /// Initiates a remote wakeup of the host by the device. 213 /// Initiate a remote wakeup of the host by the device.
191 /// 214 ///
192 /// # Errors 215 /// # Errors
193 /// 216 ///
@@ -196,25 +219,27 @@ pub trait Bus {
196 async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; 219 async fn remote_wakeup(&mut self) -> Result<(), Unsupported>;
197} 220}
198 221
222/// Endpoint trait, common for OUT and IN.
199pub trait Endpoint { 223pub trait Endpoint {
200 /// Get the endpoint address 224 /// Get the endpoint address
201 fn info(&self) -> &EndpointInfo; 225 fn info(&self) -> &EndpointInfo;
202 226
203 /// Waits for the endpoint to be enabled. 227 /// Wait for the endpoint to be enabled.
204 async fn wait_enabled(&mut self); 228 async fn wait_enabled(&mut self);
205} 229}
206 230
231/// OUT Endpoint trait.
207pub trait EndpointOut: Endpoint { 232pub trait EndpointOut: Endpoint {
208 /// Reads a single packet of data from the endpoint, and returns the actual length of 233 /// Read a single packet of data from the endpoint, and return the actual length of
209 /// the packet. 234 /// the packet.
210 /// 235 ///
211 /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. 236 /// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
212 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; 237 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
213} 238}
214 239
215/// Trait for USB control pipe. 240/// USB control pipe trait.
216/// 241///
217/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single 242/// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single
218/// unit, and manages them together to implement the control pipe state machine. 243/// unit, and manages them together to implement the control pipe state machine.
219/// 244///
220/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that 245/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that
@@ -232,6 +257,14 @@ pub trait EndpointOut: Endpoint {
232/// accept() or reject() 257/// accept() or reject()
233/// ``` 258/// ```
234/// 259///
260/// - control out for setting the device address:
261///
262/// ```not_rust
263/// setup()
264/// (...processing...)
265/// accept_set_address(addr) or reject()
266/// ```
267///
235/// - control out with len != 0: 268/// - control out with len != 0:
236/// 269///
237/// ```not_rust 270/// ```not_rust
@@ -280,26 +313,26 @@ pub trait ControlPipe {
280 /// Maximum packet size for the control pipe 313 /// Maximum packet size for the control pipe
281 fn max_packet_size(&self) -> usize; 314 fn max_packet_size(&self) -> usize;
282 315
283 /// Reads a single setup packet from the endpoint. 316 /// Read a single setup packet from the endpoint.
284 async fn setup(&mut self) -> [u8; 8]; 317 async fn setup(&mut self) -> [u8; 8];
285 318
286 /// Reads a DATA OUT packet into `buf` in response to a control write request. 319 /// Read a DATA OUT packet into `buf` in response to a control write request.
287 /// 320 ///
288 /// Must be called after `setup()` for requests with `direction` of `Out` 321 /// Must be called after `setup()` for requests with `direction` of `Out`
289 /// and `length` greater than zero. 322 /// and `length` greater than zero.
290 async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>; 323 async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>;
291 324
292 /// Sends a DATA IN packet with `data` in response to a control read request. 325 /// Send a DATA IN packet with `data` in response to a control read request.
293 /// 326 ///
294 /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. 327 /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`.
295 async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; 328 async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>;
296 329
297 /// Accepts a control request. 330 /// Accept a control request.
298 /// 331 ///
299 /// Causes the STATUS packet for the current request to be ACKed. 332 /// Causes the STATUS packet for the current request to be ACKed.
300 async fn accept(&mut self); 333 async fn accept(&mut self);
301 334
302 /// Rejects a control request. 335 /// Reject a control request.
303 /// 336 ///
304 /// Sets a STALL condition on the pipe to indicate an error. 337 /// Sets a STALL condition on the pipe to indicate an error.
305 async fn reject(&mut self); 338 async fn reject(&mut self);
@@ -311,8 +344,9 @@ pub trait ControlPipe {
311 async fn accept_set_address(&mut self, addr: u8); 344 async fn accept_set_address(&mut self, addr: u8);
312} 345}
313 346
347/// IN Endpoint trait.
314pub trait EndpointIn: Endpoint { 348pub trait EndpointIn: Endpoint {
315 /// Writes a single packet of data to the endpoint. 349 /// Write a single packet of data to the endpoint.
316 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; 350 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;
317} 351}
318 352
@@ -338,18 +372,22 @@ pub enum Event {
338 PowerRemoved, 372 PowerRemoved,
339} 373}
340 374
375/// Allocating an endpoint failed.
376///
377/// This can be due to running out of endpoints, or out of endpoint memory,
378/// or because the hardware doesn't support the requested combination of features.
341#[derive(Copy, Clone, Eq, PartialEq, Debug)] 379#[derive(Copy, Clone, Eq, PartialEq, Debug)]
342#[cfg_attr(feature = "defmt", derive(defmt::Format))] 380#[cfg_attr(feature = "defmt", derive(defmt::Format))]
343pub struct EndpointAllocError; 381pub struct EndpointAllocError;
344 382
383/// Operation is unsupported by the driver.
345#[derive(Copy, Clone, Eq, PartialEq, Debug)] 384#[derive(Copy, Clone, Eq, PartialEq, Debug)]
346#[cfg_attr(feature = "defmt", derive(defmt::Format))] 385#[cfg_attr(feature = "defmt", derive(defmt::Format))]
347/// Operation is unsupported by the driver.
348pub struct Unsupported; 386pub struct Unsupported;
349 387
388/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]
350#[derive(Copy, Clone, Eq, PartialEq, Debug)] 389#[derive(Copy, Clone, Eq, PartialEq, Debug)]
351#[cfg_attr(feature = "defmt", derive(defmt::Format))] 390#[cfg_attr(feature = "defmt", derive(defmt::Format))]
352/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]
353pub enum EndpointError { 391pub enum EndpointError {
354 /// Either the packet to be written is too long to fit in the transmission 392 /// Either the packet to be written is too long to fit in the transmission
355 /// buffer or the received packet is too long to fit in `buf`. 393 /// buffer or the received packet is too long to fit in `buf`.
diff --git a/embassy-usb/README.md b/embassy-usb/README.md
new file mode 100644
index 000000000..581c3290f
--- /dev/null
+++ b/embassy-usb/README.md
@@ -0,0 +1,22 @@
1# embassy-usb
2
3TODO crate description/
4
5## Interoperability
6
7This crate can run on any executor.
8
9## Minimum supported Rust version (MSRV)
10
11This crate requires nightly Rust, due to using "async fn in trait" support.
12
13## License
14
15This work is licensed under either of
16
17- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
18 <http://www.apache.org/licenses/LICENSE-2.0>)
19- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
20
21at your option.
22
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index fc16d2b41..484989949 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -349,11 +349,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
349 self.builder.config_descriptor.write(descriptor_type, descriptor) 349 self.builder.config_descriptor.write(descriptor_type, descriptor)
350 } 350 }
351 351
352 fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn { 352 fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
353 let ep = self 353 let ep = self
354 .builder 354 .builder
355 .driver 355 .driver
356 .alloc_endpoint_in(ep_type, max_packet_size, interval) 356 .alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
357 .expect("alloc_endpoint_in failed"); 357 .expect("alloc_endpoint_in failed");
358 358
359 self.builder.config_descriptor.endpoint(ep.info()); 359 self.builder.config_descriptor.endpoint(ep.info());
@@ -361,11 +361,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
361 ep 361 ep
362 } 362 }
363 363
364 fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut { 364 fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
365 let ep = self 365 let ep = self
366 .builder 366 .builder
367 .driver 367 .driver
368 .alloc_endpoint_out(ep_type, max_packet_size, interval) 368 .alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
369 .expect("alloc_endpoint_out failed"); 369 .expect("alloc_endpoint_out failed");
370 370
371 self.builder.config_descriptor.endpoint(ep.info()); 371 self.builder.config_descriptor.endpoint(ep.info());
@@ -393,25 +393,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
393 /// 393 ///
394 /// Descriptors are written in the order builder functions are called. Note that some 394 /// Descriptors are written in the order builder functions are called. Note that some
395 /// classes care about the order. 395 /// classes care about the order.
396 pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { 396 pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
397 self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval) 397 self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms)
398 } 398 }
399 399
400 /// Allocate a INTERRUPT OUT endpoint and write its descriptor. 400 /// Allocate a INTERRUPT OUT endpoint and write its descriptor.
401 pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { 401 pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
402 self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) 402 self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms)
403 } 403 }
404 404
405 /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. 405 /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
406 /// 406 ///
407 /// Descriptors are written in the order builder functions are called. Note that some 407 /// Descriptors are written in the order builder functions are called. Note that some
408 /// classes care about the order. 408 /// classes care about the order.
409 pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { 409 pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
410 self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) 410 self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms)
411 } 411 }
412 412
413 /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. 413 /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
414 pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { 414 pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
415 self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) 415 self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms)
416 } 416 }
417} 417}
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs
index 84db20621..09f17456c 100644
--- a/embassy-usb/src/class/cdc_acm.rs
+++ b/embassy-usb/src/class/cdc_acm.rs
@@ -1,3 +1,5 @@
1//! CDC-ACM class implementation, aka Serial over USB.
2
1use core::cell::Cell; 3use core::cell::Cell;
2use core::mem::{self, MaybeUninit}; 4use core::mem::{self, MaybeUninit};
3use core::sync::atomic::{AtomicBool, Ordering}; 5use core::sync::atomic::{AtomicBool, Ordering};
@@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20;
28const REQ_GET_LINE_CODING: u8 = 0x21; 30const REQ_GET_LINE_CODING: u8 = 0x21;
29const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; 31const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
30 32
33/// Internal state for CDC-ACM
31pub struct State<'a> { 34pub struct State<'a> {
32 control: MaybeUninit<Control<'a>>, 35 control: MaybeUninit<Control<'a>>,
33 shared: ControlShared, 36 shared: ControlShared,
34} 37}
35 38
36impl<'a> State<'a> { 39impl<'a> State<'a> {
40 /// Create a new `State`.
37 pub fn new() -> Self { 41 pub fn new() -> Self {
38 Self { 42 Self {
39 control: MaybeUninit::uninit(), 43 control: MaybeUninit::uninit(),
@@ -284,10 +288,15 @@ impl From<u8> for StopBits {
284#[derive(Copy, Clone, Debug, PartialEq, Eq)] 288#[derive(Copy, Clone, Debug, PartialEq, Eq)]
285#[cfg_attr(feature = "defmt", derive(defmt::Format))] 289#[cfg_attr(feature = "defmt", derive(defmt::Format))]
286pub enum ParityType { 290pub enum ParityType {
291 /// No parity bit.
287 None = 0, 292 None = 0,
293 /// Parity bit is 1 if the amount of `1` bits in the data byte is odd.
288 Odd = 1, 294 Odd = 1,
295 /// Parity bit is 1 if the amount of `1` bits in the data byte is even.
289 Even = 2, 296 Even = 2,
297 /// Parity bit is always 1
290 Mark = 3, 298 Mark = 3,
299 /// Parity bit is always 0
291 Space = 4, 300 Space = 4,
292} 301}
293 302
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index 501df2d8c..bc79b3671 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -1,3 +1,4 @@
1//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class.
1use embassy_futures::select::{select, Either}; 2use embassy_futures::select::{select, Either};
2use embassy_net_driver_channel as ch; 3use embassy_net_driver_channel as ch;
3use embassy_net_driver_channel::driver::LinkState; 4use embassy_net_driver_channel::driver::LinkState;
@@ -5,11 +6,13 @@ use embassy_usb_driver::Driver;
5 6
6use super::{CdcNcmClass, Receiver, Sender}; 7use super::{CdcNcmClass, Receiver, Sender};
7 8
9/// Internal state for the embassy-net integration.
8pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { 10pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
9 ch_state: ch::State<MTU, N_RX, N_TX>, 11 ch_state: ch::State<MTU, N_RX, N_TX>,
10} 12}
11 13
12impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> { 14impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {
15 /// Create a new `State`.
13 pub const fn new() -> Self { 16 pub const fn new() -> Self {
14 Self { 17 Self {
15 ch_state: ch::State::new(), 18 ch_state: ch::State::new(),
@@ -17,6 +20,9 @@ impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_
17 } 20 }
18} 21}
19 22
23/// Background runner for the CDC-NCM class.
24///
25/// You must call `.run()` in a background task for the class to operate.
20pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { 26pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
21 tx_usb: Sender<'d, D>, 27 tx_usb: Sender<'d, D>,
22 rx_usb: Receiver<'d, D>, 28 rx_usb: Receiver<'d, D>,
@@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
24} 30}
25 31
26impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { 32impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
33 /// Run the CDC-NCM class.
34 ///
35 /// You must call this in a background task for the class to operate.
27 pub async fn run(mut self) -> ! { 36 pub async fn run(mut self) -> ! {
28 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); 37 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
29 let rx_fut = async move { 38 let rx_fut = async move {
@@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
66 75
67// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? 76// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug?
68//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; 77//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd;
78/// Type alias for the embassy-net driver for CDC-NCM.
69pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; 79pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
70 80
71impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 81impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
82 /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net).
72 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( 83 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
73 self, 84 self,
74 state: &'d mut State<MTU, N_RX, N_TX>, 85 state: &'d mut State<MTU, N_RX, N_TX>,
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index 4954a65bc..5e59b72fe 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -1,18 +1,19 @@
1/// CDC-NCM, aka Ethernet over USB. 1//! CDC-NCM class implementation, aka Ethernet over USB.
2/// 2//!
3/// # Compatibility 3//! # Compatibility
4/// 4//!
5/// Windows: NOT supported in Windows 10. Supported in Windows 11. 5//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11.
6/// 6//!
7/// Linux: Well-supported since forever. 7//! Linux: Well-supported since forever.
8/// 8//!
9/// Android: Support for CDC-NCM is spotty and varies across manufacturers. 9//! Android: Support for CDC-NCM is spotty and varies across manufacturers.
10/// 10//!
11/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. 11//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
12/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), 12//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
13/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. 13//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
14/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 14//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
15/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 15//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
16
16use core::intrinsics::copy_nonoverlapping; 17use core::intrinsics::copy_nonoverlapping;
17use core::mem::{size_of, MaybeUninit}; 18use core::mem::{size_of, MaybeUninit};
18 19
@@ -114,6 +115,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
114 &buf[..len] 115 &buf[..len]
115} 116}
116 117
118/// Internal state for the CDC-NCM class.
117pub struct State<'a> { 119pub struct State<'a> {
118 comm_control: MaybeUninit<CommControl<'a>>, 120 comm_control: MaybeUninit<CommControl<'a>>,
119 data_control: MaybeUninit<DataControl>, 121 data_control: MaybeUninit<DataControl>,
@@ -121,6 +123,7 @@ pub struct State<'a> {
121} 123}
122 124
123impl<'a> State<'a> { 125impl<'a> State<'a> {
126 /// Create a new `State`.
124 pub fn new() -> Self { 127 pub fn new() -> Self {
125 Self { 128 Self {
126 comm_control: MaybeUninit::uninit(), 129 comm_control: MaybeUninit::uninit(),
@@ -223,6 +226,7 @@ impl ControlHandler for DataControl {
223 } 226 }
224} 227}
225 228
229/// CDC-NCM class
226pub struct CdcNcmClass<'d, D: Driver<'d>> { 230pub struct CdcNcmClass<'d, D: Driver<'d>> {
227 _comm_if: InterfaceNumber, 231 _comm_if: InterfaceNumber,
228 comm_ep: D::EndpointIn, 232 comm_ep: D::EndpointIn,
@@ -235,6 +239,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> {
235} 239}
236 240
237impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 241impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
242 /// Create a new CDC NCM class.
238 pub fn new( 243 pub fn new(
239 builder: &mut Builder<'d, D>, 244 builder: &mut Builder<'d, D>,
240 state: &'d mut State<'d>, 245 state: &'d mut State<'d>,
@@ -319,6 +324,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
319 } 324 }
320 } 325 }
321 326
327 /// Split the class into a sender and receiver.
328 ///
329 /// This allows concurrently sending and receiving packets from separate tasks.
322 pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { 330 pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
323 ( 331 (
324 Sender { 332 Sender {
@@ -334,12 +342,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
334 } 342 }
335} 343}
336 344
345/// CDC NCM class packet sender.
346///
347/// You can obtain a `Sender` with [`CdcNcmClass::split`]
337pub struct Sender<'d, D: Driver<'d>> { 348pub struct Sender<'d, D: Driver<'d>> {
338 write_ep: D::EndpointIn, 349 write_ep: D::EndpointIn,
339 seq: u16, 350 seq: u16,
340} 351}
341 352
342impl<'d, D: Driver<'d>> Sender<'d, D> { 353impl<'d, D: Driver<'d>> Sender<'d, D> {
354 /// Write a packet.
355 ///
356 /// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers.
343 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { 357 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
344 let seq = self.seq; 358 let seq = self.seq;
345 self.seq = self.seq.wrapping_add(1); 359 self.seq = self.seq.wrapping_add(1);
@@ -393,6 +407,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
393 } 407 }
394} 408}
395 409
410/// CDC NCM class packet receiver.
411///
412/// You can obtain a `Receiver` with [`CdcNcmClass::split`]
396pub struct Receiver<'d, D: Driver<'d>> { 413pub struct Receiver<'d, D: Driver<'d>> {
397 data_if: InterfaceNumber, 414 data_if: InterfaceNumber,
398 comm_ep: D::EndpointIn, 415 comm_ep: D::EndpointIn,
@@ -400,7 +417,9 @@ pub struct Receiver<'d, D: Driver<'d>> {
400} 417}
401 418
402impl<'d, D: Driver<'d>> Receiver<'d, D> { 419impl<'d, D: Driver<'d>> Receiver<'d, D> {
403 /// Reads a single packet from the OUT endpoint. 420 /// Write a network packet.
421 ///
422 /// This waits until a packet is succesfully received from the endpoint buffers.
404 pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { 423 pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
405 // Retry loop 424 // Retry loop
406 loop { 425 loop {
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs
index b967aba0e..2493061b0 100644
--- a/embassy-usb/src/class/hid.rs
+++ b/embassy-usb/src/class/hid.rs
@@ -1,3 +1,5 @@
1//! USB HID (Human Interface Device) class implementation.
2
1use core::mem::MaybeUninit; 3use core::mem::MaybeUninit;
2use core::ops::Range; 4use core::ops::Range;
3use core::sync::atomic::{AtomicUsize, Ordering}; 5use core::sync::atomic::{AtomicUsize, Ordering};
@@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09;
28const HID_REQ_GET_PROTOCOL: u8 = 0x03; 30const HID_REQ_GET_PROTOCOL: u8 = 0x03;
29const HID_REQ_SET_PROTOCOL: u8 = 0x0b; 31const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
30 32
33/// Configuration for the HID class.
31pub struct Config<'d> { 34pub struct Config<'d> {
32 /// HID report descriptor. 35 /// HID report descriptor.
33 pub report_descriptor: &'d [u8], 36 pub report_descriptor: &'d [u8],
@@ -46,11 +49,15 @@ pub struct Config<'d> {
46 pub max_packet_size: u16, 49 pub max_packet_size: u16,
47} 50}
48 51
52/// Report ID
49#[derive(Debug, Clone, Copy, PartialEq, Eq)] 53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))] 54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub enum ReportId { 55pub enum ReportId {
56 /// IN report
52 In(u8), 57 In(u8),
58 /// OUT report
53 Out(u8), 59 Out(u8),
60 /// Feature report
54 Feature(u8), 61 Feature(u8),
55} 62}
56 63
@@ -65,12 +72,14 @@ impl ReportId {
65 } 72 }
66} 73}
67 74
75/// Internal state for USB HID.
68pub struct State<'d> { 76pub struct State<'d> {
69 control: MaybeUninit<Control<'d>>, 77 control: MaybeUninit<Control<'d>>,
70 out_report_offset: AtomicUsize, 78 out_report_offset: AtomicUsize,
71} 79}
72 80
73impl<'d> State<'d> { 81impl<'d> State<'d> {
82 /// Create a new `State`.
74 pub fn new() -> Self { 83 pub fn new() -> Self {
75 State { 84 State {
76 control: MaybeUninit::uninit(), 85 control: MaybeUninit::uninit(),
@@ -79,6 +88,7 @@ impl<'d> State<'d> {
79 } 88 }
80} 89}
81 90
91/// USB HID reader/writer.
82pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { 92pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
83 reader: HidReader<'d, D, READ_N>, 93 reader: HidReader<'d, D, READ_N>,
84 writer: HidWriter<'d, D, WRITE_N>, 94 writer: HidWriter<'d, D, WRITE_N>,
@@ -180,20 +190,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit
180 } 190 }
181} 191}
182 192
193/// USB HID writer.
194///
195/// You can obtain a `HidWriter` using [`HidReaderWriter::split`].
183pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { 196pub struct HidWriter<'d, D: Driver<'d>, const N: usize> {
184 ep_in: D::EndpointIn, 197 ep_in: D::EndpointIn,
185} 198}
186 199
200/// USB HID reader.
201///
202/// You can obtain a `HidReader` using [`HidReaderWriter::split`].
187pub struct HidReader<'d, D: Driver<'d>, const N: usize> { 203pub struct HidReader<'d, D: Driver<'d>, const N: usize> {
188 ep_out: D::EndpointOut, 204 ep_out: D::EndpointOut,
189 offset: &'d AtomicUsize, 205 offset: &'d AtomicUsize,
190} 206}
191 207
208/// Error when reading a HID report.
192#[derive(Debug, Clone, PartialEq, Eq)] 209#[derive(Debug, Clone, PartialEq, Eq)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))] 210#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194pub enum ReadError { 211pub enum ReadError {
212 /// The given buffer was too small to read the received report.
195 BufferOverflow, 213 BufferOverflow,
214 /// The endpoint is disabled.
196 Disabled, 215 Disabled,
216 /// The report was only partially read. See [`HidReader::read`] for details.
197 Sync(Range<usize>), 217 Sync(Range<usize>),
198} 218}
199 219
@@ -344,6 +364,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
344 } 364 }
345} 365}
346 366
367/// Handler for HID-related control requests.
347pub trait RequestHandler { 368pub trait RequestHandler {
348 /// Reads the value of report `id` into `buf` returning the size. 369 /// Reads the value of report `id` into `buf` returning the size.
349 /// 370 ///
diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs
index af27577a6..b23e03d40 100644
--- a/embassy-usb/src/class/mod.rs
+++ b/embassy-usb/src/class/mod.rs
@@ -1,3 +1,4 @@
1//! Implementations of well-known USB classes.
1pub mod cdc_acm; 2pub mod cdc_acm;
2pub mod cdc_ncm; 3pub mod cdc_ncm;
3pub mod hid; 4pub mod hid;
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs
index d6d0c6565..39b499f03 100644
--- a/embassy-usb/src/control.rs
+++ b/embassy-usb/src/control.rs
@@ -126,17 +126,23 @@ impl Request {
126 } 126 }
127} 127}
128 128
129/// Response for a CONTROL OUT request.
129#[derive(Copy, Clone, Eq, PartialEq, Debug)] 130#[derive(Copy, Clone, Eq, PartialEq, Debug)]
130#[cfg_attr(feature = "defmt", derive(defmt::Format))] 131#[cfg_attr(feature = "defmt", derive(defmt::Format))]
131pub enum OutResponse { 132pub enum OutResponse {
133 /// The request was accepted.
132 Accepted, 134 Accepted,
135 /// The request was rejected.
133 Rejected, 136 Rejected,
134} 137}
135 138
139/// Response for a CONTROL IN request.
136#[derive(Copy, Clone, Eq, PartialEq, Debug)] 140#[derive(Copy, Clone, Eq, PartialEq, Debug)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))] 141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub enum InResponse<'a> { 142pub enum InResponse<'a> {
143 /// The request was accepted. The buffer contains the response data.
139 Accepted(&'a [u8]), 144 Accepted(&'a [u8]),
145 /// The request was rejected.
140 Rejected, 146 Rejected,
141} 147}
142 148
@@ -148,6 +154,7 @@ pub trait ControlHandler {
148 /// Called after a USB reset after the bus reset sequence is complete. 154 /// Called after a USB reset after the bus reset sequence is complete.
149 fn reset(&mut self) {} 155 fn reset(&mut self) {}
150 156
157 /// Called when a "set alternate setting" control request is done on the interface.
151 fn set_alternate_setting(&mut self, alternate_setting: u8) { 158 fn set_alternate_setting(&mut self, alternate_setting: u8) {
152 let _ = alternate_setting; 159 let _ = alternate_setting;
153 } 160 }
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs
index 497f03196..ae38e26ca 100644
--- a/embassy-usb/src/descriptor.rs
+++ b/embassy-usb/src/descriptor.rs
@@ -1,3 +1,5 @@
1//! Utilities for writing USB descriptors.
2
1use crate::builder::Config; 3use crate::builder::Config;
2use crate::driver::EndpointInfo; 4use crate::driver::EndpointInfo;
3use crate::types::*; 5use crate::types::*;
@@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> {
236 endpoint.ep_type as u8, // bmAttributes 238 endpoint.ep_type as u8, // bmAttributes
237 endpoint.max_packet_size as u8, 239 endpoint.max_packet_size as u8,
238 (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize 240 (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
239 endpoint.interval, // bInterval 241 endpoint.interval_ms, // bInterval
240 ], 242 ],
241 ); 243 );
242 } 244 }
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 096e8b07a..2656af29d 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -1,5 +1,7 @@
1#![no_std] 1#![no_std]
2#![feature(type_alias_impl_trait)] 2#![feature(type_alias_impl_trait)]
3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
3 5
4// This mod MUST go first, so that the others see its macros. 6// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt; 7pub(crate) mod fmt;
@@ -46,10 +48,13 @@ pub enum UsbDeviceState {
46 Configured, 48 Configured,
47} 49}
48 50
51/// Error returned by [`UsbDevice::remote_wakeup`].
49#[derive(PartialEq, Eq, Copy, Clone, Debug)] 52#[derive(PartialEq, Eq, Copy, Clone, Debug)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))] 53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub enum RemoteWakeupError { 54pub enum RemoteWakeupError {
55 /// The USB device is not suspended, or remote wakeup was not enabled.
52 InvalidState, 56 InvalidState,
57 /// The underlying driver doesn't support remote wakeup.
53 Unsupported, 58 Unsupported,
54} 59}
55 60
@@ -65,6 +70,7 @@ pub const CONFIGURATION_NONE: u8 = 0;
65/// The bConfiguration value for the single configuration supported by this device. 70/// The bConfiguration value for the single configuration supported by this device.
66pub const CONFIGURATION_VALUE: u8 = 1; 71pub const CONFIGURATION_VALUE: u8 = 1;
67 72
73/// Maximum interface count, configured at compile time.
68pub const MAX_INTERFACE_COUNT: usize = 4; 74pub const MAX_INTERFACE_COUNT: usize = 4;
69 75
70const STRING_INDEX_MANUFACTURER: u8 = 1; 76const STRING_INDEX_MANUFACTURER: u8 = 1;
@@ -100,6 +106,7 @@ struct Interface<'d> {
100 num_strings: u8, 106 num_strings: u8,
101} 107}
102 108
109/// Main struct for the USB device stack.
103pub struct UsbDevice<'d, D: Driver<'d>> { 110pub struct UsbDevice<'d, D: Driver<'d>> {
104 control_buf: &'d mut [u8], 111 control_buf: &'d mut [u8],
105 control: D::ControlPipe, 112 control: D::ControlPipe,
@@ -489,7 +496,6 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
489 .unwrap(); 496 .unwrap();
490 497
491 // TODO check it is valid (not out of range) 498 // TODO check it is valid (not out of range)
492 // TODO actually enable/disable endpoints.
493 499
494 if let Some(handler) = &mut iface.handler { 500 if let Some(handler) = &mut iface.handler {
495 handler.set_alternate_setting(new_altsetting); 501 handler.set_alternate_setting(new_altsetting);
diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs
index aeab063d1..1743e61ff 100644
--- a/embassy-usb/src/types.rs
+++ b/embassy-usb/src/types.rs
@@ -1,3 +1,5 @@
1//! USB types.
2
1/// A handle for a USB interface that contains its number. 3/// A handle for a USB interface that contains its number.
2#[derive(Copy, Clone, Eq, PartialEq)] 4#[derive(Copy, Clone, Eq, PartialEq)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))] 5#[cfg_attr(feature = "defmt", derive(defmt::Format))]