diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-09-26 12:29:27 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-09-26 12:29:27 +0200 |
| commit | 7f7c14b7bce5b84eb27c8122535a96a6f0e5dd77 (patch) | |
| tree | c7481fd07b616128718301de7aafea553cc8dd6f /embassy-usb-driver/src | |
| parent | a9efbf18c62186de0581ec72cca90a69340a02a3 (diff) | |
usb: split driver trait to separate crate.
Diffstat (limited to 'embassy-usb-driver/src')
| -rw-r--r-- | embassy-usb-driver/src/lib.rs | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs new file mode 100644 index 000000000..051190a48 --- /dev/null +++ b/embassy-usb-driver/src/lib.rs | |||
| @@ -0,0 +1,337 @@ | |||
| 1 | #![no_std] | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | |||
| 5 | /// 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 | ||
| 7 | /// for consistency. | ||
| 8 | /// | ||
| 9 | /// The values of the enum also match the direction bit used in endpoint addresses and control | ||
| 10 | /// request types. | ||
| 11 | #[repr(u8)] | ||
| 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 = 0x00, | ||
| 17 | /// Device to host (IN) | ||
| 18 | In = 0x80, | ||
| 19 | } | ||
| 20 | |||
| 21 | impl From<u8> for Direction { | ||
| 22 | fn from(value: u8) -> Self { | ||
| 23 | unsafe { core::mem::transmute(value & 0x80) } | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the | ||
| 28 | /// transfer bmAttributes transfer type bits. | ||
| 29 | #[repr(u8)] | ||
| 30 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 31 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 32 | pub enum EndpointType { | ||
| 33 | /// Control endpoint. Used for device management. Only the host can initiate requests. Usually | ||
| 34 | /// used only endpoint 0. | ||
| 35 | Control = 0b00, | ||
| 36 | /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. | ||
| 37 | Isochronous = 0b01, | ||
| 38 | /// Bulk endpoint. Used for large amounts of best-effort reliable data. | ||
| 39 | Bulk = 0b10, | ||
| 40 | /// Interrupt endpoint. Used for small amounts of time-critical reliable data. | ||
| 41 | Interrupt = 0b11, | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Type-safe endpoint address. | ||
| 45 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| 46 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 47 | pub struct EndpointAddress(u8); | ||
| 48 | |||
| 49 | impl From<u8> for EndpointAddress { | ||
| 50 | #[inline] | ||
| 51 | fn from(addr: u8) -> EndpointAddress { | ||
| 52 | EndpointAddress(addr) | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | impl From<EndpointAddress> for u8 { | ||
| 57 | #[inline] | ||
| 58 | fn from(addr: EndpointAddress) -> u8 { | ||
| 59 | addr.0 | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | impl EndpointAddress { | ||
| 64 | const INBITS: u8 = Direction::In as u8; | ||
| 65 | |||
| 66 | /// Constructs a new EndpointAddress with the given index and direction. | ||
| 67 | #[inline] | ||
| 68 | pub fn from_parts(index: usize, dir: Direction) -> Self { | ||
| 69 | EndpointAddress(index as u8 | dir as u8) | ||
| 70 | } | ||
| 71 | |||
| 72 | /// Gets the direction part of the address. | ||
| 73 | #[inline] | ||
| 74 | pub fn direction(&self) -> Direction { | ||
| 75 | if (self.0 & Self::INBITS) != 0 { | ||
| 76 | Direction::In | ||
| 77 | } else { | ||
| 78 | Direction::Out | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Returns true if the direction is IN, otherwise false. | ||
| 83 | #[inline] | ||
| 84 | pub fn is_in(&self) -> bool { | ||
| 85 | (self.0 & Self::INBITS) != 0 | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Returns true if the direction is OUT, otherwise false. | ||
| 89 | #[inline] | ||
| 90 | pub fn is_out(&self) -> bool { | ||
| 91 | (self.0 & Self::INBITS) == 0 | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Gets the index part of the endpoint address. | ||
| 95 | #[inline] | ||
| 96 | pub fn index(&self) -> usize { | ||
| 97 | (self.0 & !Self::INBITS) as usize | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 102 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 103 | pub struct EndpointInfo { | ||
| 104 | pub addr: EndpointAddress, | ||
| 105 | pub ep_type: EndpointType, | ||
| 106 | pub max_packet_size: u16, | ||
| 107 | pub interval: u8, | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Driver for a specific USB peripheral. Implement this to add support for a new hardware | ||
| 111 | /// platform. | ||
| 112 | pub trait Driver<'a> { | ||
| 113 | type EndpointOut: EndpointOut + 'a; | ||
| 114 | type EndpointIn: EndpointIn + 'a; | ||
| 115 | type ControlPipe: ControlPipe + 'a; | ||
| 116 | type Bus: Bus + 'a; | ||
| 117 | |||
| 118 | /// Allocates an endpoint and specified endpoint parameters. This method is called by the device | ||
| 119 | /// and class implementations to allocate endpoints, and can only be called before | ||
| 120 | /// [`start`](Self::start) is called. | ||
| 121 | /// | ||
| 122 | /// # Arguments | ||
| 123 | /// | ||
| 124 | /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should | ||
| 125 | /// attempt to return an endpoint with the specified address. If None, the implementation | ||
| 126 | /// should return the next available one. | ||
| 127 | /// * `max_packet_size` - Maximum packet size in bytes. | ||
| 128 | /// * `interval` - Polling interval parameter for interrupt endpoints. | ||
| 129 | fn alloc_endpoint_out( | ||
| 130 | &mut self, | ||
| 131 | ep_type: EndpointType, | ||
| 132 | max_packet_size: u16, | ||
| 133 | interval: u8, | ||
| 134 | ) -> Result<Self::EndpointOut, EndpointAllocError>; | ||
| 135 | |||
| 136 | fn alloc_endpoint_in( | ||
| 137 | &mut self, | ||
| 138 | ep_type: EndpointType, | ||
| 139 | max_packet_size: u16, | ||
| 140 | interval: u8, | ||
| 141 | ) -> Result<Self::EndpointIn, EndpointAllocError>; | ||
| 142 | |||
| 143 | /// Start operation of the USB device. | ||
| 144 | /// | ||
| 145 | /// This returns the `Bus` and `ControlPipe` instances that are used to operate | ||
| 146 | /// the USB device. Additionally, this makes all the previously allocated endpoints | ||
| 147 | /// start operating. | ||
| 148 | /// | ||
| 149 | /// This consumes the `Driver` instance, so it's no longer possible to allocate more | ||
| 150 | /// endpoints. | ||
| 151 | fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe); | ||
| 152 | |||
| 153 | /// Indicates that `set_device_address` must be called before accepting the corresponding | ||
| 154 | /// control transfer, not after. | ||
| 155 | /// | ||
| 156 | /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6 | ||
| 157 | const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false; | ||
| 158 | } | ||
| 159 | |||
| 160 | pub trait Bus { | ||
| 161 | type EnableFuture<'a>: Future<Output = ()> + 'a | ||
| 162 | where | ||
| 163 | Self: 'a; | ||
| 164 | type DisableFuture<'a>: Future<Output = ()> + 'a | ||
| 165 | where | ||
| 166 | Self: 'a; | ||
| 167 | type PollFuture<'a>: Future<Output = Event> + 'a | ||
| 168 | where | ||
| 169 | Self: 'a; | ||
| 170 | type RemoteWakeupFuture<'a>: Future<Output = Result<(), Unsupported>> + 'a | ||
| 171 | where | ||
| 172 | Self: 'a; | ||
| 173 | |||
| 174 | /// Enables the USB peripheral. Soon after enabling the device will be reset, so | ||
| 175 | /// there is no need to perform a USB reset in this method. | ||
| 176 | fn enable(&mut self) -> Self::EnableFuture<'_>; | ||
| 177 | |||
| 178 | /// Disables and powers down the USB peripheral. | ||
| 179 | fn disable(&mut self) -> Self::DisableFuture<'_>; | ||
| 180 | |||
| 181 | fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>; | ||
| 182 | |||
| 183 | /// Sets the device USB address to `addr`. | ||
| 184 | fn set_address(&mut self, addr: u8); | ||
| 185 | |||
| 186 | /// Enables or disables an endpoint. | ||
| 187 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); | ||
| 188 | |||
| 189 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | ||
| 190 | /// should be prepared to receive data again. Only used during control transfers. | ||
| 191 | fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); | ||
| 192 | |||
| 193 | /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. | ||
| 194 | fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; | ||
| 195 | |||
| 196 | /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the | ||
| 197 | /// device. | ||
| 198 | /// | ||
| 199 | /// The default implementation just returns `Unsupported`. | ||
| 200 | /// | ||
| 201 | /// # Errors | ||
| 202 | /// | ||
| 203 | /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support | ||
| 204 | /// simulating a disconnect or it has not been enabled at creation time. | ||
| 205 | fn force_reset(&mut self) -> Result<(), Unsupported> { | ||
| 206 | Err(Unsupported) | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Initiates a remote wakeup of the host by the device. | ||
| 210 | /// | ||
| 211 | /// # Errors | ||
| 212 | /// | ||
| 213 | /// * [`Unsupported`](crate::driver::Unsupported) - This UsbBus implementation doesn't support | ||
| 214 | /// remote wakeup or it has not been enabled at creation time. | ||
| 215 | fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_>; | ||
| 216 | } | ||
| 217 | |||
| 218 | pub trait Endpoint { | ||
| 219 | type WaitEnabledFuture<'a>: Future<Output = ()> + 'a | ||
| 220 | where | ||
| 221 | Self: 'a; | ||
| 222 | |||
| 223 | /// Get the endpoint address | ||
| 224 | fn info(&self) -> &EndpointInfo; | ||
| 225 | |||
| 226 | /// Waits for the endpoint to be enabled. | ||
| 227 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>; | ||
| 228 | } | ||
| 229 | |||
| 230 | pub trait EndpointOut: Endpoint { | ||
| 231 | type ReadFuture<'a>: Future<Output = Result<usize, EndpointError>> + 'a | ||
| 232 | where | ||
| 233 | Self: 'a; | ||
| 234 | |||
| 235 | /// Reads a single packet of data from the endpoint, and returns the actual length of | ||
| 236 | /// the packet. | ||
| 237 | /// | ||
| 238 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. | ||
| 239 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; | ||
| 240 | } | ||
| 241 | |||
| 242 | pub trait ControlPipe { | ||
| 243 | type SetupFuture<'a>: Future<Output = [u8; 8]> + 'a | ||
| 244 | where | ||
| 245 | Self: 'a; | ||
| 246 | type DataOutFuture<'a>: Future<Output = Result<usize, EndpointError>> + 'a | ||
| 247 | where | ||
| 248 | Self: 'a; | ||
| 249 | type DataInFuture<'a>: Future<Output = Result<(), EndpointError>> + 'a | ||
| 250 | where | ||
| 251 | Self: 'a; | ||
| 252 | type AcceptFuture<'a>: Future<Output = ()> + 'a | ||
| 253 | where | ||
| 254 | Self: 'a; | ||
| 255 | type RejectFuture<'a>: Future<Output = ()> + 'a | ||
| 256 | where | ||
| 257 | Self: 'a; | ||
| 258 | |||
| 259 | /// Maximum packet size for the control pipe | ||
| 260 | fn max_packet_size(&self) -> usize; | ||
| 261 | |||
| 262 | /// Reads a single setup packet from the endpoint. | ||
| 263 | fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a>; | ||
| 264 | |||
| 265 | /// Reads a DATA OUT packet into `buf` in response to a control write request. | ||
| 266 | /// | ||
| 267 | /// Must be called after `setup()` for requests with `direction` of `Out` | ||
| 268 | /// and `length` greater than zero. | ||
| 269 | fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a>; | ||
| 270 | |||
| 271 | /// Sends a DATA IN packet with `data` in response to a control read request. | ||
| 272 | /// | ||
| 273 | /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. | ||
| 274 | fn data_in<'a>(&'a mut self, data: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a>; | ||
| 275 | |||
| 276 | /// Accepts a control request. | ||
| 277 | /// | ||
| 278 | /// Causes the STATUS packet for the current request to be ACKed. | ||
| 279 | fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a>; | ||
| 280 | |||
| 281 | /// Rejects a control request. | ||
| 282 | /// | ||
| 283 | /// Sets a STALL condition on the pipe to indicate an error. | ||
| 284 | fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a>; | ||
| 285 | } | ||
| 286 | |||
| 287 | pub trait EndpointIn: Endpoint { | ||
| 288 | type WriteFuture<'a>: Future<Output = Result<(), EndpointError>> + 'a | ||
| 289 | where | ||
| 290 | Self: 'a; | ||
| 291 | |||
| 292 | /// Writes a single packet of data to the endpoint. | ||
| 293 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; | ||
| 294 | } | ||
| 295 | |||
| 296 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 297 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 298 | /// Event returned by [`Bus::poll`]. | ||
| 299 | pub enum Event { | ||
| 300 | /// The USB reset condition has been detected. | ||
| 301 | Reset, | ||
| 302 | |||
| 303 | /// A USB suspend request has been detected or, in the case of self-powered devices, the device | ||
| 304 | /// has been disconnected from the USB bus. | ||
| 305 | Suspend, | ||
| 306 | |||
| 307 | /// A USB resume request has been detected after being suspended or, in the case of self-powered | ||
| 308 | /// devices, the device has been connected to the USB bus. | ||
| 309 | Resume, | ||
| 310 | |||
| 311 | /// The USB power has been detected. | ||
| 312 | PowerDetected, | ||
| 313 | |||
| 314 | /// The USB power has been removed. Not supported by all devices. | ||
| 315 | PowerRemoved, | ||
| 316 | } | ||
| 317 | |||
| 318 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 319 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 320 | pub struct EndpointAllocError; | ||
| 321 | |||
| 322 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 323 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 324 | /// Operation is unsupported by the driver. | ||
| 325 | pub struct Unsupported; | ||
| 326 | |||
| 327 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 328 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 329 | /// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] | ||
| 330 | pub enum EndpointError { | ||
| 331 | /// Either the packet to be written is too long to fit in the transmission | ||
| 332 | /// buffer or the received packet is too long to fit in `buf`. | ||
| 333 | BufferOverflow, | ||
| 334 | |||
| 335 | /// The endpoint is disabled. | ||
| 336 | Disabled, | ||
| 337 | } | ||
