diff options
| -rw-r--r-- | embassy-nrf/src/usb.rs | 8 | ||||
| -rw-r--r-- | embassy-rp/src/usb.rs | 16 | ||||
| -rw-r--r-- | embassy-stm32/src/usb/usb.rs | 16 | ||||
| -rw-r--r-- | embassy-stm32/src/usb_otg/usb.rs | 16 | ||||
| -rw-r--r-- | embassy-usb-driver/README.md | 32 | ||||
| -rw-r--r-- | embassy-usb-driver/src/lib.rs | 116 | ||||
| -rw-r--r-- | embassy-usb/README.md | 22 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 24 | ||||
| -rw-r--r-- | embassy-usb/src/class/cdc_acm.rs | 9 | ||||
| -rw-r--r-- | embassy-usb/src/class/cdc_ncm/embassy_net.rs | 11 | ||||
| -rw-r--r-- | embassy-usb/src/class/cdc_ncm/mod.rs | 51 | ||||
| -rw-r--r-- | embassy-usb/src/class/hid.rs | 21 | ||||
| -rw-r--r-- | embassy-usb/src/class/mod.rs | 1 | ||||
| -rw-r--r-- | embassy-usb/src/control.rs | 7 | ||||
| -rw-r--r-- | embassy-usb/src/descriptor.rs | 4 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 8 | ||||
| -rw-r--r-- | embassy-usb/src/types.rs | 2 |
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 | |||
| 3 | This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these | ||
| 4 | traits to add support for using `embassy-usb` for a given chip/platform. | ||
| 5 | |||
| 6 | The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`] | ||
| 7 | APIs don't cause a semver-major bump of thsi crate. This allows existing HALs/BSPs to be used | ||
| 8 | with the newer `embassy-usb` without needing updates. | ||
| 9 | |||
| 10 | If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate | ||
| 11 | instead of this one. | ||
| 12 | |||
| 13 | [`embassy-usb`]: https://crates.io/crates/embassy-usb | ||
| 14 | |||
| 15 | ## Interoperability | ||
| 16 | |||
| 17 | This crate can run on any executor. | ||
| 18 | |||
| 19 | ## Minimum supported Rust version (MSRV) | ||
| 20 | |||
| 21 | This crate requires nightly Rust, due to using "async fn in trait" support. | ||
| 22 | |||
| 23 | ## License | ||
| 24 | |||
| 25 | This 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 | |||
| 31 | at 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))] |
| 100 | pub struct EndpointInfo { | 103 | pub 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. | ||
| 109 | pub trait Driver<'a> { | 117 | pub 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. | ||
| 157 | pub trait Bus { | 176 | pub 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. | ||
| 199 | pub trait Endpoint { | 223 | pub 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. | ||
| 207 | pub trait EndpointOut: Endpoint { | 232 | pub 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. | ||
| 314 | pub trait EndpointIn: Endpoint { | 348 | pub 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))] |
| 343 | pub struct EndpointAllocError; | 381 | pub 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. | ||
| 348 | pub struct Unsupported; | 386 | pub 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`] | ||
| 353 | pub enum EndpointError { | 391 | pub 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 | |||
| 3 | TODO crate description/ | ||
| 4 | |||
| 5 | ## Interoperability | ||
| 6 | |||
| 7 | This crate can run on any executor. | ||
| 8 | |||
| 9 | ## Minimum supported Rust version (MSRV) | ||
| 10 | |||
| 11 | This crate requires nightly Rust, due to using "async fn in trait" support. | ||
| 12 | |||
| 13 | ## License | ||
| 14 | |||
| 15 | This 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 | |||
| 21 | at 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 | |||
| 1 | use core::cell::Cell; | 3 | use core::cell::Cell; |
| 2 | use core::mem::{self, MaybeUninit}; | 4 | use core::mem::{self, MaybeUninit}; |
| 3 | use core::sync::atomic::{AtomicBool, Ordering}; | 5 | use core::sync::atomic::{AtomicBool, Ordering}; |
| @@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20; | |||
| 28 | const REQ_GET_LINE_CODING: u8 = 0x21; | 30 | const REQ_GET_LINE_CODING: u8 = 0x21; |
| 29 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | 31 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; |
| 30 | 32 | ||
| 33 | /// Internal state for CDC-ACM | ||
| 31 | pub struct State<'a> { | 34 | pub struct State<'a> { |
| 32 | control: MaybeUninit<Control<'a>>, | 35 | control: MaybeUninit<Control<'a>>, |
| 33 | shared: ControlShared, | 36 | shared: ControlShared, |
| 34 | } | 37 | } |
| 35 | 38 | ||
| 36 | impl<'a> State<'a> { | 39 | impl<'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))] |
| 286 | pub enum ParityType { | 290 | pub 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. | ||
| 1 | use embassy_futures::select::{select, Either}; | 2 | use embassy_futures::select::{select, Either}; |
| 2 | use embassy_net_driver_channel as ch; | 3 | use embassy_net_driver_channel as ch; |
| 3 | use embassy_net_driver_channel::driver::LinkState; | 4 | use embassy_net_driver_channel::driver::LinkState; |
| @@ -5,11 +6,13 @@ use embassy_usb_driver::Driver; | |||
| 5 | 6 | ||
| 6 | use super::{CdcNcmClass, Receiver, Sender}; | 7 | use super::{CdcNcmClass, Receiver, Sender}; |
| 7 | 8 | ||
| 9 | /// Internal state for the embassy-net integration. | ||
| 8 | pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { | 10 | pub 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 | ||
| 12 | impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> { | 14 | impl<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. | ||
| 20 | pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { | 26 | pub 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 | ||
| 26 | impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { | 32 | impl<'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. | ||
| 69 | pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; | 79 | pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; |
| 70 | 80 | ||
| 71 | impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | 81 | impl<'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 | |||
| 16 | use core::intrinsics::copy_nonoverlapping; | 17 | use core::intrinsics::copy_nonoverlapping; |
| 17 | use core::mem::{size_of, MaybeUninit}; | 18 | use 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. | ||
| 117 | pub struct State<'a> { | 119 | pub 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 | ||
| 123 | impl<'a> State<'a> { | 125 | impl<'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 | ||
| 226 | pub struct CdcNcmClass<'d, D: Driver<'d>> { | 230 | pub 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 | ||
| 237 | impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { | 241 | impl<'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`] | ||
| 337 | pub struct Sender<'d, D: Driver<'d>> { | 348 | pub 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 | ||
| 342 | impl<'d, D: Driver<'d>> Sender<'d, D> { | 353 | impl<'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`] | ||
| 396 | pub struct Receiver<'d, D: Driver<'d>> { | 413 | pub 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 | ||
| 402 | impl<'d, D: Driver<'d>> Receiver<'d, D> { | 419 | impl<'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 | |||
| 1 | use core::mem::MaybeUninit; | 3 | use core::mem::MaybeUninit; |
| 2 | use core::ops::Range; | 4 | use core::ops::Range; |
| 3 | use core::sync::atomic::{AtomicUsize, Ordering}; | 5 | use core::sync::atomic::{AtomicUsize, Ordering}; |
| @@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09; | |||
| 28 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; | 30 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; |
| 29 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; | 31 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; |
| 30 | 32 | ||
| 33 | /// Configuration for the HID class. | ||
| 31 | pub struct Config<'d> { | 34 | pub 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))] |
| 51 | pub enum ReportId { | 55 | pub 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. | ||
| 68 | pub struct State<'d> { | 76 | pub 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 | ||
| 73 | impl<'d> State<'d> { | 81 | impl<'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. | ||
| 82 | pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { | 92 | pub 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`]. | ||
| 183 | pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { | 196 | pub 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`]. | ||
| 187 | pub struct HidReader<'d, D: Driver<'d>, const N: usize> { | 203 | pub 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))] |
| 194 | pub enum ReadError { | 211 | pub 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. | ||
| 347 | pub trait RequestHandler { | 368 | pub 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. | ||
| 1 | pub mod cdc_acm; | 2 | pub mod cdc_acm; |
| 2 | pub mod cdc_ncm; | 3 | pub mod cdc_ncm; |
| 3 | pub mod hid; | 4 | pub 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))] |
| 131 | pub enum OutResponse { | 132 | pub 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))] |
| 138 | pub enum InResponse<'a> { | 142 | pub 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 | |||
| 1 | use crate::builder::Config; | 3 | use crate::builder::Config; |
| 2 | use crate::driver::EndpointInfo; | 4 | use crate::driver::EndpointInfo; |
| 3 | use crate::types::*; | 5 | use 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. |
| 5 | pub(crate) mod fmt; | 7 | pub(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))] |
| 51 | pub enum RemoteWakeupError { | 54 | pub 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. |
| 66 | pub const CONFIGURATION_VALUE: u8 = 1; | 71 | pub const CONFIGURATION_VALUE: u8 = 1; |
| 67 | 72 | ||
| 73 | /// Maximum interface count, configured at compile time. | ||
| 68 | pub const MAX_INTERFACE_COUNT: usize = 4; | 74 | pub const MAX_INTERFACE_COUNT: usize = 4; |
| 69 | 75 | ||
| 70 | const STRING_INDEX_MANUFACTURER: u8 = 1; | 76 | const 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. | ||
| 103 | pub struct UsbDevice<'d, D: Driver<'d>> { | 110 | pub 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))] |
