aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume MICHEL <[email protected]>2022-11-07 17:46:32 +0100
committerGuillaume MICHEL <[email protected]>2022-11-07 17:46:32 +0100
commit1365ce6ab88d4891ddad945fae9f71043f521fb6 (patch)
treecff76e77fc3eca5a5054cb51f973d594b77f4bb7
parentb99533607ceed225dd12ae73aaa9a0d969a7365e (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.rs162
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
7use atomic_polyfill::{compiler_fence, Ordering}; 7use atomic_polyfill::{compiler_fence, Ordering};
8use embassy_cortex_m::interrupt::InterruptExt; 8use embassy_cortex_m::interrupt::InterruptExt;
9use embassy_futures::select::{select, Either};
10use embassy_hal_common::drop::OnDrop;
9use embassy_hal_common::{into_ref, PeripheralRef}; 11use embassy_hal_common::{into_ref, PeripheralRef};
10 12
11use crate::dma::NoDma; 13use crate::dma::NoDma;
@@ -85,6 +87,13 @@ pub enum Error {
85 BufferTooLong, 87 BufferTooLong,
86} 88}
87 89
90enum ReadCompletionEvent {
91 // DMA Read transfer completed first
92 DmaCompleted,
93 // Idle line detected first
94 Idle,
95}
96
88pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { 97pub 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