| 1 | #![no_std ] |
| 2 | #![allow (async_fn_in_trait)] |
| 3 | #![doc = include_str!("../README.md" )] |
| 4 | #![warn (missing_docs)] |
| 5 | |
| 6 | /// Direction of USB traffic. Note that in the USB standard the direction is always indicated from |
| 7 | /// the perspective of the host, which is backward for devices, but the standard directions are used |
| 8 | /// for consistency. |
| 9 | /// |
| 10 | /// The values of the enum also match the direction bit used in endpoint addresses and control |
| 11 | /// request types. |
| 12 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 13 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 14 | pub enum Direction { |
| 15 | /// Host to device (OUT) |
| 16 | Out, |
| 17 | /// Device to host (IN) |
| 18 | In, |
| 19 | } |
| 20 | |
| 21 | /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the |
| 22 | /// transfer bmAttributes transfer type bits. |
| 23 | #[repr (u8)] |
| 24 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 25 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 26 | pub enum EndpointType { |
| 27 | /// Control endpoint. Used for device management. Only the host can initiate requests. Usually |
| 28 | /// used only endpoint 0. |
| 29 | Control = 0b00, |
| 30 | /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. |
| 31 | Isochronous = 0b01, |
| 32 | /// Bulk endpoint. Used for large amounts of best-effort reliable data. |
| 33 | Bulk = 0b10, |
| 34 | /// Interrupt endpoint. Used for small amounts of time-critical reliable data. |
| 35 | Interrupt = 0b11, |
| 36 | } |
| 37 | |
| 38 | /// Type-safe endpoint address. |
| 39 | #[derive (Debug, Clone, Copy, Eq, PartialEq)] |
| 40 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 41 | pub struct EndpointAddress(u8); |
| 42 | |
| 43 | impl From<u8> for EndpointAddress { |
| 44 | #[inline ] |
| 45 | fn from(addr: u8) -> EndpointAddress { |
| 46 | EndpointAddress(addr) |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | impl From<EndpointAddress> for u8 { |
| 51 | #[inline ] |
| 52 | fn from(addr: EndpointAddress) -> u8 { |
| 53 | addr.0 |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | impl EndpointAddress { |
| 58 | const INBITS: u8 = 0x80; |
| 59 | |
| 60 | /// Constructs a new EndpointAddress with the given index and direction. |
| 61 | #[inline ] |
| 62 | pub fn from_parts(index: usize, dir: Direction) -> Self { |
| 63 | let dir_u8 = match dir { |
| 64 | Direction::Out => 0x00, |
| 65 | Direction::In => Self::INBITS, |
| 66 | }; |
| 67 | EndpointAddress(index as u8 | dir_u8) |
| 68 | } |
| 69 | |
| 70 | /// Gets the direction part of the address. |
| 71 | #[inline ] |
| 72 | pub fn direction(&self) -> Direction { |
| 73 | if (self.0 & Self::INBITS) != 0 { |
| 74 | Direction::In |
| 75 | } else { |
| 76 | Direction::Out |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /// Returns true if the direction is IN, otherwise false. |
| 81 | #[inline ] |
| 82 | pub fn is_in(&self) -> bool { |
| 83 | (self.0 & Self::INBITS) != 0 |
| 84 | } |
| 85 | |
| 86 | /// Returns true if the direction is OUT, otherwise false. |
| 87 | #[inline ] |
| 88 | pub fn is_out(&self) -> bool { |
| 89 | (self.0 & Self::INBITS) == 0 |
| 90 | } |
| 91 | |
| 92 | /// Gets the index part of the endpoint address. |
| 93 | #[inline ] |
| 94 | pub fn index(&self) -> usize { |
| 95 | (self.0 & !Self::INBITS) as usize |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /// Information for an endpoint. |
| 100 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 101 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 102 | pub struct EndpointInfo { |
| 103 | /// Endpoint's address. |
| 104 | pub addr: EndpointAddress, |
| 105 | /// Endpoint's type. |
| 106 | pub ep_type: EndpointType, |
| 107 | /// Max packet size, in bytes. |
| 108 | pub max_packet_size: u16, |
| 109 | /// Polling interval, in milliseconds. |
| 110 | pub interval_ms: u8, |
| 111 | } |
| 112 | |
| 113 | /// Main USB driver trait. |
| 114 | /// |
| 115 | /// Implement this to add support for a new hardware platform. |
| 116 | pub trait Driver<'a> { |
| 117 | /// Type of the OUT endpoints for this driver. |
| 118 | type EndpointOut: EndpointOut + 'a; |
| 119 | /// Type of the IN endpoints for this driver. |
| 120 | type EndpointIn: EndpointIn + 'a; |
| 121 | /// Type of the control pipe for this driver. |
| 122 | type ControlPipe: ControlPipe + 'a; |
| 123 | /// Type for bus control for this driver. |
| 124 | type Bus: Bus + 'a; |
| 125 | |
| 126 | /// Allocates an OUT endpoint. |
| 127 | /// |
| 128 | /// This method is called by the USB stack to allocate endpoints. |
| 129 | /// It can only be called before [`start`](Self::start) is called. |
| 130 | /// |
| 131 | /// # Arguments |
| 132 | /// |
| 133 | /// * `ep_type` - the endpoint's type. |
| 134 | /// * `max_packet_size` - Maximum packet size in bytes. |
| 135 | /// * `interval_ms` - Polling interval parameter for interrupt endpoints. |
| 136 | fn alloc_endpoint_out( |
| 137 | &mut self, |
| 138 | ep_type: EndpointType, |
| 139 | max_packet_size: u16, |
| 140 | interval_ms: u8, |
| 141 | ) -> Result<Self::EndpointOut, EndpointAllocError>; |
| 142 | |
| 143 | /// Allocates an IN endpoint. |
| 144 | /// |
| 145 | /// This method is called by the USB stack to allocate endpoints. |
| 146 | /// It can only be called before [`start`](Self::start) is called. |
| 147 | /// |
| 148 | /// # Arguments |
| 149 | /// |
| 150 | /// * `ep_type` - the endpoint's type. |
| 151 | /// * `max_packet_size` - Maximum packet size in bytes. |
| 152 | /// * `interval_ms` - Polling interval parameter for interrupt endpoints. |
| 153 | fn alloc_endpoint_in( |
| 154 | &mut self, |
| 155 | ep_type: EndpointType, |
| 156 | max_packet_size: u16, |
| 157 | interval_ms: u8, |
| 158 | ) -> Result<Self::EndpointIn, EndpointAllocError>; |
| 159 | |
| 160 | /// Start operation of the USB device. |
| 161 | /// |
| 162 | /// This returns the `Bus` and `ControlPipe` instances that are used to operate |
| 163 | /// the USB device. Additionally, this makes all the previously allocated endpoints |
| 164 | /// start operating. |
| 165 | /// |
| 166 | /// This consumes the `Driver` instance, so it's no longer possible to allocate more |
| 167 | /// endpoints. |
| 168 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe); |
| 169 | } |
| 170 | |
| 171 | /// USB bus trait. |
| 172 | /// |
| 173 | /// This trait provides methods that act on the whole bus. It is kept owned by |
| 174 | /// the main USB task, and used to manage the bus. |
| 175 | pub trait Bus { |
| 176 | /// Enable the USB peripheral. |
| 177 | async fn enable(&mut self); |
| 178 | |
| 179 | /// Disable and powers down the USB peripheral. |
| 180 | async fn disable(&mut self); |
| 181 | |
| 182 | /// Wait for a bus-related event. |
| 183 | /// |
| 184 | /// This method should asynchronously wait for an event to happen, then |
| 185 | /// return it. See [`Event`] for the list of events this method should return. |
| 186 | async fn poll(&mut self) -> Event; |
| 187 | |
| 188 | /// Enable or disable an endpoint. |
| 189 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); |
| 190 | |
| 191 | /// Set or clear the STALL condition for an endpoint. |
| 192 | /// |
| 193 | /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. |
| 194 | fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); |
| 195 | |
| 196 | /// Get whether the STALL condition is set for an endpoint. |
| 197 | fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; |
| 198 | |
| 199 | /// Simulate a disconnect from the USB bus, causing the host to reset and re-enumerate the |
| 200 | /// device. |
| 201 | /// |
| 202 | /// The default implementation just returns `Unsupported`. |
| 203 | /// |
| 204 | /// # Errors |
| 205 | /// |
| 206 | /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support |
| 207 | /// simulating a disconnect or it has not been enabled at creation time. |
| 208 | fn force_reset(&mut self) -> Result<(), Unsupported> { |
| 209 | Err(Unsupported) |
| 210 | } |
| 211 | |
| 212 | /// Initiate a remote wakeup of the host by the device. |
| 213 | /// |
| 214 | /// # Errors |
| 215 | /// |
| 216 | /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support |
| 217 | /// remote wakeup or it has not been enabled at creation time. |
| 218 | async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; |
| 219 | } |
| 220 | |
| 221 | /// Endpoint trait, common for OUT and IN. |
| 222 | pub trait Endpoint { |
| 223 | /// Get the endpoint address |
| 224 | fn info(&self) -> &EndpointInfo; |
| 225 | |
| 226 | /// Wait for the endpoint to be enabled. |
| 227 | async fn wait_enabled(&mut self); |
| 228 | } |
| 229 | |
| 230 | /// OUT Endpoint trait. |
| 231 | pub trait EndpointOut: Endpoint { |
| 232 | /// Read a single packet of data from the endpoint, and return the actual length of |
| 233 | /// the packet. |
| 234 | /// |
| 235 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. |
| 236 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>; |
| 237 | } |
| 238 | |
| 239 | /// USB control pipe trait. |
| 240 | /// |
| 241 | /// The USB control pipe owns both OUT endpoint 0 and IN endpoint 0 in a single |
| 242 | /// unit, and manages them together to implement the control pipe state machine. |
| 243 | /// |
| 244 | /// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that |
| 245 | /// many USB peripherals treat the control pipe endpoints differently (different registers, |
| 246 | /// different procedures), usually to accelerate processing in hardware somehow. A separate |
| 247 | /// trait allows the driver to handle it specially. |
| 248 | /// |
| 249 | /// The call sequences made by the USB stack to the ControlPipe are the following: |
| 250 | /// |
| 251 | /// - control in/out with len=0: |
| 252 | /// |
| 253 | /// ```not_rust |
| 254 | /// setup() |
| 255 | /// (...processing...) |
| 256 | /// accept() or reject() |
| 257 | /// ``` |
| 258 | /// |
| 259 | /// - control out for setting the device address: |
| 260 | /// |
| 261 | /// ```not_rust |
| 262 | /// setup() |
| 263 | /// (...processing...) |
| 264 | /// accept_set_address(addr) or reject() |
| 265 | /// ``` |
| 266 | /// |
| 267 | /// - control out with len != 0: |
| 268 | /// |
| 269 | /// ```not_rust |
| 270 | /// setup() |
| 271 | /// data_out(first=true, last=false) |
| 272 | /// data_out(first=false, last=false) |
| 273 | /// ... |
| 274 | /// data_out(first=false, last=false) |
| 275 | /// data_out(first=false, last=true) |
| 276 | /// (...processing...) |
| 277 | /// accept() or reject() |
| 278 | /// ``` |
| 279 | /// |
| 280 | /// - control in with len != 0, accepted: |
| 281 | /// |
| 282 | /// ```not_rust |
| 283 | /// setup() |
| 284 | /// (...processing...) |
| 285 | /// data_in(first=true, last=false) |
| 286 | /// data_in(first=false, last=false) |
| 287 | /// ... |
| 288 | /// data_in(first=false, last=false) |
| 289 | /// data_in(first=false, last=true) |
| 290 | /// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.) |
| 291 | /// ``` |
| 292 | /// |
| 293 | /// - control in with len != 0, rejected: |
| 294 | /// |
| 295 | /// ```not_rust |
| 296 | /// setup() |
| 297 | /// (...processing...) |
| 298 | /// reject() |
| 299 | /// ``` |
| 300 | /// |
| 301 | /// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length |
| 302 | /// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should |
| 303 | /// be triggered by either `accept()`, or `data_in` with `last = true`. |
| 304 | /// |
| 305 | /// Note that the host can abandon a control request and send a new SETUP packet any time. If |
| 306 | /// a SETUP packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`, |
| 307 | /// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`) |
| 308 | /// to let the stack call `setup()` again to start handling the new control request. Not doing |
| 309 | /// so will cause things to get stuck, because the host will never read/send the packet we're |
| 310 | /// waiting for. |
| 311 | pub trait ControlPipe { |
| 312 | /// Maximum packet size for the control pipe |
| 313 | fn max_packet_size(&self) -> usize; |
| 314 | |
| 315 | /// Read a single setup packet from the endpoint. |
| 316 | async fn setup(&mut self) -> [u8; 8]; |
| 317 | |
| 318 | /// Read a DATA OUT packet into `buf` in response to a control write request. |
| 319 | /// |
| 320 | /// Must be called after `setup()` for requests with `direction` of `Out` |
| 321 | /// and `length` greater than zero. |
| 322 | async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>; |
| 323 | |
| 324 | /// Send a DATA IN packet with `data` in response to a control read request. |
| 325 | /// |
| 326 | /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. |
| 327 | async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>; |
| 328 | |
| 329 | /// Accept a control request. |
| 330 | /// |
| 331 | /// Causes the STATUS packet for the current request to be ACKed. |
| 332 | async fn accept(&mut self); |
| 333 | |
| 334 | /// Reject a control request. |
| 335 | /// |
| 336 | /// Sets a STALL condition on the pipe to indicate an error. |
| 337 | async fn reject(&mut self); |
| 338 | |
| 339 | /// Accept SET_ADDRESS control and change bus address. |
| 340 | /// |
| 341 | /// For most drivers this function should firstly call `accept()` and then change the bus address. |
| 342 | /// However, there are peripherals (Synopsys USB OTG) that have reverse order. |
| 343 | async fn accept_set_address(&mut self, addr: u8); |
| 344 | } |
| 345 | |
| 346 | /// IN Endpoint trait. |
| 347 | pub trait EndpointIn: Endpoint { |
| 348 | /// Write a single packet of data to the endpoint. |
| 349 | async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; |
| 350 | } |
| 351 | |
| 352 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 353 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 354 | /// Event returned by [`Bus::poll`]. |
| 355 | pub enum Event { |
| 356 | /// The USB reset condition has been detected. |
| 357 | Reset, |
| 358 | |
| 359 | /// A USB suspend request has been detected or, in the case of self-powered devices, the device |
| 360 | /// has been disconnected from the USB bus. |
| 361 | Suspend, |
| 362 | |
| 363 | /// A USB resume request has been detected after being suspended or, in the case of self-powered |
| 364 | /// devices, the device has been connected to the USB bus. |
| 365 | Resume, |
| 366 | |
| 367 | /// The USB power has been detected. |
| 368 | PowerDetected, |
| 369 | |
| 370 | /// The USB power has been removed. Not supported by all devices. |
| 371 | PowerRemoved, |
| 372 | } |
| 373 | |
| 374 | /// Allocating an endpoint failed. |
| 375 | /// |
| 376 | /// This can be due to running out of endpoints, or out of endpoint memory, |
| 377 | /// or because the hardware doesn't support the requested combination of features. |
| 378 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 379 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 380 | pub struct EndpointAllocError; |
| 381 | |
| 382 | /// Operation is unsupported by the driver. |
| 383 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 384 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 385 | pub struct Unsupported; |
| 386 | |
| 387 | /// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] |
| 388 | #[derive (Copy, Clone, Eq, PartialEq, Debug)] |
| 389 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 390 | pub enum EndpointError { |
| 391 | /// Either the packet to be written is too long to fit in the transmission |
| 392 | /// buffer or the received packet is too long to fit in `buf`. |
| 393 | BufferOverflow, |
| 394 | |
| 395 | /// The endpoint is disabled. |
| 396 | Disabled, |
| 397 | } |
| 398 | |