Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support remote wakeup from suspend #30

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jedrzejboczar
Copy link

This closes #28.

This PR exposes remote_wakeup method that can be used to implement remote wakeup from USB suspend (e.g. for devices like keyboards/mice). I have tested it on STM32F072 device.

To perform remote wakeup, device must set CNTR.RESUME bit for 1-15 ms, so in order to properly implement it we must have means to measure time until clearing this bit. The UsbBus does not know current time, so it cannot do the countdown. While in general UsbBus could enable and use SOF interrupt (1 kHz) to count time for some needs, during suspend USB host doesn't send SOF (this is actually the indicator of USB suspend), so the whole countdown must be implemented in user application.

With the proposed API, user must first call remote_wakeup(true) and then must ensure to call remote_wakeup(false) during 1-15 ms time window. This can be implemented with a simple counter e.g. like this:

pub struct Usb {
    pub dev: UsbDevice<'static, Bus>,
    /// ...
    wake_up_counter: u8,
}

/// Set current wake up state; must be called repeatedly at 1 ms intervals
pub fn wake_up_update(&mut self, wake_up: bool) {
    if wake_up && self.wake_up_counter == 0 {
        self.dev.bus().remote_wakeup(true);
        self.wake_up_counter = 10;
    } else {
        self.wake_up_counter = self.wake_up_counter.saturating_sub(1);
        self.dev.bus().remote_wakeup(self.wake_up_counter != 0);
    }
}

This PR also includes a fix of setting LP_MODE bit when suspending. I noticed that setting both FSUSP and LP_MODE results in some weird behaviour where my application doesn't know that suspend happened. I am not sure what is causing this, but changing suspend() to first set FSUSP and then in separate memory access set LP_MODE fixes the issue. The reference manual describes the procedure:

  1. Set the FSUSP bit in the USB_CNTR register to 1. This action activates the suspend mode within the USB peripheral. As soon as the suspend mode is activated, the check on SOF reception is disabled to avoid any further SUSP interrupts being issued while
    the USB is suspended.
  2. Remove or reduce any static power consumption in blocks different from the USB peripheral.
  3. Set LP_MODE bit in USB_CNTR register to 1 to remove static power consumption in the analog USB transceivers but keeping them able to detect resume activity.
  4. Optionally turn off external oscillator and device PLL to stop any activity inside the device.

So another reason for not setting LP_MODE is that user application might want (and should) perform actions to reduce power consumption. For these reasons I added a separate method suspend_low_power_mode which can be used by the application to perform any additional steps.

Both methods make sure that we never set these bits when not in suspend state.

Setting LP_MODE instantly does not allow user application to
prepare for the low power mode, disable clocks, peripherals, etc.
Also, self-powered devices may not want to enter low-power mode
during host suspend.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Device Triggered Wakeup of the Host
1 participant