diff options
| author | Guillaume MICHEL <[email protected]> | 2022-11-07 17:46:32 +0100 |
|---|---|---|
| committer | Guillaume MICHEL <[email protected]> | 2022-11-07 17:46:32 +0100 |
| commit | 1365ce6ab88d4891ddad945fae9f71043f521fb6 (patch) | |
| tree | cff76e77fc3eca5a5054cb51f973d594b77f4bb7 | |
| parent | b99533607ceed225dd12ae73aaa9a0d969a7365e (diff) | |
embassy-stm32: Fix bug when Uart::read future is dropped and DMA request was not stopped
fixes issue #1045
regression was introduced with PR #1031
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 162 |
1 files changed, 99 insertions, 63 deletions
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0a1ea94ed..aea054a4b 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -6,6 +6,8 @@ use core::task::Poll; | |||
| 6 | 6 | ||
| 7 | use atomic_polyfill::{compiler_fence, Ordering}; | 7 | use atomic_polyfill::{compiler_fence, Ordering}; |
| 8 | use embassy_cortex_m::interrupt::InterruptExt; | 8 | use embassy_cortex_m::interrupt::InterruptExt; |
| 9 | use embassy_futures::select::{select, Either}; | ||
| 10 | use embassy_hal_common::drop::OnDrop; | ||
| 9 | use embassy_hal_common::{into_ref, PeripheralRef}; | 11 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 10 | 12 | ||
| 11 | use crate::dma::NoDma; | 13 | use crate::dma::NoDma; |
| @@ -85,6 +87,13 @@ pub enum Error { | |||
| 85 | BufferTooLong, | 87 | BufferTooLong, |
| 86 | } | 88 | } |
| 87 | 89 | ||
| 90 | enum ReadCompletionEvent { | ||
| 91 | // DMA Read transfer completed first | ||
| 92 | DmaCompleted, | ||
| 93 | // Idle line detected first | ||
| 94 | Idle, | ||
| 95 | } | ||
| 96 | |||
| 88 | pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { | 97 | pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { |
| 89 | tx: UartTx<'d, T, TxDma>, | 98 | tx: UartTx<'d, T, TxDma>, |
| 90 | rx: UartRx<'d, T, RxDma>, | 99 | rx: UartRx<'d, T, RxDma>, |
| @@ -385,30 +394,50 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { | |||
| 385 | self.inner_read(buffer, true).await | 394 | self.inner_read(buffer, true).await |
| 386 | } | 395 | } |
| 387 | 396 | ||
| 388 | async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error> | 397 | async fn inner_read_run( |
| 398 | &mut self, | ||
| 399 | buffer: &mut [u8], | ||
| 400 | enable_idle_line_detection: bool, | ||
| 401 | ) -> Result<ReadCompletionEvent, Error> | ||
| 389 | where | 402 | where |
| 390 | RxDma: crate::usart::RxDma<T>, | 403 | RxDma: crate::usart::RxDma<T>, |
| 391 | { | 404 | { |
| 392 | if buffer.is_empty() { | ||
| 393 | return Ok(0); | ||
| 394 | } else if buffer.len() > 0xFFFF { | ||
| 395 | return Err(Error::BufferTooLong); | ||
| 396 | } | ||
| 397 | |||
| 398 | let r = T::regs(); | 405 | let r = T::regs(); |
| 399 | 406 | ||
| 400 | let buffer_len = buffer.len(); | 407 | // make sure USART state is restored to neutral state when this future is dropped |
| 408 | let _drop = OnDrop::new(move || { | ||
| 409 | // defmt::trace!("Clear all USART interrupts and DMA Read Request"); | ||
| 410 | // clear all interrupts and DMA Rx Request | ||
| 411 | // SAFETY: only clears Rx related flags | ||
| 412 | unsafe { | ||
| 413 | r.cr1().modify(|w| { | ||
| 414 | // disable RXNE interrupt | ||
| 415 | w.set_rxneie(false); | ||
| 416 | // disable parity interrupt | ||
| 417 | w.set_peie(false); | ||
| 418 | // disable idle line interrupt | ||
| 419 | w.set_idleie(false); | ||
| 420 | }); | ||
| 421 | r.cr3().modify(|w| { | ||
| 422 | // disable Error Interrupt: (Frame error, Noise error, Overrun error) | ||
| 423 | w.set_eie(false); | ||
| 424 | // disable DMA Rx Request | ||
| 425 | w.set_dmar(false); | ||
| 426 | }); | ||
| 427 | } | ||
| 428 | }); | ||
| 401 | 429 | ||
| 402 | let ch = &mut self.rx_dma; | 430 | let ch = &mut self.rx_dma; |
| 403 | let request = ch.request(); | 431 | let request = ch.request(); |
| 404 | 432 | ||
| 433 | // Start USART DMA | ||
| 434 | // will not do anything yet because DMAR is not yet set | ||
| 435 | // future which will complete when DMA Read request completes | ||
| 436 | let transfer = crate::dma::read(ch, request, rdr(T::regs()), buffer); | ||
| 437 | |||
| 405 | // SAFETY: The only way we might have a problem is using split rx and tx | 438 | // SAFETY: The only way we might have a problem is using split rx and tx |
| 406 | // here we only modify or read Rx related flags, interrupts and DMA channel | 439 | // here we only modify or read Rx related flags, interrupts and DMA channel |
| 407 | unsafe { | 440 | unsafe { |
| 408 | // Start USART DMA | ||
| 409 | // will not do anything yet because DMAR is not yet set | ||
| 410 | ch.start_read(request, rdr(r), buffer, Default::default()); | ||
| 411 | |||
| 412 | // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer | 441 | // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer |
| 413 | if !self.detect_previous_overrun { | 442 | if !self.detect_previous_overrun { |
| 414 | let sr = sr(r).read(); | 443 | let sr = sr(r).read(); |
| @@ -443,9 +472,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { | |||
| 443 | // something went wrong | 472 | // something went wrong |
| 444 | // because the only way to get this flag cleared is to have an interrupt | 473 | // because the only way to get this flag cleared is to have an interrupt |
| 445 | 474 | ||
| 446 | // abort DMA transfer | 475 | // DMA will be stopped when transfer is dropped |
| 447 | ch.request_stop(); | ||
| 448 | while ch.is_running() {} | ||
| 449 | 476 | ||
| 450 | let sr = sr(r).read(); | 477 | let sr = sr(r).read(); |
| 451 | // This read also clears the error and idle interrupt flags on v1. | 478 | // This read also clears the error and idle interrupt flags on v1. |
| @@ -468,26 +495,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { | |||
| 468 | unreachable!(); | 495 | unreachable!(); |
| 469 | } | 496 | } |
| 470 | 497 | ||
| 471 | // clear idle flag | 498 | if !enable_idle_line_detection { |
| 472 | if enable_idle_line_detection { | 499 | transfer.await; |
| 473 | let sr = sr(r).read(); | ||
| 474 | // This read also clears the error and idle interrupt flags on v1. | ||
| 475 | rdr(r).read_volatile(); | ||
| 476 | clear_interrupt_flags(r, sr); | ||
| 477 | 500 | ||
| 478 | // enable idle interrupt | 501 | return Ok(ReadCompletionEvent::DmaCompleted); |
| 479 | r.cr1().modify(|w| { | ||
| 480 | w.set_idleie(true); | ||
| 481 | }); | ||
| 482 | } | 502 | } |
| 503 | |||
| 504 | // clear idle flag | ||
| 505 | let sr = sr(r).read(); | ||
| 506 | // This read also clears the error and idle interrupt flags on v1. | ||
| 507 | rdr(r).read_volatile(); | ||
| 508 | clear_interrupt_flags(r, sr); | ||
| 509 | |||
| 510 | // enable idle interrupt | ||
| 511 | r.cr1().modify(|w| { | ||
| 512 | w.set_idleie(true); | ||
| 513 | }); | ||
| 483 | } | 514 | } |
| 484 | 515 | ||
| 485 | compiler_fence(Ordering::SeqCst); | 516 | compiler_fence(Ordering::SeqCst); |
| 486 | 517 | ||
| 487 | let res = poll_fn(move |cx| { | 518 | // future which completes when idle line is detected |
| 519 | let idle = poll_fn(move |cx| { | ||
| 488 | let s = T::state(); | 520 | let s = T::state(); |
| 489 | 521 | ||
| 490 | ch.set_waker(cx.waker()); | ||
| 491 | s.rx_waker.register(cx.waker()); | 522 | s.rx_waker.register(cx.waker()); |
| 492 | 523 | ||
| 493 | // SAFETY: read only and we only use Rx related flags | 524 | // SAFETY: read only and we only use Rx related flags |
| @@ -507,10 +538,6 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { | |||
| 507 | if has_errors { | 538 | if has_errors { |
| 508 | // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler | 539 | // all Rx interrupts and Rx DMA Request have already been cleared in interrupt handler |
| 509 | 540 | ||
| 510 | // stop dma transfer | ||
| 511 | ch.request_stop(); | ||
| 512 | while ch.is_running() {} | ||
| 513 | |||
| 514 | if sr.pe() { | 541 | if sr.pe() { |
| 515 | return Poll::Ready(Err(Error::Parity)); | 542 | return Poll::Ready(Err(Error::Parity)); |
| 516 | } | 543 | } |
| @@ -525,45 +552,54 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { | |||
| 525 | } | 552 | } |
| 526 | } | 553 | } |
| 527 | 554 | ||
| 528 | if enable_idle_line_detection && sr.idle() { | 555 | if sr.idle() { |
| 529 | // Idle line | 556 | // Idle line detected |
| 557 | return Poll::Ready(Ok(())); | ||
| 558 | } | ||
| 530 | 559 | ||
| 531 | // stop dma transfer | 560 | Poll::Pending |
| 532 | ch.request_stop(); | 561 | }); |
| 533 | while ch.is_running() {} | ||
| 534 | 562 | ||
| 535 | let n = buffer_len - (ch.remaining_transfers() as usize); | 563 | // wait for the first of DMA request or idle line detected to completes |
| 564 | // select consumes its arguments | ||
| 565 | // when transfer is dropped, it will stop the DMA request | ||
| 566 | match select(transfer, idle).await { | ||
| 567 | // DMA transfer completed first | ||
| 568 | Either::First(()) => Ok(ReadCompletionEvent::DmaCompleted), | ||
| 536 | 569 | ||
| 537 | return Poll::Ready(Ok(n)); | 570 | // Idle line detected first |
| 538 | } else if !ch.is_running() { | 571 | Either::Second(Ok(())) => Ok(ReadCompletionEvent::Idle), |
| 539 | // DMA complete | ||
| 540 | return Poll::Ready(Ok(buffer_len)); | ||
| 541 | } | ||
| 542 | 572 | ||
| 543 | Poll::Pending | 573 | // error occurred |
| 544 | }) | 574 | Either::Second(Err(e)) => Err(e), |
| 545 | .await; | 575 | } |
| 576 | } | ||
| 546 | 577 | ||
| 547 | // clear all interrupts and DMA Rx Request | 578 | async fn inner_read(&mut self, buffer: &mut [u8], enable_idle_line_detection: bool) -> Result<usize, Error> |
| 548 | // SAFETY: only clears Rx related flags | 579 | where |
| 549 | unsafe { | 580 | RxDma: crate::usart::RxDma<T>, |
| 550 | r.cr1().modify(|w| { | 581 | { |
| 551 | // disable RXNE interrupt | 582 | if buffer.is_empty() { |
| 552 | w.set_rxneie(false); | 583 | return Ok(0); |
| 553 | // disable parity interrupt | 584 | } else if buffer.len() > 0xFFFF { |
| 554 | w.set_peie(false); | 585 | return Err(Error::BufferTooLong); |
| 555 | // disable idle line interrupt | ||
| 556 | w.set_idleie(false); | ||
| 557 | }); | ||
| 558 | r.cr3().modify(|w| { | ||
| 559 | // disable Error Interrupt: (Frame error, Noise error, Overrun error) | ||
| 560 | w.set_eie(false); | ||
| 561 | // disable DMA Rx Request | ||
| 562 | w.set_dmar(false); | ||
| 563 | }); | ||
| 564 | } | 586 | } |
| 565 | 587 | ||
| 566 | res | 588 | let buffer_len = buffer.len(); |
| 589 | |||
| 590 | // wait for DMA to complete or IDLE line detection if requested | ||
| 591 | let res = self.inner_read_run(buffer, enable_idle_line_detection).await; | ||
| 592 | |||
| 593 | let ch = &mut self.rx_dma; | ||
| 594 | |||
| 595 | match res { | ||
| 596 | Ok(ReadCompletionEvent::DmaCompleted) => Ok(buffer_len), | ||
| 597 | Ok(ReadCompletionEvent::Idle) => { | ||
| 598 | let n = buffer_len - (ch.remaining_transfers() as usize); | ||
| 599 | Ok(n) | ||
| 600 | } | ||
| 601 | Err(e) => Err(e), | ||
| 602 | } | ||
| 567 | } | 603 | } |
| 568 | } | 604 | } |
| 569 | 605 | ||
