mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-29 01:48:23 +00:00
feat(core/bootloader): bootloader button power off logic
[no changelog]
This commit is contained in:
parent
0e6052dc25
commit
c1bfd5a8b3
@ -27,8 +27,6 @@
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "version.h"
|
||||
|
||||
#define BACKLIGHT_NORMAL 150
|
||||
|
||||
#define TOIF_LENGTH(ptr) ((*(uint32_t *)((ptr) + 8)) + 12)
|
||||
|
||||
// common shared functions
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include "rust_ui_bootloader.h"
|
||||
|
||||
#define BACKLIGHT_NORMAL 150
|
||||
|
||||
// Displays a warning screen before jumping to the untrusted firmware
|
||||
//
|
||||
// Shows vendor image, vendor string and firmware version
|
||||
|
@ -31,10 +31,18 @@
|
||||
#include "wire/wire_iface_usb.h"
|
||||
#include "workflow.h"
|
||||
|
||||
#ifdef USE_HAPTIC
|
||||
#include <io/haptic.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_BLE
|
||||
#include <wire/wire_iface_ble.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_BUTTON
|
||||
#include <io/button.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_POWER_MANAGER
|
||||
#include <io/display.h>
|
||||
#include <io/display_utils.h>
|
||||
@ -53,6 +61,10 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||
workflow_result_t result = WF_ERROR_FATAL;
|
||||
|
||||
#ifdef USE_POWER_MANAGER
|
||||
uint32_t button_deadline = 0;
|
||||
#ifdef USE_HAPTIC
|
||||
bool button_haptic_played = false;
|
||||
#endif
|
||||
uint32_t fade_deadline = ticks_timeout(FADE_TIME_MS);
|
||||
uint32_t suspend_deadline = ticks_timeout(SUSPEND_TIME_MS);
|
||||
bool faded = false;
|
||||
@ -86,6 +98,16 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||
sysevents_poll(&awaited, &signalled, ticks_timeout(100));
|
||||
|
||||
#ifdef USE_POWER_MANAGER
|
||||
|
||||
#ifdef USE_HAPTIC
|
||||
if (button_deadline != 0 && !button_haptic_played &&
|
||||
ticks_expired(button_deadline)) {
|
||||
// we reached hibernation time
|
||||
haptic_play(HAPTIC_BOOTLOADER_ENTRY);
|
||||
button_haptic_played = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (signalled.read_ready == 0) {
|
||||
pm_state_t pm_state = {0};
|
||||
|
||||
@ -106,7 +128,9 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||
|
||||
if (ticks_expired(suspend_deadline)) {
|
||||
pm_suspend(NULL);
|
||||
screen_render(wait_layout);
|
||||
display_fade(display_get_backlight(), fade_value, 200);
|
||||
button_deadline = 0;
|
||||
faded = false;
|
||||
fade_deadline = ticks_timeout(FADE_TIME_MS);
|
||||
suspend_deadline = ticks_timeout(SUSPEND_TIME_MS);
|
||||
@ -120,6 +144,44 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||
display_fade(display_get_backlight(), fade_value, 200);
|
||||
faded = false;
|
||||
}
|
||||
|
||||
// in case of battery powered device, power button is handled by eventloop
|
||||
if (signalled.read_ready & (1 << SYSHANDLE_BUTTON)) {
|
||||
button_event_t btn_event = {0};
|
||||
// todo this eats all button events, not only power button, so it needs to
|
||||
// be handled differently for button-based battery powered devices.
|
||||
if (button_get_event(&btn_event) && btn_event.button == BTN_POWER) {
|
||||
if (btn_event.event_type == BTN_EVENT_DOWN) {
|
||||
button_deadline = ticks_timeout(3000);
|
||||
#ifdef USE_HAPTIC
|
||||
button_haptic_played = false;
|
||||
#endif
|
||||
} else if (btn_event.event_type == BTN_EVENT_UP &&
|
||||
button_deadline != 0) {
|
||||
display_fade(display_get_backlight(), 0, 200);
|
||||
if (ticks_expired(button_deadline)) {
|
||||
// power button pressed for 3 seconds, we hibernate
|
||||
|
||||
#ifdef USE_HAPTIC
|
||||
if (!button_haptic_played) {
|
||||
haptic_play(HAPTIC_BOOTLOADER_ENTRY);
|
||||
button_haptic_played = true;
|
||||
}
|
||||
#endif
|
||||
pm_hibernate();
|
||||
} else {
|
||||
pm_suspend(NULL);
|
||||
button_deadline = 0;
|
||||
screen_render(wait_layout);
|
||||
display_fade(display_get_backlight(), BACKLIGHT_NORMAL, 200);
|
||||
faded = false;
|
||||
fade_deadline = ticks_timeout(FADE_TIME_MS);
|
||||
suspend_deadline = ticks_timeout(SUSPEND_TIME_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
if (signalled.read_ready == 0) {
|
||||
continue;
|
||||
|
@ -456,6 +456,7 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("pm_get_events")
|
||||
.allowlist_function("pm_get_state")
|
||||
.allowlist_function("pm_suspend")
|
||||
.allowlist_function("pm_hibernate")
|
||||
// irq
|
||||
.allowlist_function("irq_lock_fn")
|
||||
.allowlist_function("irq_unlock_fn")
|
||||
|
@ -11,6 +11,9 @@
|
||||
// common event function for screens that need UI + communication
|
||||
uint32_t screen_event(c_layout_t* layout, sysevents_t* signalled);
|
||||
|
||||
// common render event
|
||||
void screen_render(c_layout_t* layout);
|
||||
|
||||
// result screens
|
||||
void screen_wipe_success(void);
|
||||
void screen_wipe_fail(void);
|
||||
|
@ -4,6 +4,7 @@ use super::ffi;
|
||||
pub enum HapticEffect {
|
||||
ButtonPress = ffi::haptic_effect_t_HAPTIC_BUTTON_PRESS as _,
|
||||
HoldToConfirm = ffi::haptic_effect_t_HAPTIC_HOLD_TO_CONFIRM as _,
|
||||
BootloaderEntry = ffi::haptic_effect_t_HAPTIC_BOOTLOADER_ENTRY as _,
|
||||
}
|
||||
|
||||
pub fn play(effect: HapticEffect) {
|
||||
|
@ -51,3 +51,7 @@ pub fn is_usb_connected() -> bool {
|
||||
pub fn suspend() {
|
||||
unsafe { ffi::pm_suspend(null_mut()) };
|
||||
}
|
||||
|
||||
pub fn hibernate() {
|
||||
unsafe { ffi::pm_hibernate() };
|
||||
}
|
||||
|
@ -23,6 +23,15 @@ extern "C" fn screen_event(layout: *mut c_layout_t, signalled: &sysevents_t) ->
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_render(layout: *mut c_layout_t) {
|
||||
unsafe {
|
||||
let mut layout = LayoutBuffer::<<ModelUI as BootloaderUI>::CLayoutType>::new(layout);
|
||||
let layout = layout.get_mut();
|
||||
layout.render()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_welcome(layout: *mut c_layout_t) {
|
||||
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_welcome();
|
||||
|
@ -16,10 +16,14 @@ use crate::trezorhal::sysevent::{sysevents_poll, Syshandle};
|
||||
#[cfg(feature = "power_manager")]
|
||||
use crate::{
|
||||
time::Instant,
|
||||
trezorhal::power_manager::{is_usb_connected, suspend},
|
||||
trezorhal::power_manager::{hibernate, is_usb_connected, suspend},
|
||||
ui::display::fade_backlight_duration,
|
||||
ui::event::PhysicalButton,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "haptic", feature = "power_manager"))]
|
||||
use crate::trezorhal::haptic::{play, HapticEffect};
|
||||
|
||||
use heapless::Vec;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
@ -98,6 +102,10 @@ pub fn run(frame: &mut impl Component<Msg = impl ReturnToC>) -> u32 {
|
||||
render(frame);
|
||||
ModelUI::fadein();
|
||||
|
||||
#[cfg(all(feature = "power_manager", feature = "haptic"))]
|
||||
let mut haptic_played = false;
|
||||
#[cfg(feature = "power_manager")]
|
||||
let mut button_pressed_time = None;
|
||||
#[cfg(feature = "power_manager")]
|
||||
let mut start = Instant::now();
|
||||
let mut faded = false;
|
||||
@ -132,6 +140,39 @@ pub fn run(frame: &mut impl Component<Msg = impl ReturnToC>) -> u32 {
|
||||
#[cfg(feature = "power_manager")]
|
||||
{
|
||||
start = Instant::now();
|
||||
|
||||
if e == Event::Button(ButtonEvent::ButtonPressed(PhysicalButton::Power)) {
|
||||
button_pressed_time = Some(Instant::now());
|
||||
|
||||
#[cfg(feature = "haptic")]
|
||||
{
|
||||
haptic_played = false;
|
||||
}
|
||||
} else if e == Event::Button(ButtonEvent::ButtonReleased(PhysicalButton::Power)) {
|
||||
if let Some(t) = button_pressed_time {
|
||||
if let Some(elapsed) = Instant::now().checked_duration_since(t) {
|
||||
ModelUI::fadeout();
|
||||
if elapsed.to_secs() >= 3 {
|
||||
#[cfg(feature = "haptic")]
|
||||
{
|
||||
if !haptic_played {
|
||||
play(HapticEffect::BootloaderEntry);
|
||||
haptic_played = true;
|
||||
}
|
||||
}
|
||||
hibernate();
|
||||
} else {
|
||||
suspend();
|
||||
render(frame);
|
||||
ModelUI::fadein();
|
||||
|
||||
faded = false;
|
||||
button_pressed_time = None;
|
||||
start = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut ctx = EventCtx::new();
|
||||
@ -145,6 +186,18 @@ pub fn run(frame: &mut impl Component<Msg = impl ReturnToC>) -> u32 {
|
||||
} else {
|
||||
#[cfg(feature = "power_manager")]
|
||||
{
|
||||
#[cfg(feature = "haptic")]
|
||||
{
|
||||
if let Some(t) = button_pressed_time {
|
||||
if let Some(elapsed) = Instant::now().checked_duration_since(t) {
|
||||
if elapsed.to_secs() >= 3 && !haptic_played {
|
||||
play(HapticEffect::BootloaderEntry);
|
||||
haptic_played = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_usb_connected() {
|
||||
continue;
|
||||
}
|
||||
@ -164,6 +217,7 @@ pub fn run(frame: &mut impl Component<Msg = impl ReturnToC>) -> u32 {
|
||||
faded = false;
|
||||
}
|
||||
start = Instant::now();
|
||||
button_pressed_time = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use crate::ui::{
|
||||
component::{Event, Label},
|
||||
display::{self, toif::Toif, Color},
|
||||
geometry::{Alignment, Alignment2D, Offset, Point, Rect},
|
||||
layout::simplified::{process_frame_event, run, show},
|
||||
layout::simplified::{process_frame_event, render, run, show},
|
||||
shape::{self, render_on_display},
|
||||
ui_bootloader::{BootloaderLayoutType, BootloaderUI},
|
||||
CommonUI,
|
||||
@ -109,6 +109,18 @@ impl BootloaderLayoutType for BootloaderLayout {
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self) {
|
||||
match self {
|
||||
BootloaderLayout::Welcome(f) => render(f),
|
||||
BootloaderLayout::Menu(f) => render(f),
|
||||
BootloaderLayout::Connect(f) => render(f),
|
||||
#[cfg(feature = "ble")]
|
||||
BootloaderLayout::PairingMode(f) => render(f),
|
||||
#[cfg(feature = "ble")]
|
||||
BootloaderLayout::WirelessSetup(f) => render(f),
|
||||
}
|
||||
}
|
||||
|
||||
fn show(&mut self) {
|
||||
match self {
|
||||
BootloaderLayout::Welcome(f) => show(f, true),
|
||||
|
@ -2,6 +2,11 @@ use crate::ui::component::Event;
|
||||
|
||||
pub trait BootloaderLayoutType {
|
||||
fn event(&mut self, event: Option<Event>) -> u32;
|
||||
|
||||
fn render(&mut self) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn show(&mut self);
|
||||
fn init_welcome() -> Self;
|
||||
fn init_menu(initial_setup: bool) -> Self;
|
||||
|
Loading…
Reference in New Issue
Block a user