diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-01-31 22:27:19 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-01-31 22:27:19 +0100 |
| commit | ca10fe7135d10084e38038f3cd433da39e505bea (patch) | |
| tree | 075aca4a76caccd1bba95869c64bbb838969c8b1 /embassy-usb-driver | |
| parent | 4c1946454874597c358e7c7d5bf555b687376a5b (diff) | |
usb: docs
Diffstat (limited to 'embassy-usb-driver')
| -rw-r--r-- | embassy-usb-driver/README.md | 32 | ||||
| -rw-r--r-- | embassy-usb-driver/src/lib.rs | 116 |
2 files changed, 109 insertions, 39 deletions
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`. |
