Skip to content

Commit

Permalink
Puts in the machinery to handle power detected/removed
Browse files Browse the repository at this point in the history
  • Loading branch information
huntc committed Jun 28, 2022
1 parent 9a084d3 commit 7f9c751
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 143 deletions.
70 changes: 67 additions & 3 deletions embassy-nrf/src/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
use core::task::Poll;

use cortex_m::peripheral::NVIC;
Expand All @@ -25,6 +25,7 @@ static EP0_WAKER: AtomicWaker = NEW_AW;
static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
static POWER_AVAILABLE: AtomicBool = AtomicBool::new(false);

pub struct Driver<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
Expand All @@ -39,13 +40,37 @@ impl<'d, T: Instance> Driver<'d, T> {
irq.unpend();
irq.enable();

// Initialize the bus so that it signals that power is available.
// Not required when using with_power_management as we then rely on the irq.
POWER_AVAILABLE.store(true, Ordering::Relaxed);
BUS_WAKER.wake();

Self {
phantom: PhantomData,
alloc_in: Allocator::new(),
alloc_out: Allocator::new(),
}
}

/// Establish a new device that then uses the POWER peripheral to
/// detect USB power detected/removed events are handled.
#[cfg(not(feature = "_nrf5340-app"))]
pub fn with_power_management(
_usb: impl Unborrow<Target = T> + 'd,
irq: impl Unborrow<Target = T::Interrupt> + 'd,
power_irq: impl Interrupt,
) -> Self {
let regs = unsafe { &*pac::POWER::ptr() };

power_irq.set_handler(Self::on_power_interrupt);
power_irq.unpend();
power_irq.enable();

regs.intenset.write(|w| w.usbdetected().set().usbremoved().set());

Self::new(_usb, irq)
}

fn on_interrupt(_: *mut ()) {
let regs = T::regs();

Expand Down Expand Up @@ -76,7 +101,6 @@ impl<'d, T: Instance> Driver<'d, T> {
// doesn't cause an infinite irq loop.
if regs.events_usbevent.read().bits() != 0 {
regs.events_usbevent.reset();
//regs.intenclr.write(|w| w.usbevent().clear());
BUS_WAKER.wake();
}

Expand All @@ -96,6 +120,31 @@ impl<'d, T: Instance> Driver<'d, T> {
}
}
}

#[cfg(not(feature = "_nrf5340-app"))]
fn on_power_interrupt(_: *mut ()) {
let regs = unsafe { &*pac::POWER::ptr() };

let mut power_changed = false;
let mut power_available = false;

if regs.events_usbdetected.read().bits() != 0 {
regs.events_usbdetected.reset();
power_changed = true;
power_available = true;
}

if regs.events_usbremoved.read().bits() != 0 {
regs.events_usbremoved.reset();
power_changed = true;
power_available = false;
}

if power_changed {
POWER_AVAILABLE.store(power_available, Ordering::Relaxed);
BUS_WAKER.wake();
}
}
}

impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
Expand Down Expand Up @@ -138,7 +187,10 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {

fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
(
Bus { phantom: PhantomData },
Bus {
phantom: PhantomData,
power_available: false,
},
ControlPipe {
_phantom: PhantomData,
max_packet_size: control_max_packet_size,
Expand All @@ -149,6 +201,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {

pub struct Bus<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
power_available: bool,
}

impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
Expand Down Expand Up @@ -246,6 +299,17 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
trace!("USB event: ready");
}

if POWER_AVAILABLE.load(Ordering::Relaxed) != self.power_available {
self.power_available = !self.power_available;
if self.power_available {
trace!("Power event: available");
return Poll::Ready(Event::PowerDetected);
} else {
trace!("Power event: removed");
return Poll::Ready(Event::PowerRemoved);
}
}

Poll::Pending
})
}
Expand Down
83 changes: 47 additions & 36 deletions embassy-stm32/src/usb/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ impl<'d, T: Instance> Driver<'d, T> {
dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
}

// Initialize the bus so that it signals that power is available
BUS_WAKER.wake();

