stm32/usb: use separate irq flags.

- Fixes race condition that could cause losing irqs (because `if flags != 0` was clearing all)
- Doesn't need CAS, which is nice for thumbv6.
This commit is contained in:
Dario Nieuwenhuis 2022-12-23 20:45:51 +01:00
parent 40ef66cdfb
commit cd9a65ba39

View File

@ -2,10 +2,9 @@
use core::future::poll_fn; use core::future::poll_fn;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::sync::atomic::Ordering; use core::sync::atomic::{AtomicBool, Ordering};
use core::task::Poll; use core::task::Poll;
use atomic_polyfill::{AtomicBool, AtomicU8};
use embassy_hal_common::into_ref; use embassy_hal_common::into_ref;
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use embassy_time::{block_for, Duration}; use embassy_time::{block_for, Duration};
@ -35,10 +34,9 @@ static BUS_WAKER: AtomicWaker = NEW_AW;
static EP0_SETUP: AtomicBool = AtomicBool::new(false); static EP0_SETUP: AtomicBool = AtomicBool::new(false);
static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
static IRQ_FLAGS: AtomicU8 = AtomicU8::new(0); static IRQ_RESET: AtomicBool = AtomicBool::new(false);
const IRQ_FLAG_RESET: u8 = 0x01; static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false);
const IRQ_FLAG_SUSPEND: u8 = 0x02; static IRQ_RESUME: AtomicBool = AtomicBool::new(false);
const IRQ_FLAG_RESUME: u8 = 0x04;
fn convert_type(t: EndpointType) -> EpType { fn convert_type(t: EndpointType) -> EpType {
match t { match t {
@ -184,47 +182,51 @@ impl<'d, T: Instance> Driver<'d, T> {
let istr = regs.istr().read(); let istr = regs.istr().read();
let mut flags: u8 = 0;
if istr.susp() { if istr.susp() {
//trace!("USB IRQ: susp"); //trace!("USB IRQ: susp");
flags |= IRQ_FLAG_SUSPEND; IRQ_SUSPEND.store(true, Ordering::Relaxed);
regs.cntr().modify(|w| { regs.cntr().modify(|w| {
w.set_fsusp(true); w.set_fsusp(true);
w.set_lpmode(true); w.set_lpmode(true);
}) });
// Write 0 to clear.
let mut clear = regs::Istr(!0);
clear.set_susp(false);
regs.istr().write_value(clear);
// Wake main thread.
BUS_WAKER.wake();
} }
if istr.wkup() { if istr.wkup() {
//trace!("USB IRQ: wkup"); //trace!("USB IRQ: wkup");
flags |= IRQ_FLAG_RESUME; IRQ_RESUME.store(true, Ordering::Relaxed);
regs.cntr().modify(|w| { regs.cntr().modify(|w| {
w.set_fsusp(false); w.set_fsusp(false);
w.set_lpmode(false); w.set_lpmode(false);
}) });
// Write 0 to clear.
let mut clear = regs::Istr(!0);
clear.set_wkup(false);
regs.istr().write_value(clear);
// Wake main thread.
BUS_WAKER.wake();
} }
if istr.reset() { if istr.reset() {
//trace!("USB IRQ: reset"); //trace!("USB IRQ: reset");
flags |= IRQ_FLAG_RESET; IRQ_RESET.store(true, Ordering::Relaxed);
// Write 0 to clear. // Write 0 to clear.
let mut clear = regs::Istr(!0); let mut clear = regs::Istr(!0);
clear.set_reset(false); clear.set_reset(false);
regs.istr().write_value(clear); regs.istr().write_value(clear);
}
if flags != 0 { // Wake main thread.
// Send irqs to main thread.
IRQ_FLAGS.fetch_or(flags, Ordering::AcqRel);
BUS_WAKER.wake(); BUS_WAKER.wake();
// Clear them
let mut mask = regs::Istr(0);
mask.set_wkup(true);
mask.set_susp(true);
mask.set_reset(true);
regs.istr().write_value(regs::Istr(!(istr.0 & mask.0)));
} }
if istr.ctr() { if istr.ctr() {
@ -436,17 +438,15 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
if self.inited { if self.inited {
let regs = T::regs(); let regs = T::regs();
let flags = IRQ_FLAGS.load(Ordering::Acquire); if IRQ_RESUME.load(Ordering::Acquire) {
IRQ_RESUME.store(false, Ordering::Relaxed);
if flags & IRQ_FLAG_RESUME != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
return Poll::Ready(Event::Resume); return Poll::Ready(Event::Resume);
} }
if flags & IRQ_FLAG_RESET != 0 { if IRQ_RESET.load(Ordering::Acquire) {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); IRQ_RESET.store(false, Ordering::Relaxed);
trace!("RESET REGS WRITINGINGING"); trace!("RESET");
regs.daddr().write(|w| { regs.daddr().write(|w| {
w.set_ef(true); w.set_ef(true);
w.set_add(0); w.set_add(0);
@ -475,8 +475,8 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
return Poll::Ready(Event::Reset); return Poll::Ready(Event::Reset);
} }
if flags & IRQ_FLAG_SUSPEND != 0 { if IRQ_SUSPEND.load(Ordering::Acquire) {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); IRQ_SUSPEND.store(false, Ordering::Relaxed);
return Poll::Ready(Event::Suspend); return Poll::Ready(Event::Suspend);
} }