aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <[email protected]>2023-04-17 15:24:24 -0500
committerxoviat <[email protected]>2023-04-17 15:24:24 -0500
commitf5216624bb228b5b70f97f0e7ebc40ea4d7e1a27 (patch)
tree8667ea23da2612978a0bcb9fc7b0c892040cb9b3
parent201a038134a7863700c3e1c1f55a03b52504d0b2 (diff)
stm32/i2c: fix races when using dma.
fixes #1341.
-rw-r--r--embassy-stm32/src/i2c/v2.rs103
1 files changed, 41 insertions, 62 deletions
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 7218f7706..44237b890 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -1,6 +1,5 @@
1use core::cmp; 1use core::cmp;
2use core::future::poll_fn; 2use core::future::poll_fn;
3use core::sync::atomic::{AtomicUsize, Ordering};
4use core::task::Poll; 3use core::task::Poll;
5 4
6use embassy_embedded_hal::SetConfig; 5use embassy_embedded_hal::SetConfig;
@@ -35,14 +34,12 @@ impl Default for Config {
35 34
36pub struct State { 35pub struct State {
37 waker: AtomicWaker, 36 waker: AtomicWaker,
38 chunks_transferred: AtomicUsize,
39} 37}
40 38
41impl State { 39impl State {
42 pub(crate) const fn new() -> Self { 40 pub(crate) const fn new() -> Self {
43 Self { 41 Self {
44 waker: AtomicWaker::new(), 42 waker: AtomicWaker::new(),
45 chunks_transferred: AtomicUsize::new(0),
46 } 43 }
47 } 44 }
48} 45}
@@ -130,10 +127,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
130 let isr = regs.isr().read(); 127 let isr = regs.isr().read();
131 128
132 if isr.tcr() || isr.tc() { 129 if isr.tcr() || isr.tc() {
133 let state = T::state(); 130 T::state().waker.wake();
134 let transferred = state.chunks_transferred.load(Ordering::Relaxed);
135 state.chunks_transferred.store(transferred + 1, Ordering::Relaxed);
136 state.waker.wake();
137 } 131 }
138 // The flag can only be cleared by writting to nbytes, we won't do that here, so disable 132 // The flag can only be cleared by writting to nbytes, we won't do that here, so disable
139 // the interrupt 133 // the interrupt
@@ -457,12 +451,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
457 TXDMA: crate::i2c::TxDma<T>, 451 TXDMA: crate::i2c::TxDma<T>,
458 { 452 {
459 let total_len = write.len(); 453 let total_len = write.len();
460 let completed_chunks = total_len / 255;
461 let total_chunks = if completed_chunks * 255 == total_len {
462 completed_chunks
463 } else {
464 completed_chunks + 1
465 };
466 454
467 let dma_transfer = unsafe { 455 let dma_transfer = unsafe {
468 let regs = T::regs(); 456 let regs = T::regs();
@@ -480,7 +468,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
480 }; 468 };
481 469
482 let state = T::state(); 470 let state = T::state();
483 state.chunks_transferred.store(0, Ordering::Relaxed);
484 let mut remaining_len = total_len; 471 let mut remaining_len = total_len;
485 472
486 let on_drop = OnDrop::new(|| { 473 let on_drop = OnDrop::new(|| {
@@ -495,33 +482,31 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
495 } 482 }
496 }); 483 });
497 484
498 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
499 if first_slice {
500 unsafe {
501 Self::master_write(
502 address,
503 total_len.min(255),
504 Stop::Software,
505 (total_chunks != 1) || !last_slice,
506 &check_timeout,
507 )?;
508 }
509 } else {
510 unsafe {
511 Self::master_continue(total_len.min(255), (total_chunks != 1) || !last_slice, &check_timeout)?;
512 T::regs().cr1().modify(|w| w.set_tcie(true));
513 }
514 }
515
516 poll_fn(|cx| { 485 poll_fn(|cx| {
517 state.waker.register(cx.waker()); 486 state.waker.register(cx.waker());
518 let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed);
519 487
520 if chunks_transferred == total_chunks { 488 if remaining_len == total_len {
489 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
490 if first_slice {
491 unsafe {
492 Self::master_write(
493 address,
494 total_len.min(255),
495 Stop::Software,
496 (total_len > 255) || !last_slice,
497 &check_timeout,
498 )?;
499 }
500 } else {
501 unsafe {
502 Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?;
503 T::regs().cr1().modify(|w| w.set_tcie(true));
504 }
505 }
506 } else if remaining_len == 0 {
521 return Poll::Ready(Ok(())); 507 return Poll::Ready(Ok(()));
522 } else if chunks_transferred != 0 { 508 } else {
523 remaining_len = remaining_len.saturating_sub(255); 509 let last_piece = (remaining_len <= 255) && last_slice;
524 let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
525 510
526 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers 511 // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
527 unsafe { 512 unsafe {
@@ -531,6 +516,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
531 T::regs().cr1().modify(|w| w.set_tcie(true)); 516 T::regs().cr1().modify(|w| w.set_tcie(true));
532 } 517 }
533 } 518 }
519
520 remaining_len = remaining_len.saturating_sub(255);
534 Poll::Pending 521 Poll::Pending
535 }) 522 })
536 .await?; 523 .await?;
@@ -559,12 +546,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
559 RXDMA: crate::i2c::RxDma<T>, 546 RXDMA: crate::i2c::RxDma<T>,
560 { 547 {
561 let total_len = buffer.len(); 548 let total_len = buffer.len();
562 let completed_chunks = total_len / 255;
563 let total_chunks = if completed_chunks * 255 == total_len {
564 completed_chunks
565 } else {
566 completed_chunks + 1
567 };
568 549
569 let dma_transfer = unsafe { 550 let dma_transfer = unsafe {
570 let regs = T::regs(); 551 let regs = T::regs();
@@ -580,7 +561,6 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
580 }; 561 };
581 562
582 let state = T::state(); 563 let state = T::state();
583 state.chunks_transferred.store(0, Ordering::Relaxed);
584 let mut remaining_len = total_len; 564 let mut remaining_len = total_len;
585 565
586 let on_drop = OnDrop::new(|| { 566 let on_drop = OnDrop::new(|| {
@@ -593,27 +573,24 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
593 } 573 }
594 }); 574 });
595 575
596 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
597 unsafe {
598 Self::master_read(
599 address,
600 total_len.min(255),
601 Stop::Software,
602 total_chunks != 1,
603 restart,
604 &check_timeout,
605 )?;
606 }
607
608 poll_fn(|cx| { 576 poll_fn(|cx| {
609 state.waker.register(cx.waker()); 577 state.waker.register(cx.waker());
610 let chunks_transferred = state.chunks_transferred.load(Ordering::Relaxed); 578 if remaining_len == total_len {
611 579 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
612 if chunks_transferred == total_chunks { 580 unsafe {
581 Self::master_read(
582 address,
583 total_len.min(255),
584 Stop::Software,
585 total_len > 255,
586 restart,
587 &check_timeout,
588 )?;
589 }
590 } else if remaining_len == 0 {
613 return Poll::Ready(Ok(())); 591 return Poll::Ready(Ok(()));
614 } else if chunks_transferred != 0 { 592 } else {
615 remaining_len = remaining_len.saturating_sub(255); 593 let last_piece = remaining_len <= 255;
616 let last_piece = chunks_transferred + 1 == total_chunks;
617 594
618 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers 595 // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
619 unsafe { 596 unsafe {
@@ -623,6 +600,8 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
623 T::regs().cr1().modify(|w| w.set_tcie(true)); 600 T::regs().cr1().modify(|w| w.set_tcie(true));
624 } 601 }
625 } 602 }
603
604 remaining_len = remaining_len.saturating_sub(255);
626 Poll::Pending 605 Poll::Pending
627 }) 606 })
628 .await?; 607 .await?;