Self {
phantom: PhantomData,
alloc: [EndpointData {
Expand Down Expand Up @@ -406,6 +409,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
Bus {
phantom: PhantomData,
ep_types,
inited: false,
},
ControlPipe {
_phantom: PhantomData,
Expand All @@ -420,6 +424,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
pub struct Bus<'d, T: Instance> {
phantom: PhantomData<&'d mut T>,
ep_types: [EpType; EP_COUNT - 1],
inited: bool,
}

impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
Expand All @@ -428,53 +433,59 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> {
poll_fn(move |cx| unsafe {
BUS_WAKER.register(cx.waker());
let regs = T::regs();

let flags = IRQ_FLAGS.load(Ordering::Acquire);
if self.inited {
let regs = T::regs();

if flags & IRQ_FLAG_RESUME != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
return Poll::Ready(Event::Resume);
}
let flags = IRQ_FLAGS.load(Ordering::Acquire);

if flags & IRQ_FLAG_RESET != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel);
if flags & IRQ_FLAG_RESUME != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
return Poll::Ready(Event::Resume);
}

trace!("RESET REGS WRITINGINGING");
regs.daddr().write(|w| {
w.set_ef(true);
w.set_add(0);
});
if flags & IRQ_FLAG_RESET != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel);

regs.epr(0).write(|w| {
w.set_ep_type(EpType::CONTROL);
w.set_stat_rx(Stat::NAK);
w.set_stat_tx(Stat::NAK);
});
trace!("RESET REGS WRITINGINGING");
regs.daddr().write(|w| {
w.set_ef(true);
w.set_add(0);
});

for i in 1..EP_COUNT {
regs.epr(i).write(|w| {
w.set_ea(i as _);
w.set_ep_type(self.ep_types[i - 1]);
})
}
regs.epr(0).write(|w| {
w.set_ep_type(EpType::CONTROL);
w.set_stat_rx(Stat::NAK);
w.set_stat_tx(Stat::NAK);
});

for i in 1..EP_COUNT {
regs.epr(i).write(|w| {
w.set_ea(i as _);
w.set_ep_type(self.ep_types[i - 1]);
})
}

for w in &EP_IN_WAKERS {
w.wake()
}
for w in &EP_OUT_WAKERS {
w.wake()
for w in &EP_IN_WAKERS {
w.wake()
}
for w in &EP_OUT_WAKERS {
w.wake()
}

return Poll::Ready(Event::Reset);
}

return Poll::Ready(Event::Reset);
}
if flags & IRQ_FLAG_SUSPEND != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel);
return Poll::Ready(Event::Suspend);
}

if flags & IRQ_FLAG_SUSPEND != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel);
return Poll::Ready(Event::Suspend);
Poll::Pending
} else {
self.inited = true;
return Poll::Ready(Event::PowerDetected);
}

Poll::Pending
})
}

Expand Down
6 changes: 6 additions & 0 deletions embassy-usb/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ pub enum Event {
/// A USB resume request has been detected after being suspended or, in the case of self-powered
/// devices, the device has been connected to the USB bus.
Resume,

/// The USB power has been detected.
PowerDetected,

/// The USB power has been removed. Not supported by all devices.
PowerRemoved,
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
Expand Down
20 changes: 17 additions & 3 deletions embassy-usb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub mod descriptor;
mod descriptor_reader;
pub mod driver;
pub mod types;
pub mod util;

use embassy::util::{select, Either};
use heapless::Vec;
Expand Down Expand Up @@ -115,6 +114,7 @@ struct Inner<'d, D: Driver<'d>> {

device_state: UsbDeviceState,
suspended: bool,
power_available: bool,
remote_wakeup_enabled: bool,
self_powered: bool,

Expand Down Expand Up @@ -157,6 +157,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {

device_state: UsbDeviceState::Disabled,
suspended: false,
power_available: false,
remote_wakeup_enabled: false,
self_powered: false,
address: 0,
Expand Down Expand Up @@ -186,6 +187,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
/// before calling any other `UsbDevice` methods to fully reset the
/// peripheral.
pub async fn run_until_suspend(&mut self) -> () {
while !self.inner.power_available {
let evt = self.inner.bus.poll().await;
self.inner.handle_bus_event(evt);
}

if self.inner.device_state == UsbDeviceState::Disabled {
self.inner.bus.enable().await;
self.inner.device_state = UsbDeviceState::Default;
Expand All @@ -195,7 +201,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
}
}

while !self.inner.suspended {
while !self.inner.suspended && self.inner.power_available {
let control_fut = self.control.setup();
let bus_fut = self.inner.bus.poll();
match select(bus_fut, control_fut).await {
Expand Down Expand Up @@ -223,7 +229,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
///
/// This future is cancel-safe.
pub async fn wait_resume(&mut self) {
while self.inner.suspended {
while self.inner.suspended || !self.inner.power_available {
let evt = self.inner.bus.poll().await;
self.inner.handle_bus_event(evt);
}
Expand Down Expand Up @@ -377,6 +383,14 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
h.suspended(true);
}
}
Event::PowerDetected => {
trace!("usb: power detected");
self.power_available = true;
}
Event::PowerRemoved => {
trace!("usb: power removed");
self.power_available = false;
}
}
}

Expand Down
Loading

0 comments on commit 7f9c751

Please sign in to comment.