diff options
| author | Dániel Buga <[email protected]> | 2024-04-26 17:35:28 +0200 |
|---|---|---|
| committer | Dániel Buga <[email protected]> | 2024-04-26 17:58:23 +0200 |
| commit | 91c42e0b9e118d88a51fecf07dc3f41b61c0762c (patch) | |
| tree | 8614303f101e8fc86263c9d7cf1a6812aa01d9f1 /embassy-usb-synopsys-otg/src/lib.rs | |
| parent | 4d4cbc0dd3e84dfd7d29d1ecdd2b388568be081f (diff) | |
Extract synopsys otg driver
Diffstat (limited to 'embassy-usb-synopsys-otg/src/lib.rs')
| -rw-r--r-- | embassy-usb-synopsys-otg/src/lib.rs | 1300 |
1 files changed, 1300 insertions, 0 deletions
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs new file mode 100644 index 000000000..6d6cc9b0f --- /dev/null +++ b/embassy-usb-synopsys-otg/src/lib.rs | |||
| @@ -0,0 +1,1300 @@ | |||
| 1 | #![cfg_attr(not(test), no_std)] | ||
| 2 | #![allow(async_fn_in_trait)] | ||
| 3 | #![doc = include_str!("../README.md")] | ||
| 4 | #![warn(missing_docs)] | ||
| 5 | |||
| 6 | // This must go FIRST so that all the other modules see its macros. | ||
| 7 | mod fmt; | ||
| 8 | |||
| 9 | use core::cell::UnsafeCell; | ||
| 10 | use core::marker::PhantomData; | ||
| 11 | use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; | ||
| 12 | use core::task::Poll; | ||
| 13 | |||
| 14 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 15 | use embassy_usb_driver::{ | ||
| 16 | Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, | ||
| 17 | EndpointType, Event, Unsupported, | ||
| 18 | }; | ||
| 19 | use futures::future::poll_fn; | ||
| 20 | |||
| 21 | pub mod otg_v1; | ||
| 22 | |||
| 23 | use otg_v1::{regs, vals, Otg}; | ||
| 24 | |||
| 25 | /// Handle interrupts. | ||
| 26 | pub unsafe fn on_interrupt(r: Otg, state: &State<{ MAX_EP_COUNT }>, ep_count: usize, quirk_setup_late_cnak: bool) { | ||
| 27 | let ints = r.gintsts().read(); | ||
| 28 | if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() { | ||
| 29 | // Mask interrupts and notify `Bus` to process them | ||
| 30 | r.gintmsk().write(|_| {}); | ||
| 31 | state.bus_waker.wake(); | ||
| 32 | } | ||
| 33 | |||
| 34 | // Handle RX | ||
| 35 | while r.gintsts().read().rxflvl() { | ||
| 36 | let status = r.grxstsp().read(); | ||
| 37 | trace!("=== status {:08x}", status.0); | ||
| 38 | let ep_num = status.epnum() as usize; | ||
| 39 | let len = status.bcnt() as usize; | ||
| 40 | |||
| 41 | assert!(ep_num < ep_count); | ||
| 42 | |||
| 43 | match status.pktstsd() { | ||
| 44 | vals::Pktstsd::SETUP_DATA_RX => { | ||
| 45 | trace!("SETUP_DATA_RX"); | ||
| 46 | assert!(len == 8, "invalid SETUP packet length={}", len); | ||
| 47 | assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num); | ||
| 48 | |||
| 49 | // flushing TX if something stuck in control endpoint | ||
| 50 | if r.dieptsiz(ep_num).read().pktcnt() != 0 { | ||
| 51 | r.grstctl().modify(|w| { | ||
| 52 | w.set_txfnum(ep_num as _); | ||
| 53 | w.set_txfflsh(true); | ||
| 54 | }); | ||
| 55 | while r.grstctl().read().txfflsh() {} | ||
| 56 | } | ||
| 57 | |||
| 58 | if state.ep0_setup_ready.load(Ordering::Relaxed) == false { | ||
| 59 | // SAFETY: exclusive access ensured by atomic bool | ||
| 60 | let data = unsafe { &mut *state.ep0_setup_data.get() }; | ||
| 61 | data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); | ||
| 62 | data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); | ||
| 63 | state.ep0_setup_ready.store(true, Ordering::Release); | ||
| 64 | state.ep_out_wakers[0].wake(); | ||
| 65 | } else { | ||
| 66 | error!("received SETUP before previous finished processing"); | ||
| 67 | // discard FIFO | ||
| 68 | r.fifo(0).read(); | ||
| 69 | r.fifo(0).read(); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | vals::Pktstsd::OUT_DATA_RX => { | ||
| 73 | trace!("OUT_DATA_RX ep={} len={}", ep_num, len); | ||
| 74 | |||
| 75 | if state.ep_out_size[ep_num].load(Ordering::Acquire) == EP_OUT_BUFFER_EMPTY { | ||
| 76 | // SAFETY: Buffer size is allocated to be equal to endpoint's maximum packet size | ||
| 77 | // We trust the peripheral to not exceed its configured MPSIZ | ||
| 78 | let buf = unsafe { core::slice::from_raw_parts_mut(*state.ep_out_buffers[ep_num].get(), len) }; | ||
| 79 | |||
| 80 | for chunk in buf.chunks_mut(4) { | ||
| 81 | // RX FIFO is shared so always read from fifo(0) | ||
| 82 | let data = r.fifo(0).read().0; | ||
| 83 | chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]); | ||
| 84 | } | ||
| 85 | |||
| 86 | state.ep_out_size[ep_num].store(len as u16, Ordering::Release); | ||
| 87 | state.ep_out_wakers[ep_num].wake(); | ||
| 88 | } else { | ||
| 89 | error!("ep_out buffer overflow index={}", ep_num); | ||
| 90 | |||
| 91 | // discard FIFO data | ||
| 92 | let len_words = (len + 3) / 4; | ||
| 93 | for _ in 0..len_words { | ||
| 94 | r.fifo(0).read().data(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | vals::Pktstsd::OUT_DATA_DONE => { | ||
| 99 | trace!("OUT_DATA_DONE ep={}", ep_num); | ||
| 100 | } | ||
| 101 | vals::Pktstsd::SETUP_DATA_DONE => { | ||
| 102 | trace!("SETUP_DATA_DONE ep={}", ep_num); | ||
| 103 | |||
| 104 | if quirk_setup_late_cnak { | ||
| 105 | // Clear NAK to indicate we are ready to receive more data | ||
| 106 | r.doepctl(ep_num).modify(|w| w.set_cnak(true)); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | x => trace!("unknown PKTSTS: {}", x.to_bits()), | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | // IN endpoint interrupt | ||
| 114 | if ints.iepint() { | ||
| 115 | let mut ep_mask = r.daint().read().iepint(); | ||
| 116 | let mut ep_num = 0; | ||
| 117 | |||
| 118 | // Iterate over endpoints while there are non-zero bits in the mask | ||
| 119 | while ep_mask != 0 { | ||
| 120 | if ep_mask & 1 != 0 { | ||
| 121 | let ep_ints = r.diepint(ep_num).read(); | ||
| 122 | |||
| 123 | // clear all | ||
| 124 | r.diepint(ep_num).write_value(ep_ints); | ||
| 125 | |||
| 126 | // TXFE is cleared in DIEPEMPMSK | ||
| 127 | if ep_ints.txfe() { | ||
| 128 | critical_section::with(|_| { | ||
| 129 | r.diepempmsk().modify(|w| { | ||
| 130 | w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num)); | ||
| 131 | }); | ||
| 132 | }); | ||
| 133 | } | ||
| 134 | |||
| 135 | state.ep_in_wakers[ep_num].wake(); | ||
| 136 | trace!("in ep={} irq val={:08x}", ep_num, ep_ints.0); | ||
| 137 | } | ||
| 138 | |||
| 139 | ep_mask >>= 1; | ||
| 140 | ep_num += 1; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | // not needed? reception handled in rxflvl | ||
| 145 | // OUT endpoint interrupt | ||
| 146 | // if ints.oepint() { | ||
| 147 | // let mut ep_mask = r.daint().read().oepint(); | ||
| 148 | // let mut ep_num = 0; | ||
| 149 | |||
| 150 | // while ep_mask != 0 { | ||
| 151 | // if ep_mask & 1 != 0 { | ||
| 152 | // let ep_ints = r.doepint(ep_num).read(); | ||
| 153 | // // clear all | ||
| 154 | // r.doepint(ep_num).write_value(ep_ints); | ||
| 155 | // state.ep_out_wakers[ep_num].wake(); | ||
| 156 | // trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0); | ||
| 157 | // } | ||
| 158 | |||
| 159 | // ep_mask >>= 1; | ||
| 160 | // ep_num += 1; | ||
| 161 | // } | ||
| 162 | // } | ||
| 163 | } | ||
| 164 | |||
| 165 | /// USB PHY type | ||
| 166 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | ||
| 167 | pub enum PhyType { | ||
| 168 | /// Internal Full-Speed PHY | ||
| 169 | /// | ||
| 170 | /// Available on most High-Speed peripherals. | ||
| 171 | InternalFullSpeed, | ||
| 172 | /// Internal High-Speed PHY | ||
| 173 | /// | ||
| 174 | /// Available on a few STM32 chips. | ||
| 175 | InternalHighSpeed, | ||
| 176 | /// External ULPI High-Speed PHY | ||
| 177 | ExternalHighSpeed, | ||
| 178 | } | ||
| 179 | |||
| 180 | impl PhyType { | ||
| 181 | /// Get whether this PHY is any of the internal types. | ||
| 182 | pub fn internal(&self) -> bool { | ||
| 183 | match self { | ||
| 184 | PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true, | ||
| 185 | PhyType::ExternalHighSpeed => false, | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | /// Get whether this PHY is any of the high-speed types. | ||
| 190 | pub fn high_speed(&self) -> bool { | ||
| 191 | match self { | ||
| 192 | PhyType::InternalFullSpeed => false, | ||
| 193 | PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true, | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | fn to_dspd(&self) -> vals::Dspd { | ||
| 198 | match self { | ||
| 199 | PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL, | ||
| 200 | PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED, | ||
| 201 | PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED, | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Indicates that [State::ep_out_buffers] is empty. | ||
| 207 | const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX; | ||
| 208 | |||
| 209 | /// USB OTG driver state. | ||
| 210 | pub struct State<const EP_COUNT: usize> { | ||
| 211 | /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true. | ||
| 212 | ep0_setup_data: UnsafeCell<[u8; 8]>, | ||
| 213 | ep0_setup_ready: AtomicBool, | ||
| 214 | ep_in_wakers: [AtomicWaker; EP_COUNT], | ||
| 215 | ep_out_wakers: [AtomicWaker; EP_COUNT], | ||
| 216 | /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint. | ||
| 217 | /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY]. | ||
| 218 | ep_out_buffers: [UnsafeCell<*mut u8>; EP_COUNT], | ||
| 219 | ep_out_size: [AtomicU16; EP_COUNT], | ||
| 220 | bus_waker: AtomicWaker, | ||
| 221 | } | ||
| 222 | |||
| 223 | unsafe impl<const EP_COUNT: usize> Send for State<EP_COUNT> {} | ||
| 224 | unsafe impl<const EP_COUNT: usize> Sync for State<EP_COUNT> {} | ||
| 225 | |||
| 226 | impl<const EP_COUNT: usize> State<EP_COUNT> { | ||
| 227 | /// Create a new State. | ||
| 228 | pub const fn new() -> Self { | ||
| 229 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | ||
| 230 | const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); | ||
| 231 | const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY); | ||
| 232 | |||
| 233 | Self { | ||
| 234 | ep0_setup_data: UnsafeCell::new([0u8; 8]), | ||
| 235 | ep0_setup_ready: AtomicBool::new(false), | ||
| 236 | ep_in_wakers: [NEW_AW; EP_COUNT], | ||
| 237 | ep_out_wakers: [NEW_AW; EP_COUNT], | ||
| 238 | ep_out_buffers: [NEW_BUF; EP_COUNT], | ||
| 239 | ep_out_size: [NEW_SIZE; EP_COUNT], | ||
| 240 | bus_waker: NEW_AW, | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | #[derive(Debug, Clone, Copy)] | ||
| 246 | struct EndpointData { | ||
| 247 | ep_type: EndpointType, | ||
| 248 | max_packet_size: u16, | ||
| 249 | fifo_size_words: u16, | ||
| 250 | } | ||
| 251 | |||
| 252 | /// USB driver config. | ||
| 253 | #[non_exhaustive] | ||
| 254 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 255 | pub struct Config { | ||
| 256 | /// Enable VBUS detection. | ||
| 257 | /// | ||
| 258 | /// The USB spec requires USB devices monitor for USB cable plug/unplug and react accordingly. | ||
| 259 | /// This is done by checking whether there is 5V on the VBUS pin or not. | ||
| 260 | /// | ||
| 261 | /// If your device is bus-powered (powers itself from the USB host via VBUS), then this is optional. | ||
| 262 | /// (If there's no power in VBUS your device would be off anyway, so it's fine to always assume | ||
| 263 | /// there's power in VBUS, i.e. the USB cable is always plugged in.) | ||
| 264 | /// | ||
| 265 | /// If your device is self-powered (i.e. it gets power from a source other than the USB cable, and | ||
| 266 | /// therefore can stay powered through USB cable plug/unplug) then you MUST set this to true. | ||
| 267 | /// | ||
| 268 | /// If you set this to true, you must connect VBUS to PA9 for FS, PB13 for HS, possibly with a | ||
| 269 | /// voltage divider. See ST application note AN4879 and the reference manual for more details. | ||
| 270 | pub vbus_detection: bool, | ||
| 271 | } | ||
| 272 | |||
| 273 | impl Default for Config { | ||
| 274 | fn default() -> Self { | ||
| 275 | Self { vbus_detection: true } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | /// USB driver. | ||
| 280 | pub struct Driver<'d> { | ||
| 281 | config: Config, | ||
| 282 | ep_in: [Option<EndpointData>; MAX_EP_COUNT], | ||
| 283 | ep_out: [Option<EndpointData>; MAX_EP_COUNT], | ||
| 284 | ep_out_buffer: &'d mut [u8], | ||
| 285 | ep_out_buffer_offset: usize, | ||
| 286 | instance: OtgInstance<'d>, | ||
| 287 | } | ||
| 288 | |||
| 289 | impl<'d> Driver<'d> { | ||
| 290 | /// Initializes USB OTG peripheral. | ||
| 291 | /// | ||
| 292 | /// # Arguments | ||
| 293 | /// | ||
| 294 | /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets. | ||
| 295 | /// Must be large enough to fit all OUT endpoint max packet sizes. | ||
| 296 | /// Endpoint allocation will fail if it is too small. | ||
| 297 | pub fn new(ep_out_buffer: &'d mut [u8], instance: OtgInstance<'d>, config: Config) -> Self { | ||
| 298 | Self { | ||
| 299 | config, | ||
| 300 | ep_in: [None; MAX_EP_COUNT], | ||
| 301 | ep_out: [None; MAX_EP_COUNT], | ||
| 302 | ep_out_buffer, | ||
| 303 | ep_out_buffer_offset: 0, | ||
| 304 | instance, | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | // Returns total amount of words (u32) allocated in dedicated FIFO | ||
| 309 | fn allocated_fifo_words(&self) -> u16 { | ||
| 310 | self.instance.extra_rx_fifo_words + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in) | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Creates an [`Endpoint`] with the given parameters. | ||
| 314 | pub fn alloc_endpoint<D: Dir>( | ||
| 315 | &mut self, | ||
| 316 | ep_type: EndpointType, | ||
| 317 | max_packet_size: u16, | ||
| 318 | interval_ms: u8, | ||
| 319 | ) -> Result<Endpoint<'d, D>, EndpointAllocError> { | ||
| 320 | trace!( | ||
| 321 | "allocating type={:?} mps={:?} interval_ms={}, dir={:?}", | ||
| 322 | ep_type, | ||
| 323 | max_packet_size, | ||
| 324 | interval_ms, | ||
| 325 | D::dir() | ||
| 326 | ); | ||
| 327 | |||
| 328 | if D::dir() == Direction::Out { | ||
| 329 | if self.ep_out_buffer_offset + max_packet_size as usize >= self.ep_out_buffer.len() { | ||
| 330 | error!("Not enough endpoint out buffer capacity"); | ||
| 331 | return Err(EndpointAllocError); | ||
| 332 | } | ||
| 333 | }; | ||
| 334 | |||
| 335 | let fifo_size_words = match D::dir() { | ||
| 336 | Direction::Out => (max_packet_size + 3) / 4, | ||
| 337 | // INEPTXFD requires minimum size of 16 words | ||
| 338 | Direction::In => u16::max((max_packet_size + 3) / 4, 16), | ||
| 339 | }; | ||
| 340 | |||
| 341 | if fifo_size_words + self.allocated_fifo_words() > self.instance.fifo_depth_words { | ||
| 342 | error!("Not enough FIFO capacity"); | ||
| 343 | return Err(EndpointAllocError); | ||
| 344 | } | ||
| 345 | |||
| 346 | let eps = match D::dir() { | ||
| 347 | Direction::Out => &mut self.ep_out, | ||
| 348 | Direction::In => &mut self.ep_in, | ||
| 349 | }; | ||
| 350 | |||
| 351 | // Find free endpoint slot | ||
| 352 | let slot = eps.iter_mut().enumerate().find(|(i, ep)| { | ||
| 353 | if *i == 0 && ep_type != EndpointType::Control { | ||
| 354 | // reserved for control pipe | ||
| 355 | false | ||
| 356 | } else { | ||
| 357 | ep.is_none() | ||
| 358 | } | ||
| 359 | }); | ||
| 360 | |||
| 361 | let index = match slot { | ||
| 362 | Some((index, ep)) => { | ||
| 363 | *ep = Some(EndpointData { | ||
| 364 | ep_type, | ||
| 365 | max_packet_size, | ||
| 366 | fifo_size_words, | ||
| 367 | }); | ||
| 368 | index | ||
| 369 | } | ||
| 370 | None => { | ||
| 371 | error!("No free endpoints available"); | ||
| 372 | return Err(EndpointAllocError); | ||
| 373 | } | ||
| 374 | }; | ||
| 375 | |||
| 376 | trace!(" index={}", index); | ||
| 377 | |||
| 378 | if D::dir() == Direction::Out { | ||
| 379 | // Buffer capacity check was done above, now allocation cannot fail | ||
| 380 | unsafe { | ||
| 381 | *self.instance.state.ep_out_buffers[index].get() = | ||
| 382 | self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _); | ||
| 383 | } | ||
| 384 | self.ep_out_buffer_offset += max_packet_size as usize; | ||
| 385 | } | ||
| 386 | |||
| 387 | Ok(Endpoint { | ||
| 388 | _phantom: PhantomData, | ||
| 389 | regs: self.instance.regs, | ||
| 390 | state: self.instance.state, | ||
| 391 | info: EndpointInfo { | ||
| 392 | addr: EndpointAddress::from_parts(index, D::dir()), | ||
| 393 | ep_type, | ||
| 394 | max_packet_size, | ||
| 395 | interval_ms, | ||
| 396 | }, | ||
| 397 | }) | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | impl<'d> embassy_usb_driver::Driver<'d> for Driver<'d> { | ||
| 402 | type EndpointOut = Endpoint<'d, Out>; | ||
| 403 | type EndpointIn = Endpoint<'d, In>; | ||
| 404 | type ControlPipe = ControlPipe<'d>; | ||
| 405 | type Bus = Bus<'d>; | ||
| 406 | |||
| 407 | fn alloc_endpoint_in( | ||
| 408 | &mut self, | ||
| 409 | ep_type: EndpointType, | ||
| 410 | max_packet_size: u16, | ||
| 411 | interval_ms: u8, | ||
| 412 | ) -> Result<Self::EndpointIn, EndpointAllocError> { | ||
| 413 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||
| 414 | } | ||
| 415 | |||
| 416 | fn alloc_endpoint_out( | ||
| 417 | &mut self, | ||
| 418 | ep_type: EndpointType, | ||
| 419 | max_packet_size: u16, | ||
| 420 | interval_ms: u8, | ||
| 421 | ) -> Result<Self::EndpointOut, EndpointAllocError> { | ||
| 422 | self.alloc_endpoint(ep_type, max_packet_size, interval_ms) | ||
| 423 | } | ||
| 424 | |||
| 425 | fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | ||
| 426 | let ep_out = self | ||
| 427 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | ||
| 428 | .unwrap(); | ||
| 429 | let ep_in = self | ||
| 430 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | ||
| 431 | .unwrap(); | ||
| 432 | assert_eq!(ep_out.info.addr.index(), 0); | ||
| 433 | assert_eq!(ep_in.info.addr.index(), 0); | ||
| 434 | |||
| 435 | trace!("start"); | ||
| 436 | |||
| 437 | let regs = self.instance.regs; | ||
| 438 | let quirk_setup_late_cnak = self.instance.quirk_setup_late_cnak; | ||
| 439 | ( | ||
| 440 | Bus { | ||
| 441 | config: self.config, | ||
| 442 | ep_in: self.ep_in, | ||
| 443 | ep_out: self.ep_out, | ||
| 444 | inited: false, | ||
| 445 | instance: self.instance, | ||
| 446 | }, | ||
| 447 | ControlPipe { | ||
| 448 | max_packet_size: control_max_packet_size, | ||
| 449 | ep_out, | ||
| 450 | ep_in, | ||
| 451 | regs, | ||
| 452 | quirk_setup_late_cnak, | ||
| 453 | }, | ||
| 454 | ) | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | /// USB bus. | ||
| 459 | pub struct Bus<'d> { | ||
| 460 | config: Config, | ||
| 461 | ep_in: [Option<EndpointData>; MAX_EP_COUNT], | ||
| 462 | ep_out: [Option<EndpointData>; MAX_EP_COUNT], | ||
| 463 | instance: OtgInstance<'d>, | ||
| 464 | inited: bool, | ||
| 465 | } | ||
| 466 | |||
| 467 | impl<'d> Bus<'d> { | ||
| 468 | fn restore_irqs(&mut self) { | ||
| 469 | self.instance.regs.gintmsk().write(|w| { | ||
| 470 | w.set_usbrst(true); | ||
| 471 | w.set_enumdnem(true); | ||
| 472 | w.set_usbsuspm(true); | ||
| 473 | w.set_wuim(true); | ||
| 474 | w.set_iepint(true); | ||
| 475 | w.set_oepint(true); | ||
| 476 | w.set_rxflvlm(true); | ||
| 477 | w.set_srqim(true); | ||
| 478 | w.set_otgint(true); | ||
| 479 | }); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | impl<'d> Bus<'d> { | ||
| 484 | /// Returns the PHY type. | ||
| 485 | pub fn phy_type(&self) -> PhyType { | ||
| 486 | self.instance.phy_type | ||
| 487 | } | ||
| 488 | |||
| 489 | /// Configures the PHY as a device. | ||
| 490 | pub fn configure_as_device(&mut self) { | ||
| 491 | let r = self.instance.regs; | ||
| 492 | let phy_type = self.instance.phy_type; | ||
| 493 | r.gusbcfg().write(|w| { | ||
| 494 | // Force device mode | ||
| 495 | w.set_fdmod(true); | ||
| 496 | // Enable internal full-speed PHY | ||
| 497 | w.set_physel(phy_type.internal() && !phy_type.high_speed()); | ||
| 498 | }); | ||
| 499 | } | ||
| 500 | |||
| 501 | /// Applies configuration specific to | ||
| 502 | /// Core ID 0x0000_1100 and 0x0000_1200 | ||
| 503 | pub fn config_v1(&mut self) { | ||
| 504 | let r = self.instance.regs; | ||
| 505 | let phy_type = self.instance.phy_type; | ||
| 506 | assert!(phy_type != PhyType::InternalHighSpeed); | ||
| 507 | |||
| 508 | r.gccfg_v1().modify(|w| { | ||
| 509 | // Enable internal full-speed PHY, logic is inverted | ||
| 510 | w.set_pwrdwn(phy_type.internal()); | ||
| 511 | }); | ||
| 512 | |||
| 513 | // F429-like chips have the GCCFG.NOVBUSSENS bit | ||
| 514 | r.gccfg_v1().modify(|w| { | ||
| 515 | w.set_novbussens(!self.config.vbus_detection); | ||
| 516 | w.set_vbusasen(false); | ||
| 517 | w.set_vbusbsen(self.config.vbus_detection); | ||
| 518 | w.set_sofouten(false); | ||
| 519 | }); | ||
| 520 | } | ||
| 521 | |||
| 522 | /// Applies configuration specific to | ||
| 523 | /// Core ID 0x0000_2000, 0x0000_2100, 0x0000_2300, 0x0000_3000 and 0x0000_3100 | ||
| 524 | pub fn config_v2v3(&mut self) { | ||
| 525 | let r = self.instance.regs; | ||
| 526 | let phy_type = self.instance.phy_type; | ||
| 527 | |||
| 528 | // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning | ||
| 529 | r.gccfg_v2().modify(|w| { | ||
| 530 | // Enable internal full-speed PHY, logic is inverted | ||
| 531 | w.set_pwrdwn(phy_type.internal() && !phy_type.high_speed()); | ||
| 532 | w.set_phyhsen(phy_type.internal() && phy_type.high_speed()); | ||
| 533 | }); | ||
| 534 | |||
| 535 | r.gccfg_v2().modify(|w| { | ||
| 536 | w.set_vbden(self.config.vbus_detection); | ||
| 537 | }); | ||
| 538 | |||
| 539 | // Force B-peripheral session | ||
| 540 | r.gotgctl().modify(|w| { | ||
| 541 | w.set_bvaloen(!self.config.vbus_detection); | ||
| 542 | w.set_bvaloval(true); | ||
| 543 | }); | ||
| 544 | } | ||
| 545 | |||
| 546 | fn init(&mut self) { | ||
| 547 | let r = self.instance.regs; | ||
| 548 | let phy_type = self.instance.phy_type; | ||
| 549 | |||
| 550 | // Soft disconnect. | ||
| 551 | r.dctl().write(|w| w.set_sdis(true)); | ||
| 552 | |||
| 553 | // Set speed. | ||
| 554 | r.dcfg().write(|w| { | ||
| 555 | w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80); | ||
| 556 | w.set_dspd(phy_type.to_dspd()); | ||
| 557 | }); | ||
| 558 | |||
| 559 | // Unmask transfer complete EP interrupt | ||
| 560 | r.diepmsk().write(|w| { | ||
| 561 | w.set_xfrcm(true); | ||
| 562 | }); | ||
| 563 | |||
| 564 | // Unmask and clear core interrupts | ||
| 565 | self.restore_irqs(); | ||
| 566 | r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); | ||
| 567 | |||
| 568 | // Unmask global interrupt | ||
| 569 | r.gahbcfg().write(|w| { | ||
| 570 | w.set_gint(true); // unmask global interrupt | ||
| 571 | }); | ||
| 572 | |||
| 573 | // Connect | ||
| 574 | r.dctl().write(|w| w.set_sdis(false)); | ||
| 575 | } | ||
| 576 | |||
| 577 | fn init_fifo(&mut self) { | ||
| 578 | trace!("init_fifo"); | ||
| 579 | |||
| 580 | let regs = self.instance.regs; | ||
| 581 | // ERRATA NOTE: Don't interrupt FIFOs being written to. The interrupt | ||
| 582 | // handler COULD interrupt us here and do FIFO operations, so ensure | ||
| 583 | // the interrupt does not occur. | ||
| 584 | critical_section::with(|_| { | ||
| 585 | // Configure RX fifo size. All endpoints share the same FIFO area. | ||
| 586 | let rx_fifo_size_words = self.instance.extra_rx_fifo_words + ep_fifo_size(&self.ep_out); | ||
| 587 | trace!("configuring rx fifo size={}", rx_fifo_size_words); | ||
| 588 | |||
| 589 | regs.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); | ||
| 590 | |||
| 591 | // Configure TX (USB in direction) fifo size for each endpoint | ||
| 592 | let mut fifo_top = rx_fifo_size_words; | ||
| 593 | for i in 0..self.instance.endpoint_count { | ||
| 594 | if let Some(ep) = self.ep_in[i] { | ||
| 595 | trace!( | ||
| 596 | "configuring tx fifo ep={}, offset={}, size={}", | ||
| 597 | i, | ||
| 598 | fifo_top, | ||
| 599 | ep.fifo_size_words | ||
| 600 | ); | ||
| 601 | |||
| 602 | let dieptxf = if i == 0 { regs.dieptxf0() } else { regs.dieptxf(i - 1) }; | ||
| 603 | |||
| 604 | dieptxf.write(|w| { | ||
| 605 | w.set_fd(ep.fifo_size_words); | ||
| 606 | w.set_sa(fifo_top); | ||
| 607 | }); | ||
| 608 | |||
| 609 | fifo_top += ep.fifo_size_words; | ||
| 610 | } | ||
| 611 | } | ||
| 612 | |||
| 613 | assert!( | ||
| 614 | fifo_top <= self.instance.fifo_depth_words, | ||
| 615 | "FIFO allocations exceeded maximum capacity" | ||
| 616 | ); | ||
| 617 | |||
| 618 | // Flush fifos | ||
| 619 | regs.grstctl().write(|w| { | ||
| 620 | w.set_rxfflsh(true); | ||
| 621 | w.set_txfflsh(true); | ||
| 622 | w.set_txfnum(0x10); | ||
| 623 | }); | ||
| 624 | }); | ||
| 625 | |||
| 626 | loop { | ||
| 627 | let x = regs.grstctl().read(); | ||
| 628 | if !x.rxfflsh() && !x.txfflsh() { | ||
| 629 | break; | ||
| 630 | } | ||
| 631 | } | ||
| 632 | } | ||
| 633 | |||
| 634 | fn configure_endpoints(&mut self) { | ||
| 635 | trace!("configure_endpoints"); | ||
| 636 | |||
| 637 | let regs = self.instance.regs; | ||
| 638 | |||
| 639 | // Configure IN endpoints | ||
| 640 | for (index, ep) in self.ep_in.iter().enumerate() { | ||
| 641 | if let Some(ep) = ep { | ||
| 642 | critical_section::with(|_| { | ||
| 643 | regs.diepctl(index).write(|w| { | ||
| 644 | if index == 0 { | ||
| 645 | w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); | ||
| 646 | } else { | ||
| 647 | w.set_mpsiz(ep.max_packet_size); | ||
| 648 | w.set_eptyp(to_eptyp(ep.ep_type)); | ||
| 649 | w.set_sd0pid_sevnfrm(true); | ||
| 650 | w.set_txfnum(index as _); | ||
| 651 | w.set_snak(true); | ||
| 652 | } | ||
| 653 | }); | ||
| 654 | }); | ||
| 655 | } | ||
| 656 | } | ||
| 657 | |||
| 658 | // Configure OUT endpoints | ||
| 659 | for (index, ep) in self.ep_out.iter().enumerate() { | ||
| 660 | if let Some(ep) = ep { | ||
| 661 | critical_section::with(|_| { | ||
| 662 | regs.doepctl(index).write(|w| { | ||
| 663 | if index == 0 { | ||
| 664 | w.set_mpsiz(ep0_mpsiz(ep.max_packet_size)); | ||
| 665 | } else { | ||
| 666 | w.set_mpsiz(ep.max_packet_size); | ||
| 667 | w.set_eptyp(to_eptyp(ep.ep_type)); | ||
| 668 | w.set_sd0pid_sevnfrm(true); | ||
| 669 | } | ||
| 670 | }); | ||
| 671 | |||
| 672 | regs.doeptsiz(index).modify(|w| { | ||
| 673 | w.set_xfrsiz(ep.max_packet_size as _); | ||
| 674 | if index == 0 { | ||
| 675 | w.set_rxdpid_stupcnt(1); | ||
| 676 | } else { | ||
| 677 | w.set_pktcnt(1); | ||
| 678 | } | ||
| 679 | }); | ||
| 680 | }); | ||
| 681 | } | ||
| 682 | } | ||
| 683 | |||
| 684 | // Enable IRQs for allocated endpoints | ||
| 685 | regs.daintmsk().modify(|w| { | ||
| 686 | w.set_iepm(ep_irq_mask(&self.ep_in)); | ||
| 687 | // OUT interrupts not used, handled in RXFLVL | ||
| 688 | // w.set_oepm(ep_irq_mask(&self.ep_out)); | ||
| 689 | }); | ||
| 690 | } | ||
| 691 | |||
| 692 | fn disable_all_endpoints(&mut self) { | ||
| 693 | for i in 0..self.instance.endpoint_count { | ||
| 694 | self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::In), false); | ||
| 695 | self.endpoint_set_enabled(EndpointAddress::from_parts(i, Direction::Out), false); | ||
| 696 | } | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | impl<'d> embassy_usb_driver::Bus for Bus<'d> { | ||
| 701 | async fn poll(&mut self) -> Event { | ||
| 702 | poll_fn(move |cx| { | ||
| 703 | if !self.inited { | ||
| 704 | self.init(); | ||
| 705 | self.inited = true; | ||
| 706 | |||
| 707 | // If no vbus detection, just return a single PowerDetected event at startup. | ||
| 708 | if !self.config.vbus_detection { | ||
| 709 | return Poll::Ready(Event::PowerDetected); | ||
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | let regs = self.instance.regs; | ||
| 714 | self.instance.state.bus_waker.register(cx.waker()); | ||
| 715 | |||
| 716 | let ints = regs.gintsts().read(); | ||
| 717 | |||
| 718 | if ints.srqint() { | ||
| 719 | trace!("vbus detected"); | ||
| 720 | |||
| 721 | regs.gintsts().write(|w| w.set_srqint(true)); // clear | ||
| 722 | self.restore_irqs(); | ||
| 723 | |||
| 724 | if self.config.vbus_detection { | ||
| 725 | return Poll::Ready(Event::PowerDetected); | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | if ints.otgint() { | ||
| 730 | let otgints = regs.gotgint().read(); | ||
| 731 | regs.gotgint().write_value(otgints); // clear all | ||
| 732 | self.restore_irqs(); | ||
| 733 | |||
| 734 | if otgints.sedet() { | ||
| 735 | trace!("vbus removed"); | ||
| 736 | if self.config.vbus_detection { | ||
| 737 | self.disable_all_endpoints(); | ||
| 738 | return Poll::Ready(Event::PowerRemoved); | ||
| 739 | } | ||
| 740 | } | ||
| 741 | } | ||
| 742 | |||
| 743 | if ints.usbrst() { | ||
| 744 | trace!("reset"); | ||
| 745 | |||
| 746 | self.init_fifo(); | ||
| 747 | self.configure_endpoints(); | ||
| 748 | |||
| 749 | // Reset address | ||
| 750 | critical_section::with(|_| { | ||
| 751 | regs.dcfg().modify(|w| { | ||
| 752 | w.set_dad(0); | ||
| 753 | }); | ||
| 754 | }); | ||
| 755 | |||
| 756 | regs.gintsts().write(|w| w.set_usbrst(true)); // clear | ||
| 757 | self.restore_irqs(); | ||
| 758 | } | ||
| 759 | |||
| 760 | if ints.enumdne() { | ||
| 761 | trace!("enumdne"); | ||
| 762 | |||
| 763 | let speed = regs.dsts().read().enumspd(); | ||
| 764 | let trdt = (self.instance.calculate_trdt_fn)(speed); | ||
| 765 | trace!(" speed={} trdt={}", speed.to_bits(), trdt); | ||
| 766 | regs.gusbcfg().modify(|w| w.set_trdt(trdt)); | ||
| 767 | |||
| 768 | regs.gintsts().write(|w| w.set_enumdne(true)); // clear | ||
| 769 | self.restore_irqs(); | ||
| 770 | |||
| 771 | return Poll::Ready(Event::Reset); | ||
| 772 | } | ||
| 773 | |||
| 774 | if ints.usbsusp() { | ||
| 775 | trace!("suspend"); | ||
| 776 | regs.gintsts().write(|w| w.set_usbsusp(true)); // clear | ||
| 777 | self.restore_irqs(); | ||
| 778 | return Poll::Ready(Event::Suspend); | ||
| 779 | } | ||
| 780 | |||
| 781 | if ints.wkupint() { | ||
| 782 | trace!("resume"); | ||
| 783 | regs.gintsts().write(|w| w.set_wkupint(true)); // clear | ||
| 784 | self.restore_irqs(); | ||
| 785 | return Poll::Ready(Event::Resume); | ||
| 786 | } | ||
| 787 | |||
| 788 | Poll::Pending | ||
| 789 | }) | ||
| 790 | .await | ||
| 791 | } | ||
| 792 | |||
| 793 | fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { | ||
| 794 | trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled); | ||
| 795 | |||
| 796 | assert!( | ||
| 797 | ep_addr.index() < self.instance.endpoint_count, | ||
| 798 | "endpoint_set_stalled index {} out of range", | ||
| 799 | ep_addr.index() | ||
| 800 | ); | ||
| 801 | |||
| 802 | let regs = self.instance.regs; | ||
| 803 | let state = self.instance.state; | ||
| 804 | match ep_addr.direction() { | ||
| 805 | Direction::Out => { | ||
| 806 | critical_section::with(|_| { | ||
| 807 | regs.doepctl(ep_addr.index()).modify(|w| { | ||
| 808 | w.set_stall(stalled); | ||
| 809 | }); | ||
| 810 | }); | ||
| 811 | |||
| 812 | state.ep_out_wakers[ep_addr.index()].wake(); | ||
| 813 | } | ||
| 814 | Direction::In => { | ||
| 815 | critical_section::with(|_| { | ||
| 816 | regs.diepctl(ep_addr.index()).modify(|w| { | ||
| 817 | w.set_stall(stalled); | ||
| 818 | }); | ||
| 819 | }); | ||
| 820 | |||
| 821 | state.ep_in_wakers[ep_addr.index()].wake(); | ||
| 822 | } | ||
| 823 | } | ||
| 824 | } | ||
| 825 | |||
| 826 | fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { | ||
| 827 | assert!( | ||
| 828 | ep_addr.index() < self.instance.endpoint_count, | ||
| 829 | "endpoint_is_stalled index {} out of range", | ||
| 830 | ep_addr.index() | ||
| 831 | ); | ||
| 832 | |||
| 833 | let regs = self.instance.regs; | ||
| 834 | match ep_addr.direction() { | ||
| 835 | Direction::Out => regs.doepctl(ep_addr.index()).read().stall(), | ||
| 836 | Direction::In => regs.diepctl(ep_addr.index()).read().stall(), | ||
| 837 | } | ||
| 838 | } | ||
| 839 | |||
| 840 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { | ||
| 841 | trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled); | ||
| 842 | |||
| 843 | assert!( | ||
| 844 | ep_addr.index() < self.instance.endpoint_count, | ||
| 845 | "endpoint_set_enabled index {} out of range", | ||
| 846 | ep_addr.index() | ||
| 847 | ); | ||
| 848 | |||
| 849 | let regs = self.instance.regs; | ||
| 850 | let state = self.instance.state; | ||
| 851 | match ep_addr.direction() { | ||
| 852 | Direction::Out => { | ||
| 853 | critical_section::with(|_| { | ||
| 854 | // cancel transfer if active | ||
| 855 | if !enabled && regs.doepctl(ep_addr.index()).read().epena() { | ||
| 856 | regs.doepctl(ep_addr.index()).modify(|w| { | ||
| 857 | w.set_snak(true); | ||
| 858 | w.set_epdis(true); | ||
| 859 | }) | ||
| 860 | } | ||
| 861 | |||
| 862 | regs.doepctl(ep_addr.index()).modify(|w| { | ||
| 863 | w.set_usbaep(enabled); | ||
| 864 | }); | ||
| 865 | |||
| 866 | // Flush tx fifo | ||
| 867 | regs.grstctl().write(|w| { | ||
| 868 | w.set_txfflsh(true); | ||
| 869 | w.set_txfnum(ep_addr.index() as _); | ||
| 870 | }); | ||
| 871 | loop { | ||
| 872 | let x = regs.grstctl().read(); | ||
| 873 | if !x.txfflsh() { | ||
| 874 | break; | ||
| 875 | } | ||
| 876 | } | ||
| 877 | }); | ||
| 878 | |||
| 879 | // Wake `Endpoint::wait_enabled()` | ||
| 880 | state.ep_out_wakers[ep_addr.index()].wake(); | ||
| 881 | } | ||
| 882 | Direction::In => { | ||
| 883 | critical_section::with(|_| { | ||
| 884 | // cancel transfer if active | ||
| 885 | if !enabled && regs.diepctl(ep_addr.index()).read().epena() { | ||
| 886 | regs.diepctl(ep_addr.index()).modify(|w| { | ||
| 887 | w.set_snak(true); // set NAK | ||
| 888 | w.set_epdis(true); | ||
| 889 | }) | ||
| 890 | } | ||
| 891 | |||
| 892 | regs.diepctl(ep_addr.index()).modify(|w| { | ||
| 893 | w.set_usbaep(enabled); | ||
| 894 | w.set_cnak(enabled); // clear NAK that might've been set by SNAK above. | ||
| 895 | }) | ||
| 896 | }); | ||
| 897 | |||
| 898 | // Wake `Endpoint::wait_enabled()` | ||
| 899 | state.ep_in_wakers[ep_addr.index()].wake(); | ||
| 900 | } | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | async fn enable(&mut self) { | ||
| 905 | trace!("enable"); | ||
| 906 | // TODO: enable the peripheral once enable/disable semantics are cleared up in embassy-usb | ||
| 907 | } | ||
| 908 | |||
| 909 | async fn disable(&mut self) { | ||
| 910 | trace!("disable"); | ||
| 911 | |||
| 912 | // TODO: disable the peripheral once enable/disable semantics are cleared up in embassy-usb | ||
| 913 | //Bus::disable(self); | ||
| 914 | } | ||
| 915 | |||
| 916 | async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { | ||
| 917 | Err(Unsupported) | ||
| 918 | } | ||
| 919 | } | ||
| 920 | |||
| 921 | /// USB endpoint direction. | ||
| 922 | pub trait Dir { | ||
| 923 | /// Returns the direction value. | ||
| 924 | fn dir() -> Direction; | ||
| 925 | } | ||
| 926 | |||
| 927 | /// Marker type for the "IN" direction. | ||
| 928 | pub enum In {} | ||
| 929 | impl Dir for In { | ||
| 930 | fn dir() -> Direction { | ||
| 931 | Direction::In | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | /// Marker type for the "OUT" direction. | ||
| 936 | pub enum Out {} | ||
| 937 | impl Dir for Out { | ||
| 938 | fn dir() -> Direction { | ||
| 939 | Direction::Out | ||
| 940 | } | ||
| 941 | } | ||
| 942 | |||
| 943 | /// USB endpoint. | ||
| 944 | pub struct Endpoint<'d, D> { | ||
| 945 | _phantom: PhantomData<D>, | ||
| 946 | regs: Otg, | ||
| 947 | info: EndpointInfo, | ||
| 948 | state: &'d State<{ MAX_EP_COUNT }>, | ||
| 949 | } | ||
| 950 | |||
| 951 | impl<'d> embassy_usb_driver::Endpoint for Endpoint<'d, In> { | ||
| 952 | fn info(&self) -> &EndpointInfo { | ||
| 953 | &self.info | ||
| 954 | } | ||
| 955 | |||
| 956 | async fn wait_enabled(&mut self) { | ||
| 957 | poll_fn(|cx| { | ||
| 958 | let ep_index = self.info.addr.index(); | ||
| 959 | |||
| 960 | self.state.ep_in_wakers[ep_index].register(cx.waker()); | ||
| 961 | |||
| 962 | if self.regs.diepctl(ep_index).read().usbaep() { | ||
| 963 | Poll::Ready(()) | ||
| 964 | } else { | ||
| 965 | Poll::Pending | ||
| 966 | } | ||
| 967 | }) | ||
| 968 | .await | ||
| 969 | } | ||
| 970 | } | ||
| 971 | |||
| 972 | impl<'d> embassy_usb_driver::Endpoint for Endpoint<'d, Out> { | ||
| 973 | fn info(&self) -> &EndpointInfo { | ||
| 974 | &self.info | ||
| 975 | } | ||
| 976 | |||
| 977 | async fn wait_enabled(&mut self) { | ||
| 978 | poll_fn(|cx| { | ||
| 979 | let ep_index = self.info.addr.index(); | ||
| 980 | |||
| 981 | self.state.ep_out_wakers[ep_index].register(cx.waker()); | ||
| 982 | |||
| 983 | if self.regs.doepctl(ep_index).read().usbaep() { | ||
| 984 | Poll::Ready(()) | ||
| 985 | } else { | ||
| 986 | Poll::Pending | ||
| 987 | } | ||
| 988 | }) | ||
| 989 | .await | ||
| 990 | } | ||
| 991 | } | ||
| 992 | |||
| 993 | impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> { | ||
| 994 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | ||
| 995 | trace!("read start len={}", buf.len()); | ||
| 996 | |||
| 997 | poll_fn(|cx| { | ||
| 998 | let index = self.info.addr.index(); | ||
| 999 | self.state.ep_out_wakers[index].register(cx.waker()); | ||
| 1000 | |||
| 1001 | let doepctl = self.regs.doepctl(index).read(); | ||
| 1002 | trace!("read ep={:?}: doepctl {:08x}", self.info.addr, doepctl.0,); | ||
| 1003 | if !doepctl.usbaep() { | ||
| 1004 | trace!("read ep={:?} error disabled", self.info.addr); | ||
| 1005 | return Poll::Ready(Err(EndpointError::Disabled)); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | let len = self.state.ep_out_size[index].load(Ordering::Relaxed); | ||
| 1009 | if len != EP_OUT_BUFFER_EMPTY { | ||
| 1010 | trace!("read ep={:?} done len={}", self.info.addr, len); | ||
| 1011 | |||
| 1012 | if len as usize > buf.len() { | ||
| 1013 | return Poll::Ready(Err(EndpointError::BufferOverflow)); | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | // SAFETY: exclusive access ensured by `ep_out_size` atomic variable | ||
| 1017 | let data = | ||
| 1018 | unsafe { core::slice::from_raw_parts(*self.state.ep_out_buffers[index].get(), len as usize) }; | ||
| 1019 | buf[..len as usize].copy_from_slice(data); | ||
| 1020 | |||
| 1021 | // Release buffer | ||
| 1022 | self.state.ep_out_size[index].store(EP_OUT_BUFFER_EMPTY, Ordering::Release); | ||
| 1023 | |||
| 1024 | critical_section::with(|_| { | ||
| 1025 | // Receive 1 packet | ||
| 1026 | self.regs.doeptsiz(index).modify(|w| { | ||
| 1027 | w.set_xfrsiz(self.info.max_packet_size as _); | ||
| 1028 | w.set_pktcnt(1); | ||
| 1029 | }); | ||
| 1030 | |||
| 1031 | // Clear NAK to indicate we are ready to receive more data | ||
| 1032 | self.regs.doepctl(index).modify(|w| { | ||
| 1033 | w.set_cnak(true); | ||
| 1034 | }); | ||
| 1035 | }); | ||
| 1036 | |||
| 1037 | Poll::Ready(Ok(len as usize)) | ||
| 1038 | } else { | ||
| 1039 | Poll::Pending | ||
| 1040 | } | ||
| 1041 | }) | ||
| 1042 | .await | ||
| 1043 | } | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { | ||
| 1047 | async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { | ||
| 1048 | trace!("write ep={:?} data={:?}", self.info.addr, buf); | ||
| 1049 | |||
| 1050 | if buf.len() > self.info.max_packet_size as usize { | ||
| 1051 | return Err(EndpointError::BufferOverflow); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | let index = self.info.addr.index(); | ||
| 1055 | // Wait for previous transfer to complete and check if endpoint is disabled | ||
| 1056 | poll_fn(|cx| { | ||
| 1057 | self.state.ep_in_wakers[index].register(cx.waker()); | ||
| 1058 | |||
| 1059 | let diepctl = self.regs.diepctl(index).read(); | ||
| 1060 | let dtxfsts = self.regs.dtxfsts(index).read(); | ||
| 1061 | trace!( | ||
| 1062 | "write ep={:?}: diepctl {:08x} ftxfsts {:08x}", | ||
| 1063 | self.info.addr, | ||
| 1064 | diepctl.0, | ||
| 1065 | dtxfsts.0 | ||
| 1066 | ); | ||
| 1067 | if !diepctl.usbaep() { | ||
| 1068 | trace!("write ep={:?} wait for prev: error disabled", self.info.addr); | ||
| 1069 | Poll::Ready(Err(EndpointError::Disabled)) | ||
| 1070 | } else if !diepctl.epena() { | ||
| 1071 | trace!("write ep={:?} wait for prev: ready", self.info.addr); | ||
| 1072 | Poll::Ready(Ok(())) | ||
| 1073 | } else { | ||
| 1074 | trace!("write ep={:?} wait for prev: pending", self.info.addr); | ||
| 1075 | Poll::Pending | ||
| 1076 | } | ||
| 1077 | }) | ||
| 1078 | .await?; | ||
| 1079 | |||
| 1080 | if buf.len() > 0 { | ||
| 1081 | poll_fn(|cx| { | ||
| 1082 | self.state.ep_in_wakers[index].register(cx.waker()); | ||
| 1083 | |||
| 1084 | let size_words = (buf.len() + 3) / 4; | ||
| 1085 | |||
| 1086 | let fifo_space = self.regs.dtxfsts(index).read().ineptfsav() as usize; | ||
| 1087 | if size_words > fifo_space { | ||
| 1088 | // Not enough space in fifo, enable tx fifo empty interrupt | ||
| 1089 | critical_section::with(|_| { | ||
| 1090 | self.regs.diepempmsk().modify(|w| { | ||
| 1091 | w.set_ineptxfem(w.ineptxfem() | (1 << index)); | ||
| 1092 | }); | ||
| 1093 | }); | ||
| 1094 | |||
| 1095 | trace!("tx fifo for ep={} full, waiting for txfe", index); | ||
| 1096 | |||
| 1097 | Poll::Pending | ||
| 1098 | } else { | ||
| 1099 | trace!("write ep={:?} wait for fifo: ready", self.info.addr); | ||
| 1100 | Poll::Ready(()) | ||
| 1101 | } | ||
| 1102 | }) | ||
| 1103 | .await | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | // ERRATA: Transmit data FIFO is corrupted when a write sequence to the FIFO is interrupted with | ||
| 1107 | // accesses to certain OTG_FS registers. | ||
| 1108 | // | ||
| 1109 | // Prevent the interrupt (which might poke FIFOs) from executing while copying data to FIFOs. | ||
| 1110 | critical_section::with(|_| { | ||
| 1111 | // Setup transfer size | ||
| 1112 | self.regs.dieptsiz(index).write(|w| { | ||
| 1113 | w.set_mcnt(1); | ||
| 1114 | w.set_pktcnt(1); | ||
| 1115 | w.set_xfrsiz(buf.len() as _); | ||
| 1116 | }); | ||
| 1117 | |||
| 1118 | // Enable endpoint | ||
| 1119 | self.regs.diepctl(index).modify(|w| { | ||
| 1120 | w.set_cnak(true); | ||
| 1121 | w.set_epena(true); | ||
| 1122 | }); | ||
| 1123 | |||
| 1124 | // Write data to FIFO | ||
| 1125 | for chunk in buf.chunks(4) { | ||
| 1126 | let mut tmp = [0u8; 4]; | ||
| 1127 | tmp[0..chunk.len()].copy_from_slice(chunk); | ||
| 1128 | self.regs.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); | ||
| 1129 | } | ||
| 1130 | }); | ||
| 1131 | |||
| 1132 | trace!("write done ep={:?}", self.info.addr); | ||
| 1133 | |||
| 1134 | Ok(()) | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | /// USB control pipe. | ||
| 1139 | pub struct ControlPipe<'d> { | ||
| 1140 | max_packet_size: u16, | ||
| 1141 | regs: Otg, | ||
| 1142 | ep_in: Endpoint<'d, In>, | ||
| 1143 | ep_out: Endpoint<'d, Out>, | ||
| 1144 | quirk_setup_late_cnak: bool, | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | impl<'d> embassy_usb_driver::ControlPipe for ControlPipe<'d> { | ||
| 1148 | fn max_packet_size(&self) -> usize { | ||
| 1149 | usize::from(self.max_packet_size) | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | async fn setup(&mut self) -> [u8; 8] { | ||
| 1153 | poll_fn(|cx| { | ||
| 1154 | self.ep_out.state.ep_out_wakers[0].register(cx.waker()); | ||
| 1155 | |||
| 1156 | if self.ep_out.state.ep0_setup_ready.load(Ordering::Relaxed) { | ||
| 1157 | let data = unsafe { *self.ep_out.state.ep0_setup_data.get() }; | ||
| 1158 | self.ep_out.state.ep0_setup_ready.store(false, Ordering::Release); | ||
| 1159 | |||
| 1160 | // EP0 should not be controlled by `Bus` so this RMW does not need a critical section | ||
| 1161 | // Receive 1 SETUP packet | ||
| 1162 | self.regs.doeptsiz(self.ep_out.info.addr.index()).modify(|w| { | ||
| 1163 | w.set_rxdpid_stupcnt(1); | ||
| 1164 | }); | ||
| 1165 | |||
| 1166 | // Clear NAK to indicate we are ready to receive more data | ||
| 1167 | if !self.quirk_setup_late_cnak { | ||
| 1168 | self.regs | ||
| 1169 | .doepctl(self.ep_out.info.addr.index()) | ||
| 1170 | .modify(|w| w.set_cnak(true)); | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | trace!("SETUP received: {:?}", data); | ||
| 1174 | Poll::Ready(data) | ||
| 1175 | } else { | ||
| 1176 | trace!("SETUP waiting"); | ||
| 1177 | Poll::Pending | ||
| 1178 | } | ||
| 1179 | }) | ||
| 1180 | .await | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result<usize, EndpointError> { | ||
| 1184 | trace!("control: data_out"); | ||
| 1185 | let len = self.ep_out.read(buf).await?; | ||
| 1186 | trace!("control: data_out read: {:?}", &buf[..len]); | ||
| 1187 | Ok(len) | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | async fn data_in(&mut self, data: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { | ||
| 1191 | trace!("control: data_in write: {:?}", data); | ||
| 1192 | self.ep_in.write(data).await?; | ||
| 1193 | |||
| 1194 | // wait for status response from host after sending the last packet | ||
| 1195 | if last { | ||
| 1196 | trace!("control: data_in waiting for status"); | ||
| 1197 | self.ep_out.read(&mut []).await?; | ||
| 1198 | trace!("control: complete"); | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | Ok(()) | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | async fn accept(&mut self) { | ||
| 1205 | trace!("control: accept"); | ||
| 1206 | |||
| 1207 | self.ep_in.write(&[]).await.ok(); | ||
| 1208 | |||
| 1209 | trace!("control: accept OK"); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | async fn reject(&mut self) { | ||
| 1213 | trace!("control: reject"); | ||
| 1214 | |||
| 1215 | // EP0 should not be controlled by `Bus` so this RMW does not need a critical section | ||
| 1216 | self.regs.diepctl(self.ep_in.info.addr.index()).modify(|w| { | ||
| 1217 | w.set_stall(true); | ||
| 1218 | }); | ||
| 1219 | self.regs.doepctl(self.ep_out.info.addr.index()).modify(|w| { | ||
| 1220 | w.set_stall(true); | ||
| 1221 | }); | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | async fn accept_set_address(&mut self, addr: u8) { | ||
| 1225 | trace!("setting addr: {}", addr); | ||
| 1226 | critical_section::with(|_| { | ||
| 1227 | self.regs.dcfg().modify(|w| { | ||
| 1228 | w.set_dad(addr); | ||
| 1229 | }); | ||
| 1230 | }); | ||
| 1231 | |||
| 1232 | // synopsys driver requires accept to be sent after changing address | ||
| 1233 | self.accept().await | ||
| 1234 | } | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | /// Translates HAL [EndpointType] into PAC [vals::Eptyp] | ||
| 1238 | fn to_eptyp(ep_type: EndpointType) -> vals::Eptyp { | ||
| 1239 | match ep_type { | ||
| 1240 | EndpointType::Control => vals::Eptyp::CONTROL, | ||
| 1241 | EndpointType::Isochronous => vals::Eptyp::ISOCHRONOUS, | ||
| 1242 | EndpointType::Bulk => vals::Eptyp::BULK, | ||
| 1243 | EndpointType::Interrupt => vals::Eptyp::INTERRUPT, | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | /// Calculates total allocated FIFO size in words | ||
| 1248 | fn ep_fifo_size(eps: &[Option<EndpointData>]) -> u16 { | ||
| 1249 | eps.iter().map(|ep| ep.map(|ep| ep.fifo_size_words).unwrap_or(0)).sum() | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | /// Generates IRQ mask for enabled endpoints | ||
| 1253 | fn ep_irq_mask(eps: &[Option<EndpointData>]) -> u16 { | ||
| 1254 | eps.iter().enumerate().fold( | ||
| 1255 | 0, | ||
| 1256 | |mask, (index, ep)| { | ||
| 1257 | if ep.is_some() { | ||
| 1258 | mask | (1 << index) | ||
| 1259 | } else { | ||
| 1260 | mask | ||
| 1261 | } | ||
| 1262 | }, | ||
| 1263 | ) | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | /// Calculates MPSIZ value for EP0, which uses special values. | ||
| 1267 | fn ep0_mpsiz(max_packet_size: u16) -> u16 { | ||
| 1268 | match max_packet_size { | ||
| 1269 | 8 => 0b11, | ||
| 1270 | 16 => 0b10, | ||
| 1271 | 32 => 0b01, | ||
| 1272 | 64 => 0b00, | ||
| 1273 | other => panic!("Unsupported EP0 size: {}", other), | ||
| 1274 | } | ||
| 1275 | } | ||
| 1276 | |||
| 1277 | /// The number of maximum configurable EPs. | ||
| 1278 | // TODO: this should at least be configurable, but ideally not a constant. | ||
| 1279 | // Using OtgInstance::ENDPOINT_COUNT requires feature(const_generic_expr) so just define maximum eps | ||
| 1280 | pub const MAX_EP_COUNT: usize = 9; | ||
| 1281 | |||
| 1282 | /// USB instance. | ||
| 1283 | pub struct OtgInstance<'d> { | ||
| 1284 | /// The USB peripheral. | ||
| 1285 | pub regs: Otg, | ||
| 1286 | /// The USB state. | ||
| 1287 | pub state: &'d State<{ MAX_EP_COUNT }>, | ||
| 1288 | /// FIFO depth in words. | ||
| 1289 | pub fifo_depth_words: u16, | ||
| 1290 | /// Number of used endpoints. | ||
| 1291 | pub endpoint_count: usize, | ||
| 1292 | /// The PHY type. | ||
| 1293 | pub phy_type: PhyType, | ||
| 1294 | /// Extra RX FIFO words needed by some implementations. | ||
| 1295 | pub extra_rx_fifo_words: u16, | ||
| 1296 | /// Whether to set up late cnak | ||
| 1297 | pub quirk_setup_late_cnak: bool, | ||
| 1298 | /// Function to calculate TRDT value based on some internal clock speed. | ||
| 1299 | pub calculate_trdt_fn: fn(speed: vals::Dspd) -> u8, | ||
| 1300 | } | ||
