Puts in the machinery to handle power detected/removed

This commit is contained in:
huntc
2022-06-16 16:08:58 +10:00
parent c46e9b6cfc
commit 4a8f117f25
6 changed files with 144 additions and 145 deletions

View File

@ -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)]

View File

@ -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;
@ -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,
@ -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,
@ -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;
@ -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 {
@ -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);
}
@ -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;
}
}
}

View File

@ -1,68 +0,0 @@
use embassy::channel::signal::Signal;
use embassy::util::{select, Either};
use crate::driver::Driver;
use crate::UsbDevice;
/// Am enabled usb device is a device that further receives external notifications
/// regarding whether it is enabled or not. A common example of where this is
/// required is when receiving notifications from the POWER peripheral that
/// USB has been connected to or removed. The device here wraps an existing
/// USB device, keeping it publically available so that device-oriented operations
/// may still be performed. A signal is also provided that enables/disables the
/// USB device, taking care of suspension and resumption. In the case of the POWER
/// peripheral, this signal can be used from within a POWER_CLOCK interrupt
/// handler. Alternatively, for softdevice usage where the POWER peripheral is not
/// available, similar USB power events can be leveraged.
pub struct EnabledUsbDevice<'d, D: Driver<'d>> {
pub underlying: UsbDevice<'d, D>,
enable_usb_signal: &'d Signal<bool>,
}
impl<'d, D: Driver<'d>> EnabledUsbDevice<'d, D> {
/// Wrap an existing UsbDevice and take a signal that will be used
/// to enable/disable it, perhaps from an external POWER_CLOCK
/// interrupt, or the equivalent when dealing with softdevices.
pub fn new(underlying: UsbDevice<'d, D>, enable_usb_signal: &'d Signal<bool>) -> Self {
Self {
underlying,
enable_usb_signal,
}
}
/// Runs the underlying `UsbDevice` taking care of reacting to USB becoming
/// enabled/disabled.
///
/// This future may leave the bus in an invalid state if it is dropped.
/// After dropping the future, [`UsbDevice::disable()`] should be called
/// before calling any other `UsbDevice` methods to fully reset the
/// peripheral.
pub async fn run(&mut self) -> ! {
while !self.enable_usb_signal.wait().await {}
loop {
match select(
self.underlying.run_until_suspend(),
self.enable_usb_signal.wait(),
)
.await
{
Either::First(_) => {}
Either::Second(enable) => {
if !enable {
self.underlying.disable().await;
while !self.enable_usb_signal.wait().await {}
}
}
}
match select(self.underlying.wait_resume(), self.enable_usb_signal.wait()).await {
Either::First(_) => {}
Either::Second(enable) => {
if !enable {
self.underlying.disable().await;
while !self.enable_usb_signal.wait().await {}
}
}
}
}
}
}