mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-06-17 21:48:47 +00:00
feat(core): add BLE to bootloader
This commit is contained in:
parent
8aaf68f39f
commit
37687f1815
@ -11,7 +11,7 @@ PRODUCTION = 0 if BOOTLOADER_QA else ARGUMENTS.get('PRODUCTION', '0') == '1'
|
|||||||
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
|
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
|
||||||
UI_DEBUG_OVERLAY = ARGUMENTS.get('UI_DEBUG_OVERLAY', '0') == '1'
|
UI_DEBUG_OVERLAY = ARGUMENTS.get('UI_DEBUG_OVERLAY', '0') == '1'
|
||||||
|
|
||||||
FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "dma2d"]
|
FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "dma2d", "ble"]
|
||||||
|
|
||||||
CCFLAGS_MOD = ''
|
CCFLAGS_MOD = ''
|
||||||
CPPPATH_MOD = []
|
CPPPATH_MOD = []
|
||||||
@ -105,7 +105,6 @@ SOURCE_BOOTLOADER = [
|
|||||||
'embed/projects/bootloader/header.S',
|
'embed/projects/bootloader/header.S',
|
||||||
'embed/projects/bootloader/bootui.c',
|
'embed/projects/bootloader/bootui.c',
|
||||||
'embed/projects/bootloader/main.c',
|
'embed/projects/bootloader/main.c',
|
||||||
'embed/projects/bootloader/poll.c',
|
|
||||||
'embed/projects/bootloader/antiglitch.c',
|
'embed/projects/bootloader/antiglitch.c',
|
||||||
'embed/projects/bootloader/workflow/wf_firmware_update.c',
|
'embed/projects/bootloader/workflow/wf_firmware_update.c',
|
||||||
'embed/projects/bootloader/workflow/wf_wipe_device.c',
|
'embed/projects/bootloader/workflow/wf_wipe_device.c',
|
||||||
@ -116,8 +115,10 @@ SOURCE_BOOTLOADER = [
|
|||||||
'embed/projects/bootloader/workflow/wf_empty_device.c',
|
'embed/projects/bootloader/workflow/wf_empty_device.c',
|
||||||
'embed/projects/bootloader/workflow/wf_auto_update.c',
|
'embed/projects/bootloader/workflow/wf_auto_update.c',
|
||||||
'embed/projects/bootloader/workflow/wf_host_control.c',
|
'embed/projects/bootloader/workflow/wf_host_control.c',
|
||||||
|
'embed/projects/bootloader/workflow/wf_ble_pairing_request.c',
|
||||||
'embed/projects/bootloader/wire/codec_v1.c',
|
'embed/projects/bootloader/wire/codec_v1.c',
|
||||||
'embed/projects/bootloader/wire/wire_iface_usb.c',
|
'embed/projects/bootloader/wire/wire_iface_usb.c',
|
||||||
|
'embed/projects/bootloader/wire/wire_iface_ble.c',
|
||||||
'embed/projects/bootloader/protob/protob.c',
|
'embed/projects/bootloader/protob/protob.c',
|
||||||
'embed/projects/bootloader/protob/pb/messages.pb.c',
|
'embed/projects/bootloader/protob/pb/messages.pb.c',
|
||||||
'embed/projects/bootloader/version_check.c',
|
'embed/projects/bootloader/version_check.c',
|
||||||
|
@ -99,7 +99,6 @@ SOURCE_NANOPB = [
|
|||||||
SOURCE_BOOTLOADER = [
|
SOURCE_BOOTLOADER = [
|
||||||
'embed/projects/bootloader/bootui.c',
|
'embed/projects/bootloader/bootui.c',
|
||||||
'embed/projects/bootloader/main.c',
|
'embed/projects/bootloader/main.c',
|
||||||
'embed/projects/bootloader/poll.c',
|
|
||||||
'embed/projects/bootloader/antiglitch.c',
|
'embed/projects/bootloader/antiglitch.c',
|
||||||
'embed/projects/bootloader/workflow/wf_firmware_update.c',
|
'embed/projects/bootloader/workflow/wf_firmware_update.c',
|
||||||
'embed/projects/bootloader/workflow/wf_wipe_device.c',
|
'embed/projects/bootloader/workflow/wf_wipe_device.c',
|
||||||
|
1
core/embed/projects/bootloader/.changelog.d/4833.added
Normal file
1
core/embed/projects/bootloader/.changelog.d/4833.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add cancel button to Connect to host screen.
|
@ -24,7 +24,7 @@
|
|||||||
#include <rtl/mini_printf.h>
|
#include <rtl/mini_printf.h>
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "rust_ui.h"
|
#include "rust_ui_bootloader.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#define BACKLIGHT_NORMAL 150
|
#define BACKLIGHT_NORMAL 150
|
||||||
@ -47,6 +47,8 @@ static bool initial_setup = true;
|
|||||||
|
|
||||||
void ui_set_initial_setup(bool initial) { initial_setup = initial; }
|
void ui_set_initial_setup(bool initial) { initial_setup = initial; }
|
||||||
|
|
||||||
|
bool ui_get_initial_setup(void) { return initial_setup; }
|
||||||
|
|
||||||
#if defined USE_TOUCH
|
#if defined USE_TOUCH
|
||||||
#include <io/touch.h>
|
#include <io/touch.h>
|
||||||
|
|
||||||
@ -97,10 +99,6 @@ void ui_screen_boot(const vendor_header *const vhdr,
|
|||||||
vimg_len, wait);
|
vimg_len, wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
// welcome UI
|
|
||||||
|
|
||||||
void ui_screen_welcome(void) { screen_welcome(); }
|
|
||||||
|
|
||||||
uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr, bool fw_ok) {
|
const image_header *const hdr, bool fw_ok) {
|
||||||
char bld_ver[32];
|
char bld_ver[32];
|
||||||
@ -111,19 +109,14 @@ uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
|||||||
return screen_intro(bld_ver, vhdr->vstr, vhdr->vstr_len, ver_str, fw_ok);
|
return screen_intro(bld_ver, vhdr->vstr, vhdr->vstr_len, ver_str, fw_ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ui_screen_menu(secbool firmware_present) {
|
|
||||||
return screen_menu(firmware_present);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui_screen_connect(void) { screen_connect(initial_setup); }
|
|
||||||
|
|
||||||
// install UI
|
// install UI
|
||||||
|
|
||||||
ui_result_t ui_screen_install_confirm(const vendor_header *const vhdr,
|
confirm_result_t ui_screen_install_confirm(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr,
|
const image_header *const hdr,
|
||||||
secbool should_keep_seed,
|
secbool should_keep_seed,
|
||||||
secbool is_newvendor,
|
secbool is_newvendor,
|
||||||
secbool is_newinstall, int version_cmp) {
|
secbool is_newinstall,
|
||||||
|
int version_cmp) {
|
||||||
uint8_t fingerprint[32];
|
uint8_t fingerprint[32];
|
||||||
char ver_str[64];
|
char ver_str[64];
|
||||||
get_image_fingerprint(hdr, fingerprint);
|
get_image_fingerprint(hdr, fingerprint);
|
||||||
@ -149,7 +142,7 @@ void ui_screen_install_progress_upload(int pos) {
|
|||||||
|
|
||||||
// wipe UI
|
// wipe UI
|
||||||
|
|
||||||
ui_result_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); }
|
confirm_result_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); }
|
||||||
|
|
||||||
void ui_screen_wipe(void) { screen_wipe_progress(0, true); }
|
void ui_screen_wipe(void) { screen_wipe_progress(0, true); }
|
||||||
|
|
||||||
@ -180,3 +173,9 @@ void ui_screen_install_restricted(void) { screen_install_fail(); }
|
|||||||
void ui_fadein(void) { display_fade(0, BACKLIGHT_NORMAL, 1000); }
|
void ui_fadein(void) { display_fade(0, BACKLIGHT_NORMAL, 1000); }
|
||||||
|
|
||||||
void ui_fadeout(void) { display_fade(BACKLIGHT_NORMAL, 0, 500); }
|
void ui_fadeout(void) { display_fade(BACKLIGHT_NORMAL, 0, 500); }
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
uint32_t ui_screen_confirm_pairing(uint32_t code) {
|
||||||
|
return screen_confirm_pairing(code, initial_setup);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -23,24 +23,7 @@
|
|||||||
|
|
||||||
#include <util/image.h>
|
#include <util/image.h>
|
||||||
|
|
||||||
// todo: use bindgen to tie this to rust
|
#include "rust_ui_bootloader.h"
|
||||||
typedef enum {
|
|
||||||
UI_RESULT_CANCEL = 1,
|
|
||||||
UI_RESULT_CONFIRM = 2,
|
|
||||||
} ui_result_t;
|
|
||||||
|
|
||||||
// todo: use bindgen to tie this to rust
|
|
||||||
typedef enum {
|
|
||||||
MENU_EXIT = 0xAABBCCDD,
|
|
||||||
MENU_REBOOT = 0x11223344,
|
|
||||||
MENU_WIPE = 0x55667788,
|
|
||||||
} menu_result_t;
|
|
||||||
|
|
||||||
// todo: use bindgen to tie this to rust
|
|
||||||
typedef enum {
|
|
||||||
INTRO_MENU = 1,
|
|
||||||
INTRO_HOST = 2,
|
|
||||||
} intro_result_t;
|
|
||||||
|
|
||||||
// Displays a warning screen before jumping to the untrusted firmware
|
// Displays a warning screen before jumping to the untrusted firmware
|
||||||
//
|
//
|
||||||
@ -60,25 +43,20 @@ void ui_screen_boot(const vendor_header* const vhdr,
|
|||||||
// the user presses a button, touches the display
|
// the user presses a button, touches the display
|
||||||
void ui_click(void);
|
void ui_click(void);
|
||||||
|
|
||||||
void ui_screen_welcome(void);
|
|
||||||
|
|
||||||
uint32_t ui_screen_intro(const vendor_header* const vhdr,
|
uint32_t ui_screen_intro(const vendor_header* const vhdr,
|
||||||
const image_header* const hdr, bool fw_ok);
|
const image_header* const hdr, bool fw_ok);
|
||||||
|
|
||||||
uint32_t ui_screen_menu(secbool firmware_present);
|
confirm_result_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
||||||
|
|
||||||
void ui_screen_connect(void);
|
|
||||||
|
|
||||||
ui_result_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
|
||||||
const image_header* const hdr,
|
const image_header* const hdr,
|
||||||
secbool shold_keep_seed,
|
secbool shold_keep_seed,
|
||||||
secbool is_newvendor,
|
secbool is_newvendor,
|
||||||
secbool is_newinstall, int version_cmp);
|
secbool is_newinstall,
|
||||||
|
int version_cmp);
|
||||||
void ui_screen_install_start();
|
void ui_screen_install_start();
|
||||||
void ui_screen_install_progress_erase(int pos, int len);
|
void ui_screen_install_progress_erase(int pos, int len);
|
||||||
void ui_screen_install_progress_upload(int pos);
|
void ui_screen_install_progress_upload(int pos);
|
||||||
|
|
||||||
ui_result_t ui_screen_wipe_confirm(void);
|
confirm_result_t ui_screen_wipe_confirm(void);
|
||||||
void ui_screen_wipe(void);
|
void ui_screen_wipe(void);
|
||||||
void ui_screen_wipe_progress(int pos, int len);
|
void ui_screen_wipe_progress(int pos, int len);
|
||||||
|
|
||||||
@ -89,9 +67,14 @@ void ui_screen_fail(void);
|
|||||||
void ui_fadein(void);
|
void ui_fadein(void);
|
||||||
void ui_fadeout(void);
|
void ui_fadeout(void);
|
||||||
void ui_set_initial_setup(bool initial);
|
void ui_set_initial_setup(bool initial);
|
||||||
|
bool ui_get_initial_setup(void);
|
||||||
|
|
||||||
void ui_screen_boot_stage_1(bool fading);
|
void ui_screen_boot_stage_1(bool fading);
|
||||||
|
|
||||||
#ifdef USE_OPTIGA
|
#ifdef USE_OPTIGA
|
||||||
uint32_t ui_screen_unlock_bootloader_confirm(void);
|
uint32_t ui_screen_unlock_bootloader_confirm(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
uint32_t ui_screen_confirm_pairing(uint32_t code);
|
||||||
|
#endif
|
||||||
|
@ -62,10 +62,18 @@
|
|||||||
#ifdef USE_TAMPER
|
#ifdef USE_TAMPER
|
||||||
#include <sys/tamper.h>
|
#include <sys/tamper.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLE
|
||||||
|
#include <io/ble.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
#include "wire/wire_iface_ble.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "antiglitch.h"
|
#include "antiglitch.h"
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "version_check.h"
|
#include "version_check.h"
|
||||||
|
#include "wire/wire_iface_usb.h"
|
||||||
#include "workflow/workflow.h"
|
#include "workflow/workflow.h"
|
||||||
|
|
||||||
#ifdef TREZOR_EMULATOR
|
#ifdef TREZOR_EMULATOR
|
||||||
@ -128,6 +136,9 @@ static void drivers_init(secbool *touch_initialized) {
|
|||||||
#ifdef USE_RGB_LED
|
#ifdef USE_RGB_LED
|
||||||
rgb_led_init();
|
rgb_led_init();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLE
|
||||||
|
ble_init();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drivers_deinit(void) {
|
static void drivers_deinit(void) {
|
||||||
@ -141,6 +152,9 @@ static void drivers_deinit(void) {
|
|||||||
#ifdef USE_RGB_LED
|
#ifdef USE_RGB_LED
|
||||||
rgb_led_deinit();
|
rgb_led_deinit();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLE
|
||||||
|
ble_deinit();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
display_deinit(DISPLAY_JUMP_BEHAVIOR);
|
display_deinit(DISPLAY_JUMP_BEHAVIOR);
|
||||||
}
|
}
|
||||||
@ -390,7 +404,7 @@ int bootloader_main(void) {
|
|||||||
|
|
||||||
jump_reset();
|
jump_reset();
|
||||||
if (header_present == sectrue) {
|
if (header_present == sectrue) {
|
||||||
if (auto_upgrade == sectrue) {
|
if (auto_upgrade == sectrue && firmware_present == sectrue) {
|
||||||
result = workflow_auto_update(&vhdr, hdr);
|
result = workflow_auto_update(&vhdr, hdr);
|
||||||
} else {
|
} else {
|
||||||
result = workflow_bootloader(&vhdr, hdr, firmware_present);
|
result = workflow_bootloader(&vhdr, hdr, firmware_present);
|
||||||
@ -399,6 +413,11 @@ int bootloader_main(void) {
|
|||||||
result = workflow_empty_device();
|
result = workflow_empty_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usb_iface_deinit();
|
||||||
|
#ifdef USE_BLE
|
||||||
|
ble_iface_deinit();
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case WF_OK_FIRMWARE_INSTALLED:
|
case WF_OK_FIRMWARE_INSTALLED:
|
||||||
firmware_present = sectrue;
|
firmware_present = sectrue;
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Trezor project, https://trezor.io/
|
|
||||||
*
|
|
||||||
* Copyright (c) SatoshiLabs
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <trezor_bsp.h>
|
|
||||||
|
|
||||||
#include "poll.h"
|
|
||||||
|
|
||||||
#include <io/usb.h>
|
|
||||||
#include <sys/systick.h>
|
|
||||||
|
|
||||||
#ifdef TREZOR_EMULATOR
|
|
||||||
#include "SDL.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num,
|
|
||||||
poll_event_t* event, uint32_t timeout_ms) {
|
|
||||||
uint32_t deadline = ticks_timeout(timeout_ms);
|
|
||||||
|
|
||||||
while (!ticks_expired(deadline)) {
|
|
||||||
#ifdef TREZOR_EMULATOR
|
|
||||||
// Ensures that SDL events are processed. This prevents the emulator from
|
|
||||||
// freezing when the user interacts with the window.
|
|
||||||
SDL_PumpEvents();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ifaces_num; i++) {
|
|
||||||
uint8_t iface_num = ifaces[i] & 0xFF;
|
|
||||||
if (iface_num < IFACE_USB_MAX) {
|
|
||||||
if ((ifaces[i] & MODE_READ) == MODE_READ) {
|
|
||||||
// check if USB can read
|
|
||||||
if (sectrue == usb_webusb_can_read(iface_num)) {
|
|
||||||
event->type = EVENT_USB_CAN_READ;
|
|
||||||
return iface_num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef TREZOR_EMULATOR
|
|
||||||
__WFI();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
event->type = EVENT_NONE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -26,6 +26,8 @@
|
|||||||
#define MAX_PACKET_SIZE 256
|
#define MAX_PACKET_SIZE 256
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
// initialized flag
|
||||||
|
bool initialized;
|
||||||
// identifier of the interface used for polling communication events
|
// identifier of the interface used for polling communication events
|
||||||
uint8_t poll_iface_id;
|
uint8_t poll_iface_id;
|
||||||
// size of TX packet
|
// size of TX packet
|
||||||
|
208
core/embed/projects/bootloader/wire/wire_iface_ble.c
Normal file
208
core/embed/projects/bootloader/wire/wire_iface_ble.c
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Trezor project, https://trezor.io/
|
||||||
|
*
|
||||||
|
* Copyright (c) SatoshiLabs
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifdef USE_BLE
|
||||||
|
|
||||||
|
#include <trezor_model.h>
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include "wire_iface_ble.h"
|
||||||
|
|
||||||
|
#include <io/ble.h>
|
||||||
|
#include <sys/sysevent.h>
|
||||||
|
#include <sys/systick.h>
|
||||||
|
|
||||||
|
static wire_iface_t g_ble_iface = {0};
|
||||||
|
|
||||||
|
static bool is_connected(void) {
|
||||||
|
ble_state_t state = {0};
|
||||||
|
ble_get_state(&state);
|
||||||
|
return state.connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ble_write_(uint8_t* data, size_t size) {
|
||||||
|
if (size != BLE_TX_PACKET_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t deadline = ticks_timeout(500);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (ticks_expired(deadline)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!is_connected()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ble_can_write()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ble_write(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ble_read_(uint8_t* buffer, size_t buffer_size) {
|
||||||
|
if (buffer_size != BLE_RX_PACKET_SIZE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t deadline = ticks_timeout(500);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (ticks_expired(deadline)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!is_connected()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ble_can_read()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = ble_read(buffer, buffer_size);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_error(void) {
|
||||||
|
error_shutdown_ex("BLE ERROR",
|
||||||
|
"Error reading from BLE. Try different BLE cable.", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
wire_iface_t* ble_iface_init(void) {
|
||||||
|
wire_iface_t* iface = &g_ble_iface;
|
||||||
|
|
||||||
|
if (iface->initialized) {
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(iface, 0, sizeof(wire_iface_t));
|
||||||
|
|
||||||
|
iface->poll_iface_id = SYSHANDLE_BLE_IFACE_0;
|
||||||
|
iface->tx_packet_size = BLE_TX_PACKET_SIZE;
|
||||||
|
iface->rx_packet_size = BLE_RX_PACKET_SIZE;
|
||||||
|
iface->write = &ble_write_;
|
||||||
|
iface->read = &ble_read_;
|
||||||
|
iface->error = &ble_error;
|
||||||
|
|
||||||
|
ble_start();
|
||||||
|
|
||||||
|
ble_state_t state = {0};
|
||||||
|
|
||||||
|
ble_get_state(&state);
|
||||||
|
|
||||||
|
if (!state.connectable && !state.pairing) {
|
||||||
|
if (state.peer_count > 0) {
|
||||||
|
ble_command_t cmd = {
|
||||||
|
.cmd_type = BLE_SWITCH_ON,
|
||||||
|
.data = {.adv_start =
|
||||||
|
{
|
||||||
|
.name = "Trezor Bootloader",
|
||||||
|
.static_mac = false,
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->initialized = true;
|
||||||
|
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_iface_deinit(void) {
|
||||||
|
wire_iface_t* iface = &g_ble_iface;
|
||||||
|
|
||||||
|
if (!iface->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_command_t cmd = {
|
||||||
|
.cmd_type = BLE_SWITCH_OFF,
|
||||||
|
};
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
|
||||||
|
ble_stop();
|
||||||
|
|
||||||
|
memset(iface, 0, sizeof(wire_iface_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_iface_end_pairing(void) {
|
||||||
|
ble_state_t state = {0};
|
||||||
|
|
||||||
|
ble_get_state(&state);
|
||||||
|
|
||||||
|
if (state.peer_count > 0) {
|
||||||
|
ble_command_t cmd = {.cmd_type = BLE_SWITCH_ON};
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
} else {
|
||||||
|
ble_command_t cmd = {.cmd_type = BLE_SWITCH_OFF};
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_iface_start_pairing(void) {
|
||||||
|
ble_state_t state = {0};
|
||||||
|
|
||||||
|
ble_get_state(&state);
|
||||||
|
|
||||||
|
uint16_t retry_cnt = 0;
|
||||||
|
|
||||||
|
while (state.connected && retry_cnt < 10) {
|
||||||
|
ble_command_t cmd_disconnect = {
|
||||||
|
.cmd_type = BLE_DISCONNECT,
|
||||||
|
};
|
||||||
|
ble_issue_command(&cmd_disconnect);
|
||||||
|
systick_delay_ms(20);
|
||||||
|
ble_get_state(&state);
|
||||||
|
retry_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_command_t cmd = {
|
||||||
|
.cmd_type = BLE_PAIRING_MODE,
|
||||||
|
.data = {.adv_start =
|
||||||
|
{
|
||||||
|
.name = "Trezor Bootloader",
|
||||||
|
.static_mac = false,
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
|
||||||
|
retry_cnt = 0;
|
||||||
|
ble_get_state(&state);
|
||||||
|
while (!state.pairing && retry_cnt < 10) {
|
||||||
|
systick_delay_ms(20);
|
||||||
|
ble_get_state(&state);
|
||||||
|
retry_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.pairing) {
|
||||||
|
ble_iface_end_pairing();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -19,21 +19,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <trezor_types.h>
|
#include "codec_v1.h"
|
||||||
|
|
||||||
#define IFACE_USB_MAX (15) // 0-15 reserved for USB
|
wire_iface_t* ble_iface_init(void);
|
||||||
|
|
||||||
#define MODE_READ 0x0000
|
void ble_iface_deinit(void);
|
||||||
#define MODE_WRITE 0x0100
|
|
||||||
|
|
||||||
typedef enum {
|
bool ble_iface_start_pairing(void);
|
||||||
EVENT_NONE = 0,
|
|
||||||
EVENT_USB_CAN_READ = 0x01,
|
|
||||||
} poll_event_type_t;
|
|
||||||
|
|
||||||
typedef struct {
|
void ble_iface_end_pairing(void);
|
||||||
poll_event_type_t type;
|
|
||||||
} poll_event_t;
|
|
||||||
|
|
||||||
uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num,
|
|
||||||
poll_event_t* event, uint32_t timeout_ms);
|
|
@ -23,19 +23,22 @@
|
|||||||
#include "wire_iface_usb.h"
|
#include "wire_iface_usb.h"
|
||||||
|
|
||||||
#include <io/usb.h>
|
#include <io/usb.h>
|
||||||
|
#include <sys/sysevent.h>
|
||||||
|
|
||||||
#define USB_TIMEOUT 500
|
#define USB_TIMEOUT 500
|
||||||
#define USB_PACKET_SIZE 64
|
#define USB_PACKET_SIZE 64
|
||||||
#define USB_IFACE_NUM 0
|
|
||||||
|
|
||||||
_Static_assert(USB_PACKET_SIZE <= MAX_PACKET_SIZE, "USB_PACKET_SIZE too large");
|
_Static_assert(USB_PACKET_SIZE <= MAX_PACKET_SIZE, "USB_PACKET_SIZE too large");
|
||||||
|
|
||||||
|
static wire_iface_t g_usb_iface = {0};
|
||||||
|
|
||||||
static bool usb_write(uint8_t* data, size_t size) {
|
static bool usb_write(uint8_t* data, size_t size) {
|
||||||
if (size != USB_PACKET_SIZE) {
|
if (size != USB_PACKET_SIZE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = usb_webusb_write_blocking(USB_IFACE_NUM, data, size, USB_TIMEOUT);
|
int r =
|
||||||
|
usb_webusb_write_blocking(SYSHANDLE_USB_IFACE_0, data, size, USB_TIMEOUT);
|
||||||
|
|
||||||
return r == size;
|
return r == size;
|
||||||
}
|
}
|
||||||
@ -45,8 +48,8 @@ static int usb_read(uint8_t* buffer, size_t buffer_size) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = usb_webusb_read_blocking(USB_IFACE_NUM, buffer, USB_PACKET_SIZE,
|
int r = usb_webusb_read_blocking(SYSHANDLE_USB_IFACE_0, buffer,
|
||||||
USB_TIMEOUT);
|
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -75,7 +78,7 @@ static void usb_init_all(secbool usb21_landing) {
|
|||||||
static uint8_t rx_buffer[USB_PACKET_SIZE];
|
static uint8_t rx_buffer[USB_PACKET_SIZE];
|
||||||
|
|
||||||
static const usb_webusb_info_t webusb_info = {
|
static const usb_webusb_info_t webusb_info = {
|
||||||
.iface_num = USB_IFACE_NUM,
|
.iface_num = SYSHANDLE_USB_IFACE_0,
|
||||||
#ifdef TREZOR_EMULATOR
|
#ifdef TREZOR_EMULATOR
|
||||||
.emu_port = 21324,
|
.emu_port = 21324,
|
||||||
#else
|
#else
|
||||||
@ -96,20 +99,35 @@ static void usb_init_all(secbool usb21_landing) {
|
|||||||
ensure(usb_start(), NULL);
|
ensure(usb_start(), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_iface_init(wire_iface_t* iface, secbool usb21_landing) {
|
wire_iface_t* usb_iface_init(secbool usb21_landing) {
|
||||||
|
wire_iface_t* iface = &g_usb_iface;
|
||||||
|
|
||||||
|
if (iface->initialized) {
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
usb_init_all(usb21_landing);
|
usb_init_all(usb21_landing);
|
||||||
|
|
||||||
memset(iface, 0, sizeof(wire_iface_t));
|
memset(iface, 0, sizeof(wire_iface_t));
|
||||||
|
|
||||||
iface->poll_iface_id = USB_IFACE_NUM;
|
iface->poll_iface_id = SYSHANDLE_USB_IFACE_0;
|
||||||
iface->tx_packet_size = USB_PACKET_SIZE;
|
iface->tx_packet_size = USB_PACKET_SIZE;
|
||||||
iface->rx_packet_size = USB_PACKET_SIZE;
|
iface->rx_packet_size = USB_PACKET_SIZE;
|
||||||
iface->write = &usb_write;
|
iface->write = &usb_write;
|
||||||
iface->read = &usb_read;
|
iface->read = &usb_read;
|
||||||
iface->error = &usb_error;
|
iface->error = &usb_error;
|
||||||
|
iface->initialized = true;
|
||||||
|
|
||||||
|
return iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_iface_deinit(wire_iface_t* iface) {
|
void usb_iface_deinit(void) {
|
||||||
|
wire_iface_t* iface = &g_usb_iface;
|
||||||
|
|
||||||
|
if (!iface->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memset(iface, 0, sizeof(wire_iface_t));
|
memset(iface, 0, sizeof(wire_iface_t));
|
||||||
usb_deinit();
|
usb_deinit();
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,6 @@
|
|||||||
|
|
||||||
#include "codec_v1.h"
|
#include "codec_v1.h"
|
||||||
|
|
||||||
void usb_iface_init(wire_iface_t* iface, secbool usb21_landing);
|
wire_iface_t* usb_iface_init(secbool usb21_landing);
|
||||||
|
|
||||||
void usb_iface_deinit(wire_iface_t* iface);
|
void usb_iface_deinit(void);
|
||||||
|
@ -17,12 +17,15 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <antiglitch.h>
|
||||||
|
#include <sys/bootargs.h>
|
||||||
#include <trezor_model.h>
|
#include <trezor_model.h>
|
||||||
#include <trezor_rtl.h>
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
#include <util/image.h>
|
#include <util/image.h>
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
|
#include "rust_ui_bootloader.h"
|
||||||
#include "workflow.h"
|
#include "workflow.h"
|
||||||
|
|
||||||
workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
||||||
@ -30,8 +33,19 @@ workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
|||||||
ui_set_initial_setup(true);
|
ui_set_initial_setup(true);
|
||||||
|
|
||||||
workflow_result_t res = WF_CANCELLED;
|
workflow_result_t res = WF_CANCELLED;
|
||||||
while (res == WF_CANCELLED) {
|
uint32_t ui_result = CONNECT_CANCEL;
|
||||||
res = workflow_host_control(vhdr, hdr, ui_screen_connect);
|
|
||||||
|
c_layout_t layout;
|
||||||
|
memset(&layout, 0, sizeof(layout));
|
||||||
|
screen_connect(true, false, &layout);
|
||||||
|
res = workflow_host_control(vhdr, hdr, &layout, &ui_result);
|
||||||
|
|
||||||
|
if (res == WF_OK_UI_ACTION && ui_result == CONNECT_CANCEL) {
|
||||||
|
bootargs_set(BOOT_COMMAND_NONE, NULL, 0);
|
||||||
|
jump_allow_1();
|
||||||
|
jump_allow_2();
|
||||||
|
return WF_OK_REBOOT_SELECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
118
core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
Normal file
118
core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Trezor project, https://trezor.io/
|
||||||
|
*
|
||||||
|
* Copyright (c) SatoshiLabs
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifdef USE_BLE
|
||||||
|
|
||||||
|
#include <trezor_model.h>
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <io/ble.h>
|
||||||
|
|
||||||
|
#include "bootui.h"
|
||||||
|
#include "rust_ui_bootloader.h"
|
||||||
|
#include "wire/wire_iface_ble.h"
|
||||||
|
#include "workflow.h"
|
||||||
|
|
||||||
|
static bool encode_pairing_code(uint32_t code, uint8_t *outbuf) {
|
||||||
|
if (code > 999999) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < BLE_PAIRING_CODE_LEN; i++) {
|
||||||
|
outbuf[BLE_PAIRING_CODE_LEN - i - 1] = '0' + (code % 10);
|
||||||
|
code /= 10;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
|
||||||
|
const image_header *const hdr) {
|
||||||
|
if (!ble_iface_start_pairing()) {
|
||||||
|
return WF_OK_PAIRING_FAILED;
|
||||||
|
}
|
||||||
|
c_layout_t layout;
|
||||||
|
memset(&layout, 0, sizeof(layout));
|
||||||
|
screen_pairing_mode(ui_get_initial_setup(), &layout);
|
||||||
|
|
||||||
|
uint32_t code = 0;
|
||||||
|
workflow_result_t res = workflow_host_control(vhdr, hdr, &layout, &code);
|
||||||
|
|
||||||
|
if (res != WF_OK_UI_ACTION) {
|
||||||
|
ble_iface_end_pairing();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == PAIRING_MODE_CANCEL) {
|
||||||
|
ble_iface_end_pairing();
|
||||||
|
return WF_OK_PAIRING_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t result = ui_screen_confirm_pairing(code);
|
||||||
|
|
||||||
|
uint8_t pairing_code[BLE_PAIRING_CODE_LEN] = {0};
|
||||||
|
|
||||||
|
if (result != CONFIRM || !encode_pairing_code(code, pairing_code)) {
|
||||||
|
ble_command_t cmd = {
|
||||||
|
.cmd_type = BLE_REJECT_PAIRING,
|
||||||
|
};
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
return WF_OK_PAIRING_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_command_t cmd = {
|
||||||
|
.cmd_type = BLE_ALLOW_PAIRING,
|
||||||
|
.data_len = sizeof(pairing_code),
|
||||||
|
};
|
||||||
|
memcpy(cmd.data.raw, pairing_code, sizeof(pairing_code));
|
||||||
|
ble_issue_command(&cmd);
|
||||||
|
|
||||||
|
bool skip_finalization = false;
|
||||||
|
|
||||||
|
sysevents_t awaited = {0};
|
||||||
|
sysevents_t signalled = {0};
|
||||||
|
awaited.read_ready |= 1 << SYSHANDLE_BLE;
|
||||||
|
|
||||||
|
sysevents_poll(&awaited, &signalled, 500);
|
||||||
|
|
||||||
|
if (signalled.read_ready == 1 << SYSHANDLE_BLE) {
|
||||||
|
ble_event_t event = {0};
|
||||||
|
if (ble_get_event(&event)) {
|
||||||
|
if (event.type == BLE_PAIRING_COMPLETED) {
|
||||||
|
skip_finalization = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skip_finalization) {
|
||||||
|
pairing_mode_finalization_result_t r =
|
||||||
|
screen_pairing_mode_finalizing(ui_get_initial_setup());
|
||||||
|
if (r == PAIRING_FINALIZATION_FAILED) {
|
||||||
|
ble_iface_end_pairing();
|
||||||
|
return WF_OK_PAIRING_FAILED;
|
||||||
|
}
|
||||||
|
if (r == PAIRING_FINALIZATION_CANCEL) {
|
||||||
|
ble_command_t disconnect = {.cmd_type = BLE_DISCONNECT};
|
||||||
|
ble_issue_command(&disconnect);
|
||||||
|
ble_iface_end_pairing();
|
||||||
|
return WF_OK_PAIRING_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WF_OK_PAIRING_COMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -25,74 +25,151 @@
|
|||||||
|
|
||||||
#include "antiglitch.h"
|
#include "antiglitch.h"
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
|
#include "rust_ui_bootloader.h"
|
||||||
#include "workflow.h"
|
#include "workflow.h"
|
||||||
|
|
||||||
typedef enum {
|
workflow_result_t workflow_menu(const vendor_header* const vhdr,
|
||||||
SCREEN_INTRO,
|
const image_header* const hdr,
|
||||||
SCREEN_MENU,
|
|
||||||
SCREEN_WAIT_FOR_HOST,
|
|
||||||
} screen_t;
|
|
||||||
|
|
||||||
workflow_result_t workflow_bootloader(const vendor_header *const vhdr,
|
|
||||||
const image_header *const hdr,
|
|
||||||
secbool firmware_present) {
|
secbool firmware_present) {
|
||||||
ui_set_initial_setup(false);
|
|
||||||
|
|
||||||
screen_t screen = SCREEN_INTRO;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (screen) {
|
c_layout_t layout;
|
||||||
case SCREEN_INTRO: {
|
memset(&layout, 0, sizeof(layout));
|
||||||
intro_result_t ui_result = ui_screen_intro(vhdr, hdr, firmware_present);
|
screen_menu(ui_get_initial_setup(), firmware_present, &layout);
|
||||||
if (ui_result == INTRO_MENU) {
|
uint32_t ui_result = 0;
|
||||||
screen = SCREEN_MENU;
|
workflow_result_t result =
|
||||||
|
workflow_host_control(vhdr, hdr, &layout, &ui_result);
|
||||||
|
|
||||||
|
if (result != WF_OK_UI_ACTION) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
if (ui_result == INTRO_HOST) {
|
|
||||||
screen = SCREEN_WAIT_FOR_HOST;
|
menu_result_t menu_result = (menu_result_t)ui_result;
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case SCREEN_MENU: {
|
|
||||||
menu_result_t menu_result = ui_screen_menu(firmware_present);
|
|
||||||
if (menu_result == MENU_EXIT) { // exit menu
|
if (menu_result == MENU_EXIT) { // exit menu
|
||||||
screen = SCREEN_INTRO;
|
return WF_OK;
|
||||||
|
}
|
||||||
|
#ifdef USE_BLE
|
||||||
|
if (menu_result == MENU_BLUETOOTH) {
|
||||||
|
workflow_ble_pairing_request(vhdr, hdr);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (menu_result == MENU_REBOOT) { // reboot
|
|
||||||
#ifndef USE_HASH_PROCESSOR
|
|
||||||
ui_screen_boot_stage_1(true);
|
|
||||||
#endif
|
#endif
|
||||||
|
if (menu_result == MENU_REBOOT) { // reboot
|
||||||
jump_allow_1();
|
jump_allow_1();
|
||||||
jump_allow_2();
|
jump_allow_2();
|
||||||
return WF_OK_REBOOT_SELECTED;
|
return WF_OK_REBOOT_SELECTED;
|
||||||
}
|
}
|
||||||
if (menu_result == MENU_WIPE) { // wipe
|
if (menu_result == MENU_WIPE) { // wipe
|
||||||
workflow_result_t r = workflow_wipe_device(NULL);
|
workflow_result_t r = workflow_wipe_device(NULL);
|
||||||
if (r == WF_ERROR) {
|
if (r == WF_ERROR || r == WF_OK_DEVICE_WIPED || r == WF_CANCELLED) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
if (r == WF_OK_DEVICE_WIPED) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
if (r == WF_CANCELLED) {
|
|
||||||
screen = SCREEN_MENU;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
return WF_ERROR_FATAL;
|
return WF_ERROR_FATAL;
|
||||||
}
|
}
|
||||||
} break;
|
}
|
||||||
case SCREEN_WAIT_FOR_HOST: {
|
|
||||||
workflow_result_t res =
|
typedef enum {
|
||||||
workflow_host_control(vhdr, hdr, ui_screen_connect);
|
SCREEN_INTRO,
|
||||||
switch (res) {
|
SCREEN_MENU,
|
||||||
case WF_CANCELLED:
|
SCREEN_WAIT_FOR_HOST,
|
||||||
screen = SCREEN_INTRO;
|
SCREEN_DONE,
|
||||||
continue;
|
} screen_t;
|
||||||
default:
|
|
||||||
return res;
|
// Each handler returns either a next screen, or SCREEN_DONE and an out‑param
|
||||||
}
|
// for the result.
|
||||||
} break;
|
static screen_t handle_intro(const vendor_header* vhdr, const image_header* hdr,
|
||||||
default:
|
secbool firmware_present,
|
||||||
return WF_ERROR_FATAL;
|
workflow_result_t* out_result) {
|
||||||
break;
|
intro_result_t ui = ui_screen_intro(vhdr, hdr, firmware_present);
|
||||||
}
|
if (ui == INTRO_MENU) return SCREEN_MENU;
|
||||||
}
|
if (ui == INTRO_HOST) return SCREEN_WAIT_FOR_HOST;
|
||||||
|
// no other valid INTRO result -> fatal
|
||||||
|
*out_result = WF_ERROR_FATAL;
|
||||||
|
return SCREEN_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static screen_t handle_menu(const vendor_header* vhdr, const image_header* hdr,
|
||||||
|
secbool firmware_present,
|
||||||
|
workflow_result_t* out_result) {
|
||||||
|
workflow_result_t res = workflow_menu(vhdr, hdr, firmware_present);
|
||||||
|
switch (res) {
|
||||||
|
case WF_OK:
|
||||||
|
return SCREEN_INTRO; // back to intro
|
||||||
|
case WF_CANCELLED:
|
||||||
|
return SCREEN_MENU; // re‑show menu
|
||||||
|
default:
|
||||||
|
*out_result = res; // final result
|
||||||
|
return SCREEN_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static screen_t handle_wait_for_host(const vendor_header* vhdr,
|
||||||
|
const image_header* hdr,
|
||||||
|
secbool firmware_present,
|
||||||
|
workflow_result_t* out_result) {
|
||||||
|
c_layout_t layout;
|
||||||
|
memset(&layout, 0, sizeof(layout));
|
||||||
|
uint32_t ui_res = 0;
|
||||||
|
|
||||||
|
screen_connect(false, true, &layout);
|
||||||
|
workflow_result_t res = workflow_host_control(vhdr, hdr, &layout, &ui_res);
|
||||||
|
|
||||||
|
switch (res) {
|
||||||
|
case WF_OK_UI_ACTION: {
|
||||||
|
switch (ui_res) {
|
||||||
|
case CONNECT_CANCEL:
|
||||||
|
return SCREEN_INTRO;
|
||||||
|
#ifdef USE_BLE
|
||||||
|
case CONNECT_PAIRING_MODE: {
|
||||||
|
workflow_result_t ble = workflow_ble_pairing_request(vhdr, hdr);
|
||||||
|
if (ble == WF_OK_PAIRING_COMPLETED || ble == WF_OK_PAIRING_FAILED)
|
||||||
|
return SCREEN_WAIT_FOR_HOST;
|
||||||
|
if (ble == WF_CANCELLED) return SCREEN_INTRO;
|
||||||
|
*out_result = ble;
|
||||||
|
return SCREEN_DONE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
case CONNECT_MENU:
|
||||||
|
return SCREEN_MENU;
|
||||||
|
default:
|
||||||
|
*out_result = WF_ERROR_FATAL;
|
||||||
|
return SCREEN_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case WF_CANCELLED: {
|
||||||
|
return SCREEN_INTRO;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
*out_result = res;
|
||||||
|
return SCREEN_DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workflow_result_t workflow_bootloader(const vendor_header* vhdr,
|
||||||
|
const image_header* hdr,
|
||||||
|
secbool firmware_present) {
|
||||||
|
ui_set_initial_setup(false);
|
||||||
|
screen_t screen = SCREEN_INTRO;
|
||||||
|
workflow_result_t final_res = WF_ERROR_FATAL;
|
||||||
|
|
||||||
|
while (screen != SCREEN_DONE) {
|
||||||
|
switch (screen) {
|
||||||
|
case SCREEN_INTRO:
|
||||||
|
screen = handle_intro(vhdr, hdr, firmware_present, &final_res);
|
||||||
|
break;
|
||||||
|
case SCREEN_MENU:
|
||||||
|
screen = handle_menu(vhdr, hdr, firmware_present, &final_res);
|
||||||
|
break;
|
||||||
|
case SCREEN_WAIT_FOR_HOST:
|
||||||
|
screen = handle_wait_for_host(vhdr, hdr, firmware_present, &final_res);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// shouldn’t happen
|
||||||
|
return WF_ERROR_FATAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return final_res;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
|
#include "rust_ui_bootloader.h"
|
||||||
#include "workflow.h"
|
#include "workflow.h"
|
||||||
|
|
||||||
workflow_result_t workflow_empty_device(void) {
|
workflow_result_t workflow_empty_device(void) {
|
||||||
@ -50,8 +51,35 @@ workflow_result_t workflow_empty_device(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
workflow_result_t res = WF_CANCELLED;
|
workflow_result_t res = WF_CANCELLED;
|
||||||
while (res == WF_CANCELLED) {
|
uint32_t ui_result = WELCOME_CANCEL;
|
||||||
res = workflow_host_control(NULL, NULL, ui_screen_welcome);
|
while (res == WF_CANCELLED ||
|
||||||
|
(res == WF_OK_UI_ACTION && ui_result == WELCOME_CANCEL)) {
|
||||||
|
c_layout_t layout;
|
||||||
|
memset(&layout, 0, sizeof(layout));
|
||||||
|
screen_welcome(&layout);
|
||||||
|
res = workflow_host_control(NULL, NULL, &layout, &ui_result);
|
||||||
|
#ifdef USE_BLE
|
||||||
|
if (res == WF_OK_UI_ACTION && ui_result == WELCOME_PAIRING_MODE) {
|
||||||
|
res = workflow_ble_pairing_request(NULL, NULL);
|
||||||
|
if (res == WF_OK_PAIRING_COMPLETED || res == WF_OK_PAIRING_FAILED) {
|
||||||
|
res = WF_CANCELLED;
|
||||||
|
ui_result = WELCOME_CANCEL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (res == WF_OK_UI_ACTION && ui_result == WELCOME_MENU) {
|
||||||
|
do {
|
||||||
|
res = workflow_menu(NULL, NULL, false);
|
||||||
|
} while (res == WF_CANCELLED);
|
||||||
|
|
||||||
|
if (res == WF_OK) {
|
||||||
|
res = WF_CANCELLED;
|
||||||
|
ui_result = WELCOME_CANCEL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,6 @@
|
|||||||
#include <sec/secret.h>
|
#include <sec/secret.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <poll.h>
|
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "protob/protob.h"
|
#include "protob/protob.h"
|
||||||
#include "version_check.h"
|
#include "version_check.h"
|
||||||
@ -311,12 +309,12 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ui_result_t response = UI_RESULT_CANCEL;
|
confirm_result_t response = CANCEL;
|
||||||
if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) &&
|
if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) &&
|
||||||
(sectrue == is_new || sectrue == is_ilu)) {
|
(sectrue == is_new || sectrue == is_ilu)) {
|
||||||
// new installation or interaction less updated - auto confirm
|
// new installation or interaction less updated - auto confirm
|
||||||
// only allowed for full-trust images
|
// only allowed for full-trust images
|
||||||
response = UI_RESULT_CONFIRM;
|
response = CONFIRM;
|
||||||
} else {
|
} else {
|
||||||
if (sectrue != is_new) {
|
if (sectrue != is_new) {
|
||||||
int version_cmp = version_compare(hdr.version, current_hdr->version);
|
int version_cmp = version_compare(hdr.version, current_hdr->version);
|
||||||
@ -328,7 +326,7 @@ static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UI_RESULT_CONFIRM != response) {
|
if (CONFIRM != response) {
|
||||||
send_user_abort(iface, "Firmware install cancelled");
|
send_user_abort(iface, "Firmware install cancelled");
|
||||||
return UPLOAD_ERR_USER_ABORT;
|
return UPLOAD_ERR_USER_ABORT;
|
||||||
}
|
}
|
||||||
@ -519,11 +517,14 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
|
|||||||
upload_status_t s = UPLOAD_IN_PROGRESS;
|
upload_status_t s = UPLOAD_IN_PROGRESS;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ};
|
sysevents_t awaited = {0};
|
||||||
poll_event_t e = {0};
|
sysevents_t signalled = {0};
|
||||||
uint8_t i = poll_events(ifaces, 1, &e, 100);
|
|
||||||
|
|
||||||
if (e.type == EVENT_NONE || i != protob_get_iface_flag(iface)) {
|
awaited.read_ready = 1 << protob_get_iface_flag(iface);
|
||||||
|
|
||||||
|
sysevents_poll(&awaited, &signalled, 100);
|
||||||
|
|
||||||
|
if (awaited.read_ready != signalled.read_ready) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,56 +17,99 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <bootui.h>
|
||||||
|
#include <rust_ui_bootloader.h>
|
||||||
#include <trezor_model.h>
|
#include <trezor_model.h>
|
||||||
#include <trezor_rtl.h>
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <sys/sysevent.h>
|
||||||
#include <sys/systick.h>
|
#include <sys/systick.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <util/image.h>
|
#include <util/image.h>
|
||||||
|
|
||||||
#include "antiglitch.h"
|
#include "antiglitch.h"
|
||||||
#include "poll.h"
|
|
||||||
#include "protob/protob.h"
|
#include "protob/protob.h"
|
||||||
#include "wire/wire_iface_usb.h"
|
#include "wire/wire_iface_usb.h"
|
||||||
#include "workflow.h"
|
#include "workflow.h"
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
#include <wire/wire_iface_ble.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr,
|
const image_header *const hdr,
|
||||||
void (*redraw_wait_screen)(void)) {
|
c_layout_t *wait_layout,
|
||||||
wire_iface_t usb_iface = {0};
|
uint32_t *ui_action_result) {
|
||||||
protob_io_t protob_usb_iface = {0};
|
protob_io_t protob_usb_iface = {0};
|
||||||
|
|
||||||
redraw_wait_screen();
|
|
||||||
|
|
||||||
// if both are NULL, we don't have a firmware installed
|
// if both are NULL, we don't have a firmware installed
|
||||||
// let's show a webusb landing page in this case
|
// let's show a webusb landing page in this case
|
||||||
usb_iface_init(&usb_iface,
|
wire_iface_t *usb_iface =
|
||||||
(vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
|
usb_iface_init((vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
|
||||||
|
|
||||||
protob_init(&protob_usb_iface, &usb_iface);
|
protob_init(&protob_usb_iface, usb_iface);
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
wire_iface_t *ble_iface = ble_iface_init();
|
||||||
|
protob_io_t protob_ble_iface = {0};
|
||||||
|
|
||||||
|
protob_init(&protob_ble_iface, ble_iface);
|
||||||
|
#endif
|
||||||
|
|
||||||
workflow_result_t result = WF_ERROR_FATAL;
|
workflow_result_t result = WF_ERROR_FATAL;
|
||||||
|
|
||||||
for (;;) {
|
sysevents_t awaited = {0};
|
||||||
uint16_t ifaces[1] = {protob_get_iface_flag(&protob_usb_iface) | MODE_READ};
|
|
||||||
poll_event_t e = {0};
|
|
||||||
|
|
||||||
uint8_t i = poll_events(ifaces, 1, &e, 100);
|
awaited.read_ready |= 1 << protob_get_iface_flag(&protob_usb_iface);
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
awaited.read_ready |= 1 << protob_get_iface_flag(&protob_ble_iface);
|
||||||
|
awaited.read_ready |= 1 << SYSHANDLE_BLE;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BUTTON
|
||||||
|
awaited.read_ready |= 1 << SYSHANDLE_BUTTON;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TOUCH
|
||||||
|
awaited.read_ready |= 1 << SYSHANDLE_TOUCH;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
sysevents_t signalled = {0};
|
||||||
|
|
||||||
|
sysevents_poll(&awaited, &signalled, 100);
|
||||||
|
|
||||||
|
if (signalled.read_ready == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t msg_id = 0;
|
uint16_t msg_id = 0;
|
||||||
protob_io_t *active_iface = NULL;
|
protob_io_t *active_iface = NULL;
|
||||||
|
|
||||||
switch (e.type) {
|
if (signalled.read_ready ==
|
||||||
case EVENT_USB_CAN_READ:
|
(1 << protob_get_iface_flag(&protob_usb_iface)) &&
|
||||||
if (i == protob_get_iface_flag(&protob_usb_iface) &&
|
|
||||||
sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) {
|
sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) {
|
||||||
active_iface = &protob_usb_iface;
|
active_iface = &protob_usb_iface;
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case EVENT_NONE:
|
#ifdef USE_BLE
|
||||||
default:
|
if (signalled.read_ready ==
|
||||||
|
(1 << protob_get_iface_flag(&protob_ble_iface)) &&
|
||||||
|
sectrue == protob_get_msg_header(&protob_ble_iface, &msg_id)) {
|
||||||
|
active_iface = &protob_ble_iface;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// no data, lets pass the event signal to UI
|
||||||
|
if (active_iface == NULL) {
|
||||||
|
uint32_t res = screen_event(wait_layout, &signalled);
|
||||||
|
|
||||||
|
if (res != 0) {
|
||||||
|
if (ui_action_result != NULL) {
|
||||||
|
*ui_action_result = res;
|
||||||
|
}
|
||||||
|
result = WF_OK_UI_ACTION;
|
||||||
|
goto exit_host_control;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +152,9 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
|||||||
|
|
||||||
exit_host_control:
|
exit_host_control:
|
||||||
systick_delay_ms(100);
|
systick_delay_ms(100);
|
||||||
usb_iface_deinit(&usb_iface);
|
usb_iface_deinit();
|
||||||
|
#ifdef USE_BLE
|
||||||
|
ble_iface_deinit();
|
||||||
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@
|
|||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "protob.h"
|
#include "protob.h"
|
||||||
#include "rust_ui.h"
|
#include "rust_ui_bootloader.h"
|
||||||
#include "workflow.h"
|
#include "workflow.h"
|
||||||
|
|
||||||
workflow_result_t workflow_unlock_bootloader(protob_io_t *iface) {
|
workflow_result_t workflow_unlock_bootloader(protob_io_t *iface) {
|
||||||
ui_result_t response = ui_screen_unlock_bootloader_confirm();
|
confirm_result_t response = ui_screen_unlock_bootloader_confirm();
|
||||||
if (UI_RESULT_CONFIRM != response) {
|
if (CONFIRM != response) {
|
||||||
send_user_abort(iface, "Bootloader unlock cancelled");
|
send_user_abort(iface, "Bootloader unlock cancelled");
|
||||||
return WF_CANCELLED;
|
return WF_CANCELLED;
|
||||||
}
|
}
|
||||||
|
@ -22,18 +22,69 @@
|
|||||||
|
|
||||||
#include <util/flash_utils.h>
|
#include <util/flash_utils.h>
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
#include <io/ble.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/systick.h>
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "protob.h"
|
#include "protob.h"
|
||||||
#include "rust_ui.h"
|
#include "rust_ui_bootloader.h"
|
||||||
#include "workflow.h"
|
#include "workflow.h"
|
||||||
|
|
||||||
workflow_result_t workflow_wipe_device(protob_io_t *iface) {
|
static void send_error_conditionally(protob_io_t* iface, char* msg) {
|
||||||
|
if (iface != NULL) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Could not read BLE status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
static bool wipe_bonds(protob_io_t* iface) {
|
||||||
|
ble_state_t state = {0};
|
||||||
|
ble_get_state(&state);
|
||||||
|
|
||||||
|
if (!state.state_known) {
|
||||||
|
send_error_conditionally(iface, "Could not read BLE status");
|
||||||
|
screen_wipe_fail();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_command_t ble_command = {0};
|
||||||
|
ble_command.cmd_type = BLE_ERASE_BONDS;
|
||||||
|
if (!ble_issue_command(&ble_command)) {
|
||||||
|
send_error_conditionally(iface, "Could not issue BLE command");
|
||||||
|
screen_wipe_fail();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t deadline = ticks_timeout(100);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ble_get_state(&state);
|
||||||
|
if (state.peer_count == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ticks_expired(deadline)) {
|
||||||
|
send_error_conditionally(iface, "Could not erase bonds");
|
||||||
|
screen_wipe_fail();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
workflow_result_t workflow_wipe_device(protob_io_t* iface) {
|
||||||
WipeDevice msg_recv;
|
WipeDevice msg_recv;
|
||||||
if (iface != NULL) {
|
if (iface != NULL) {
|
||||||
recv_msg_wipe_device(iface, &msg_recv);
|
recv_msg_wipe_device(iface, &msg_recv);
|
||||||
}
|
}
|
||||||
ui_result_t response = ui_screen_wipe_confirm();
|
|
||||||
if (UI_RESULT_CONFIRM != response) {
|
confirm_result_t response = ui_screen_wipe_confirm();
|
||||||
|
if (CONFIRM != response) {
|
||||||
if (iface != NULL) {
|
if (iface != NULL) {
|
||||||
send_user_abort(iface, "Wipe cancelled");
|
send_user_abort(iface, "Wipe cancelled");
|
||||||
}
|
}
|
||||||
@ -42,11 +93,14 @@ workflow_result_t workflow_wipe_device(protob_io_t *iface) {
|
|||||||
ui_screen_wipe();
|
ui_screen_wipe();
|
||||||
secbool wipe_result = erase_device(ui_screen_wipe_progress);
|
secbool wipe_result = erase_device(ui_screen_wipe_progress);
|
||||||
|
|
||||||
if (sectrue != wipe_result) {
|
#ifdef USE_BLE
|
||||||
if (iface != NULL) {
|
if (!wipe_bonds(iface)) {
|
||||||
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
return WF_ERROR;
|
||||||
"Could not erase flash");
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (sectrue != wipe_result) {
|
||||||
|
send_error_conditionally(iface, "Could not erase flash");
|
||||||
screen_wipe_fail();
|
screen_wipe_fail();
|
||||||
return WF_ERROR;
|
return WF_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,11 @@
|
|||||||
|
|
||||||
#include <trezor_types.h>
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
#include <sys/sysevent.h>
|
||||||
#include <util/image.h>
|
#include <util/image.h>
|
||||||
|
|
||||||
#include "protob/protob.h"
|
#include "protob/protob.h"
|
||||||
|
#include "rust_ui_bootloader.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WF_ERROR_FATAL = 0,
|
WF_ERROR_FATAL = 0,
|
||||||
@ -33,6 +35,9 @@ typedef enum {
|
|||||||
WF_OK_FIRMWARE_INSTALLED = 0x04D9D07F,
|
WF_OK_FIRMWARE_INSTALLED = 0x04D9D07F,
|
||||||
WF_OK_DEVICE_WIPED = 0x30DC3841,
|
WF_OK_DEVICE_WIPED = 0x30DC3841,
|
||||||
WF_OK_BOOTLOADER_UNLOCKED = 0x23FCBD03,
|
WF_OK_BOOTLOADER_UNLOCKED = 0x23FCBD03,
|
||||||
|
WF_OK_UI_ACTION = 0xAABBCCEE,
|
||||||
|
WF_OK_PAIRING_COMPLETED = 0xAABBCCEF,
|
||||||
|
WF_OK_PAIRING_FAILED = 0xAABBCCF0,
|
||||||
WF_CANCELLED = 0x55667788,
|
WF_CANCELLED = 0x55667788,
|
||||||
} workflow_result_t;
|
} workflow_result_t;
|
||||||
|
|
||||||
@ -54,6 +59,9 @@ workflow_result_t workflow_get_features(protob_io_t *iface,
|
|||||||
const vendor_header *const vhdr,
|
const vendor_header *const vhdr,
|
||||||
const image_header *const hdr);
|
const image_header *const hdr);
|
||||||
|
|
||||||
|
workflow_result_t workflow_menu(const vendor_header *const vhdr,
|
||||||
|
const image_header *const hdr,
|
||||||
|
secbool firmware_present);
|
||||||
workflow_result_t workflow_bootloader(const vendor_header *const vhdr,
|
workflow_result_t workflow_bootloader(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr,
|
const image_header *const hdr,
|
||||||
secbool firmware_present);
|
secbool firmware_present);
|
||||||
@ -62,7 +70,13 @@ workflow_result_t workflow_empty_device(void);
|
|||||||
|
|
||||||
workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr,
|
const image_header *const hdr,
|
||||||
void (*redraw_wait_screen)(void));
|
c_layout_t *wait_layout,
|
||||||
|
uint32_t *ui_action_result);
|
||||||
|
|
||||||
workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr);
|
const image_header *const hdr);
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
|
||||||
|
const image_header *const hdr);
|
||||||
|
#endif
|
||||||
|
@ -44,6 +44,7 @@ const DEFAULT_BINDGEN_MACROS_COMMON: &[&str] = &[
|
|||||||
"-I../io/usb/inc",
|
"-I../io/usb/inc",
|
||||||
"-I../sec/entropy/inc",
|
"-I../sec/entropy/inc",
|
||||||
"-I../sys/time/inc",
|
"-I../sys/time/inc",
|
||||||
|
"-I../sys/task/inc",
|
||||||
"-I../util/flash/inc",
|
"-I../util/flash/inc",
|
||||||
"-I../util/translations/inc",
|
"-I../util/translations/inc",
|
||||||
"-I../models",
|
"-I../models",
|
||||||
@ -407,16 +408,19 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_var("BLE_TX_PACKET_SIZE")
|
.allowlist_var("BLE_TX_PACKET_SIZE")
|
||||||
.allowlist_var("BLE_ADV_NAME_LEN")
|
.allowlist_var("BLE_ADV_NAME_LEN")
|
||||||
.allowlist_function("ble_get_state")
|
.allowlist_function("ble_get_state")
|
||||||
|
.allowlist_function("ble_get_event")
|
||||||
.allowlist_function("ble_issue_command")
|
.allowlist_function("ble_issue_command")
|
||||||
.allowlist_function("ble_start")
|
.allowlist_function("ble_start")
|
||||||
.allowlist_function("ble_write")
|
.allowlist_function("ble_write")
|
||||||
.allowlist_function("ble_read")
|
.allowlist_function("ble_read")
|
||||||
.allowlist_type("ble_command_t")
|
.allowlist_type("ble_command_t")
|
||||||
.allowlist_type("ble_state_t")
|
.allowlist_type("ble_state_t")
|
||||||
|
.allowlist_type("ble_event_t")
|
||||||
// touch
|
// touch
|
||||||
.allowlist_function("touch_get_event")
|
.allowlist_function("touch_get_event")
|
||||||
// button
|
// button
|
||||||
.allowlist_type("button_t")
|
.allowlist_type("button_t")
|
||||||
|
.allowlist_type("button_event_t")
|
||||||
.allowlist_function("button_get_event")
|
.allowlist_function("button_get_event")
|
||||||
// haptic
|
// haptic
|
||||||
.allowlist_type("haptic_effect_t")
|
.allowlist_type("haptic_effect_t")
|
||||||
@ -434,7 +438,13 @@ fn generate_trezorhal_bindings() {
|
|||||||
.allowlist_function("jpegdec_process")
|
.allowlist_function("jpegdec_process")
|
||||||
.allowlist_function("jpegdec_get_info")
|
.allowlist_function("jpegdec_get_info")
|
||||||
.allowlist_function("jpegdec_get_slice_rgba8888")
|
.allowlist_function("jpegdec_get_slice_rgba8888")
|
||||||
.allowlist_function("jpegdec_get_slice_mono8");
|
.allowlist_function("jpegdec_get_slice_mono8")
|
||||||
|
// sysevent
|
||||||
|
.allowlist_type("syshandle_t")
|
||||||
|
.allowlist_type("sysevents_t")
|
||||||
|
.allowlist_function("sysevents_poll")
|
||||||
|
// c_layout
|
||||||
|
.allowlist_type("c_layout_t");
|
||||||
|
|
||||||
// Write the bindings to a file in the OUR_DIR.
|
// Write the bindings to a file in the OUR_DIR.
|
||||||
bindings
|
bindings
|
||||||
|
6
core/embed/rust/rust_types.h
Normal file
6
core/embed/rust/rust_types.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// additional type for C-rust interface
|
||||||
|
typedef struct {
|
||||||
|
uint8_t buf[1024];
|
||||||
|
} c_layout_t;
|
@ -1,2 +0,0 @@
|
|||||||
#include "rust_ui_bootloader.h"
|
|
||||||
#include "rust_ui_common.h"
|
|
@ -1,29 +1,91 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <trezor_types.h>
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
#include <sys/sysevent.h>
|
||||||
|
|
||||||
|
#include "rust_types.h"
|
||||||
|
|
||||||
|
// todo: use bindgen to bind return values to rust
|
||||||
|
|
||||||
|
// common event function for screens that need UI + communication
|
||||||
|
uint32_t screen_event(c_layout_t* layout, sysevents_t* signalled);
|
||||||
|
|
||||||
|
// result screens
|
||||||
|
void screen_wipe_success(void);
|
||||||
|
void screen_wipe_fail(void);
|
||||||
|
uint32_t screen_install_success(uint8_t restart_seconds, bool initial_setup,
|
||||||
|
bool complete_draw);
|
||||||
|
uint32_t screen_install_fail(void);
|
||||||
|
void screen_unlock_bootloader_success(void);
|
||||||
|
|
||||||
|
// progress screens
|
||||||
|
void screen_install_progress(int16_t progress, bool initialize,
|
||||||
|
bool initial_setup);
|
||||||
|
void screen_wipe_progress(int16_t progress, bool initialize);
|
||||||
|
|
||||||
|
// simple screens with no interaction
|
||||||
|
|
||||||
|
void screen_boot_stage_1(bool fading);
|
||||||
|
void screen_boot(bool warning, const char* vendor_str, size_t vendor_str_len,
|
||||||
|
uint32_t version, const void* vendor_img,
|
||||||
|
size_t vendor_img_len, int wait);
|
||||||
|
|
||||||
|
// confirm screens
|
||||||
|
typedef enum {
|
||||||
|
CANCEL = 1,
|
||||||
|
CONFIRM = 2,
|
||||||
|
} confirm_result_t;
|
||||||
uint32_t screen_install_confirm(const char* vendor_str, uint8_t vendor_str_len,
|
uint32_t screen_install_confirm(const char* vendor_str, uint8_t vendor_str_len,
|
||||||
const char* version_str,
|
const char* version_str,
|
||||||
const uint8_t* fingerprint,
|
const uint8_t* fingerprint,
|
||||||
bool should_keep_seed, bool is_newvendor,
|
bool should_keep_seed, bool is_newvendor,
|
||||||
bool is_newinstall, int version_cmp);
|
bool is_newinstall, int version_cmp);
|
||||||
uint32_t screen_wipe_confirm(void);
|
uint32_t screen_wipe_confirm(void);
|
||||||
void screen_install_progress(int16_t progress, bool initialize,
|
uint32_t screen_confirm_pairing(uint32_t code, bool initial_setup);
|
||||||
bool initial_setup);
|
uint32_t screen_unlock_bootloader_confirm(void);
|
||||||
void screen_wipe_progress(int16_t progress, bool initialize);
|
|
||||||
|
// screens with UI but no communication
|
||||||
|
typedef enum {
|
||||||
|
INTRO_MENU = 1,
|
||||||
|
INTRO_HOST = 2,
|
||||||
|
} intro_result_t;
|
||||||
uint32_t screen_intro(const char* bld_version_str, const char* vendor_str,
|
uint32_t screen_intro(const char* bld_version_str, const char* vendor_str,
|
||||||
uint8_t vendor_str_len, const char* version_str,
|
uint8_t vendor_str_len, const char* version_str,
|
||||||
bool fw_ok);
|
bool fw_ok);
|
||||||
uint32_t screen_menu(secbool firmware_present);
|
|
||||||
void screen_connect(bool initial_setup);
|
typedef enum {
|
||||||
void screen_wipe_success(void);
|
PAIRING_FINALIZATION_COMPLETED = 1,
|
||||||
void screen_wipe_fail(void);
|
PAIRING_FINALIZATION_CANCEL = 2,
|
||||||
uint32_t screen_install_success(uint8_t restart_seconds, bool initial_setup,
|
PAIRING_FINALIZATION_FAILED = 3,
|
||||||
bool complete_draw);
|
} pairing_mode_finalization_result_t;
|
||||||
uint32_t screen_install_fail(void);
|
uint32_t screen_pairing_mode_finalizing(bool initial_setup);
|
||||||
void screen_welcome(void);
|
|
||||||
void screen_boot_stage_1(bool fading);
|
// screens with UI and communication interactions
|
||||||
uint32_t screen_unlock_bootloader_confirm(void);
|
typedef enum {
|
||||||
void screen_unlock_bootloader_success(void);
|
MENU_EXIT = 0xAABBCCDD,
|
||||||
void bld_continue_label(uint16_t bg_color);
|
MENU_REBOOT = 0x11223344,
|
||||||
void screen_boot(bool warning, const char* vendor_str, size_t vendor_str_len,
|
MENU_WIPE = 0x55667788,
|
||||||
uint32_t version, const void* vendor_img,
|
MENU_BLUETOOTH = 0x99AABBCC,
|
||||||
size_t vendor_img_len, int wait);
|
} menu_result_t;
|
||||||
|
void screen_menu(bool initial_setup, secbool firmware_present,
|
||||||
|
c_layout_t* layout);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CONNECT_CANCEL = 1,
|
||||||
|
CONNECT_PAIRING_MODE = 2,
|
||||||
|
CONNECT_MENU = 3,
|
||||||
|
} connect_result_t;
|
||||||
|
void screen_connect(bool initial_setup, bool auto_update, c_layout_t* layout);
|
||||||
|
typedef enum {
|
||||||
|
WELCOME_CANCEL = 1,
|
||||||
|
WELCOME_PAIRING_MODE = 2,
|
||||||
|
WELCOME_MENU = 3,
|
||||||
|
} welcome_result_t;
|
||||||
|
void screen_welcome(c_layout_t* layout);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// 0 - 999999 - pairing code
|
||||||
|
PAIRING_MODE_CANCEL = 1000000,
|
||||||
|
} pairing_mode_result_t;
|
||||||
|
void screen_pairing_mode(bool initial_setup, c_layout_t* layout);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::*;
|
use super::{super::model, *};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
micropython::{
|
micropython::{
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
mod micropython;
|
mod micropython;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::{error::Error, ui::event::BLEEvent};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
|
|
||||||
use super::{ffi, model};
|
use super::ffi;
|
||||||
|
|
||||||
pub const ADV_NAME_LEN: usize = ffi::BLE_ADV_NAME_LEN as usize;
|
pub const ADV_NAME_LEN: usize = ffi::BLE_ADV_NAME_LEN as usize;
|
||||||
pub const PAIRING_CODE_LEN: usize = ffi::BLE_PAIRING_CODE_LEN as usize;
|
pub const PAIRING_CODE_LEN: usize = ffi::BLE_PAIRING_CODE_LEN as usize;
|
||||||
@ -23,6 +23,25 @@ fn prefix_utf8_bytes(text: &str, max_len: usize) -> &[u8] {
|
|||||||
&text.as_bytes()[..i]
|
&text.as_bytes()[..i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ble_parse_event(event: ffi::ble_event_t) -> BLEEvent {
|
||||||
|
match event.type_ {
|
||||||
|
ffi::ble_event_type_t_BLE_CONNECTED => BLEEvent::Connected,
|
||||||
|
ffi::ble_event_type_t_BLE_DISCONNECTED => BLEEvent::Disconnected,
|
||||||
|
ffi::ble_event_type_t_BLE_PAIRING_REQUEST => {
|
||||||
|
let code: u32 = event
|
||||||
|
.data
|
||||||
|
.iter()
|
||||||
|
.take(6)
|
||||||
|
.map(|&b| (b - b'0'))
|
||||||
|
.fold(0, |acc, d| acc * 10 + d as u32);
|
||||||
|
BLEEvent::PairingRequest(code)
|
||||||
|
}
|
||||||
|
ffi::ble_event_type_t_BLE_PAIRING_CANCELLED => BLEEvent::PairingCanceled,
|
||||||
|
ffi::ble_event_type_t_BLE_PAIRING_COMPLETED => BLEEvent::PairingCompleted,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn state() -> ffi::ble_state_t {
|
fn state() -> ffi::ble_state_t {
|
||||||
let mut state = ffi::ble_state_t {
|
let mut state = ffi::ble_state_t {
|
||||||
connected: false,
|
connected: false,
|
||||||
|
@ -17,6 +17,13 @@ pub enum PhysicalButtonEvent {
|
|||||||
Up = ffi::button_event_type_t_BTN_EVENT_UP as _,
|
Up = ffi::button_event_type_t_BTN_EVENT_UP as _,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn button_parse_event(event: ffi::button_event_t) -> (PhysicalButton, PhysicalButtonEvent) {
|
||||||
|
(
|
||||||
|
unwrap!(PhysicalButton::from_u8(event.button as _)),
|
||||||
|
unwrap!(PhysicalButtonEvent::from_u8(event.event_type as _)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn button_get_event() -> Option<(PhysicalButton, PhysicalButtonEvent)> {
|
pub fn button_get_event() -> Option<(PhysicalButton, PhysicalButtonEvent)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut e = ffi::button_event_t {
|
let mut e = ffi::button_event_t {
|
||||||
@ -24,10 +31,7 @@ pub fn button_get_event() -> Option<(PhysicalButton, PhysicalButtonEvent)> {
|
|||||||
button: ffi::button_t_BTN_LEFT,
|
button: ffi::button_t_BTN_LEFT,
|
||||||
};
|
};
|
||||||
if ffi::button_get_event(&mut e as _) {
|
if ffi::button_get_event(&mut e as _) {
|
||||||
Some((
|
Some(button_parse_event(e))
|
||||||
unwrap!(PhysicalButton::from_u8(e.button as _)),
|
|
||||||
unwrap!(PhysicalButtonEvent::from_u8(e.event_type as _)),
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
60
core/embed/rust/src/trezorhal/layout_buf.rs
Normal file
60
core/embed/rust/src/trezorhal/layout_buf.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
|
||||||
|
|
||||||
|
pub use super::ffi::c_layout_t;
|
||||||
|
|
||||||
|
pub struct LayoutBuffer<T> {
|
||||||
|
layout: NonNull<MaybeUninit<T>>,
|
||||||
|
_marker: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> LayoutBuffer<T> {
|
||||||
|
/// Create a new `LayoutBuffer` from an underlying buffer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Caller must ensure that the buffer can be held mutably for the lifetime
|
||||||
|
/// of the `LayoutBuffer`.
|
||||||
|
pub unsafe fn new(buffer: *mut c_layout_t) -> Self {
|
||||||
|
let layout = unwrap!(NonNull::new(buffer));
|
||||||
|
// SAFETY: c_layout_t is just an array of u8 which is always aligned
|
||||||
|
let buffer = unsafe { &mut (*layout.as_ptr()).buf };
|
||||||
|
|
||||||
|
// SAFETY: buffer is just an array of u8 which we are slicing.
|
||||||
|
let (_, aligned, _) = unsafe { buffer.align_to_mut::<MaybeUninit<T>>() };
|
||||||
|
|
||||||
|
let slot = aligned.first_mut();
|
||||||
|
|
||||||
|
let slot = unwrap!(slot, "Cannot align buffer or buffer is too small");
|
||||||
|
|
||||||
|
// SAFETY: we ensured that slot is non-null and valid
|
||||||
|
let layout = unsafe { NonNull::new_unchecked(slot as *mut _) };
|
||||||
|
Self {
|
||||||
|
layout,
|
||||||
|
_marker: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_maybe_uninit(&mut self) -> &mut MaybeUninit<T> {
|
||||||
|
// SAFETY: we ensured in `new()` that `ptr` is valid for the lifetime
|
||||||
|
// of the buffer and properly aligned.
|
||||||
|
unsafe { &mut *self.layout.as_ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store a layout in the buffer.
|
||||||
|
pub fn store(&mut self, value: T) {
|
||||||
|
self.get_maybe_uninit().write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the stored layout.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Caller is responsible for ensuring that the buffer contains a valid
|
||||||
|
/// layout -- that is, that `store()` has previously been called on the
|
||||||
|
/// underlying pointer.
|
||||||
|
pub unsafe fn get_mut(&mut self) -> &mut T {
|
||||||
|
let layout = self.get_maybe_uninit();
|
||||||
|
// SAFETY: safe under the safety assumptions of this function
|
||||||
|
unsafe { layout.assume_init_mut() }
|
||||||
|
}
|
||||||
|
}
|
@ -35,3 +35,9 @@ pub mod wordlist;
|
|||||||
pub mod secbool;
|
pub mod secbool;
|
||||||
|
|
||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
|
#[cfg(feature = "ui")]
|
||||||
|
pub mod sysevent;
|
||||||
|
|
||||||
|
#[cfg(feature = "bootloader")]
|
||||||
|
pub mod layout_buf;
|
||||||
|
136
core/embed/rust/src/trezorhal/sysevent.rs
Normal file
136
core/embed/rust/src/trezorhal/sysevent.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
|
use super::ffi;
|
||||||
|
use core::mem::{self, MaybeUninit};
|
||||||
|
|
||||||
|
use crate::ui::component::Event;
|
||||||
|
pub use ffi::{sysevents_t, syshandle_t};
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
use crate::trezorhal::ble::ble_parse_event;
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
use crate::ui::event::BLEEvent;
|
||||||
|
|
||||||
|
#[cfg(feature = "touch")]
|
||||||
|
use crate::trezorhal::touch::touch_get_event;
|
||||||
|
#[cfg(feature = "touch")]
|
||||||
|
use crate::ui::event::TouchEvent;
|
||||||
|
|
||||||
|
#[cfg(feature = "button")]
|
||||||
|
use crate::trezorhal::button::button_parse_event;
|
||||||
|
#[cfg(feature = "button")]
|
||||||
|
use crate::trezorhal::ffi::button_get_event;
|
||||||
|
#[cfg(feature = "button")]
|
||||||
|
use crate::ui::event::ButtonEvent;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Eq, Clone, Copy)]
|
||||||
|
pub enum Syshandle {
|
||||||
|
Button = ffi::syshandle_t_SYSHANDLE_BUTTON as _,
|
||||||
|
Touch = ffi::syshandle_t_SYSHANDLE_TOUCH as _,
|
||||||
|
Ble = ffi::syshandle_t_SYSHANDLE_BLE as _,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Syshandle {
|
||||||
|
pub fn set_in(self, mask: &mut ffi::syshandle_mask_t) {
|
||||||
|
*mask |= 1 << self as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_set_in(self, mask: &ffi::syshandle_mask_t) -> bool {
|
||||||
|
mask & (1 << self as u32) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
impl ffi::ble_event_t {
|
||||||
|
pub fn get() -> Option<Self> {
|
||||||
|
let mut ble_event = MaybeUninit::zeroed();
|
||||||
|
unsafe {
|
||||||
|
let event_available = ffi::ble_get_event(ble_event.as_mut_ptr());
|
||||||
|
// SAFETY: We only assume_init after the C call returns a success.
|
||||||
|
event_available.then_some(ble_event.assume_init())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "button")]
|
||||||
|
impl ffi::button_event_t {
|
||||||
|
pub fn get() -> Option<Self> {
|
||||||
|
let mut button_event = MaybeUninit::zeroed();
|
||||||
|
unsafe {
|
||||||
|
let event_available = ffi::button_get_event(button_event.as_mut_ptr());
|
||||||
|
// SAFETY: We only assume_init after the C call returns a success.
|
||||||
|
event_available.then_some(button_event.assume_init())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Sysevents = ffi::sysevents_t;
|
||||||
|
|
||||||
|
impl Sysevents {
|
||||||
|
pub fn zeroed() -> Self {
|
||||||
|
Self {
|
||||||
|
read_ready: 0,
|
||||||
|
write_ready: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reading_from(ifaces: &[Syshandle]) -> Self {
|
||||||
|
let mut awaited = Self::zeroed();
|
||||||
|
for iface in ifaces {
|
||||||
|
iface.set_in(&mut awaited.read_ready);
|
||||||
|
}
|
||||||
|
awaited
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writing_to(ifaces: &[Syshandle]) -> Self {
|
||||||
|
let mut awaited = Self::zeroed();
|
||||||
|
for iface in ifaces {
|
||||||
|
iface.set_in(&mut awaited.write_ready);
|
||||||
|
}
|
||||||
|
awaited
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_event(signalled: &sysevents_t) -> Option<Event> {
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
if Syshandle::Ble.is_set_in(&signalled.read_ready) {
|
||||||
|
if let Some(ble_event) = ffi::ble_event_t::get() {
|
||||||
|
let ble_event = ble_parse_event(ble_event);
|
||||||
|
return Some(Event::BLE(ble_event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "button")]
|
||||||
|
if Syshandle::Button.is_set_in(&signalled.read_ready) {
|
||||||
|
if let Some(button_event) = ffi::button_event_t::get() {
|
||||||
|
let (btn, evt) = button_parse_event(button_event);
|
||||||
|
return Some(Event::Button(unwrap!(ButtonEvent::new(evt, btn))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "touch")]
|
||||||
|
if Syshandle::Touch.is_set_in(&signalled.read_ready) {
|
||||||
|
let touch_event = touch_get_event();
|
||||||
|
|
||||||
|
if touch_event != 0 {
|
||||||
|
let (event_type, ex, ey) = {
|
||||||
|
let event_type = touch_event >> 24;
|
||||||
|
|
||||||
|
let ex = (touch_event >> 12) & 0xFFF;
|
||||||
|
let ey = touch_event & 0xFFF;
|
||||||
|
(event_type, ex, ey)
|
||||||
|
};
|
||||||
|
return Some(Event::Touch(unwrap!(TouchEvent::new(event_type, ex, ey))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sysevents_poll(ifaces: &[Syshandle]) -> Option<Event> {
|
||||||
|
let awaited = Sysevents::reading_from(ifaces);
|
||||||
|
let mut signalled = Sysevents::zeroed();
|
||||||
|
|
||||||
|
// SAFETY: safe.
|
||||||
|
unsafe { ffi::sysevents_poll(&awaited as _, &mut signalled as _, 100) };
|
||||||
|
|
||||||
|
parse_event(&signalled)
|
||||||
|
}
|
@ -1,16 +1,36 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::hexlify,
|
strutil::hexlify,
|
||||||
trezorhal::secbool::secbool,
|
trezorhal::{
|
||||||
|
layout_buf::{c_layout_t, LayoutBuffer},
|
||||||
|
secbool::secbool,
|
||||||
|
sysevent::{parse_event, sysevents_t},
|
||||||
|
},
|
||||||
ui::{
|
ui::{
|
||||||
ui_bootloader::BootloaderUI,
|
ui_bootloader::{BootloaderLayoutType as _, BootloaderUI},
|
||||||
util::{from_c_array, from_c_str},
|
util::{from_c_array, from_c_str},
|
||||||
ModelUI,
|
ModelUI,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn screen_welcome() {
|
extern "C" fn screen_event(layout: *mut c_layout_t, signalled: &sysevents_t) -> u32 {
|
||||||
ModelUI::screen_welcome();
|
let e = parse_event(signalled);
|
||||||
|
// SAFETY: calling code is supposed to give us exclusive access to an already
|
||||||
|
// initialized layout
|
||||||
|
unsafe {
|
||||||
|
let mut layout = LayoutBuffer::<<ModelUI as BootloaderUI>::CLayoutType>::new(layout);
|
||||||
|
let layout = layout.get_mut();
|
||||||
|
layout.event(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn screen_welcome(layout: *mut c_layout_t) {
|
||||||
|
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_welcome();
|
||||||
|
screen.show();
|
||||||
|
// SAFETY: calling code is supposed to give us exclusive access to the layout
|
||||||
|
let mut layout = unsafe { LayoutBuffer::new(layout) };
|
||||||
|
layout.store(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -75,8 +95,13 @@ extern "C" fn screen_unlock_bootloader_success() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
extern "C" fn screen_menu(initial_setup: bool, firmware_present: secbool, layout: *mut c_layout_t) {
|
||||||
ModelUI::screen_menu(firmware_present)
|
let mut screen =
|
||||||
|
<ModelUI as BootloaderUI>::CLayoutType::init_menu(initial_setup, firmware_present);
|
||||||
|
screen.show();
|
||||||
|
// SAFETY: calling code is supposed to give us exclusive access to the layout
|
||||||
|
let mut layout = unsafe { LayoutBuffer::new(layout) };
|
||||||
|
layout.store(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -131,8 +156,13 @@ extern "C" fn screen_install_progress(progress: u16, initialize: bool, initial_s
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" fn screen_connect(initial_setup: bool) {
|
extern "C" fn screen_connect(initial_setup: bool, auto_update: bool, layout: *mut c_layout_t) {
|
||||||
ModelUI::screen_connect(initial_setup)
|
let mut screen =
|
||||||
|
<ModelUI as BootloaderUI>::CLayoutType::init_connect(initial_setup, auto_update);
|
||||||
|
screen.show();
|
||||||
|
// SAFETY: calling code is supposed to give us exclusive access to the layout
|
||||||
|
let mut layout = unsafe { LayoutBuffer::new(layout) };
|
||||||
|
layout.store(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -144,3 +174,25 @@ extern "C" fn screen_wipe_success() {
|
|||||||
extern "C" fn screen_wipe_fail() {
|
extern "C" fn screen_wipe_fail() {
|
||||||
ModelUI::screen_wipe_fail()
|
ModelUI::screen_wipe_fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn screen_confirm_pairing(code: u32, initial_setup: bool) -> u32 {
|
||||||
|
ModelUI::screen_confirm_pairing(code, initial_setup)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn screen_pairing_mode(initial_setup: bool, layout: *mut c_layout_t) {
|
||||||
|
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_pairing_mode(initial_setup);
|
||||||
|
screen.show();
|
||||||
|
// SAFETY: calling code is supposed to give us exclusive access to the layout
|
||||||
|
let mut layout = unsafe { LayoutBuffer::new(layout) };
|
||||||
|
layout.store(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn screen_pairing_mode_finalizing(initial_setup: bool) -> u32 {
|
||||||
|
ModelUI::screen_pairing_mode_finalizing(initial_setup)
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ pub mod obj;
|
|||||||
|
|
||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
pub mod result;
|
pub mod result;
|
||||||
|
|
||||||
pub mod simplified;
|
pub mod simplified;
|
||||||
|
|
||||||
#[cfg(feature = "micropython")]
|
#[cfg(feature = "micropython")]
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
#[cfg(feature = "button")]
|
#[cfg(feature = "button")]
|
||||||
use crate::trezorhal::button::button_get_event;
|
use crate::trezorhal::button::button_get_event;
|
||||||
#[cfg(feature = "touch")]
|
|
||||||
use crate::trezorhal::touch::touch_get_event;
|
|
||||||
#[cfg(feature = "button")]
|
#[cfg(feature = "button")]
|
||||||
use crate::ui::event::ButtonEvent;
|
use crate::ui::event::ButtonEvent;
|
||||||
#[cfg(feature = "touch")]
|
#[cfg(feature = "touch")]
|
||||||
use crate::ui::event::TouchEvent;
|
use crate::ui::event::TouchEvent;
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{Component, Event, EventCtx, Never},
|
component::{Component, EventCtx, Never},
|
||||||
display, CommonUI, ModelUI,
|
display, CommonUI, ModelUI,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::ui::{component::Event, display::color::Color, shape::render_on_display};
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
use crate::ui::{display::color::Color, shape::render_on_display};
|
use crate::trezorhal::sysevent::{sysevents_poll, Syshandle};
|
||||||
|
use heapless::Vec;
|
||||||
|
|
||||||
pub trait ReturnToC {
|
pub trait ReturnToC {
|
||||||
fn return_to_c(self) -> u32;
|
fn return_to_c(self) -> u32;
|
||||||
@ -33,6 +33,7 @@ where
|
|||||||
self.to_u32().unwrap()
|
self.to_u32().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "button")]
|
#[cfg(feature = "button")]
|
||||||
fn button_eval() -> Option<ButtonEvent> {
|
fn button_eval() -> Option<ButtonEvent> {
|
||||||
let event = button_get_event();
|
let event = button_get_event();
|
||||||
@ -57,7 +58,7 @@ pub fn touch_unpack(event: u32) -> Option<TouchEvent> {
|
|||||||
TouchEvent::new(event_type, ex as _, ey as _).ok()
|
TouchEvent::new(event_type, ex as _, ey as _).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(frame: &mut impl Component) {
|
pub(crate) fn render(frame: &mut impl Component) {
|
||||||
display::sync();
|
display::sync();
|
||||||
render_on_display(None, Some(Color::black()), |target| {
|
render_on_display(None, Some(Color::black()), |target| {
|
||||||
frame.render(target);
|
frame.render(target);
|
||||||
@ -65,26 +66,52 @@ fn render(frame: &mut impl Component) {
|
|||||||
display::refresh();
|
display::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_frame_event<A>(frame: &mut A, event: Option<Event>) -> u32
|
||||||
|
where
|
||||||
|
A: Component,
|
||||||
|
A::Msg: ReturnToC,
|
||||||
|
{
|
||||||
|
if let Some(event) = event {
|
||||||
|
let mut ctx = EventCtx::new();
|
||||||
|
let msg = frame.event(&mut ctx, event);
|
||||||
|
if let Some(message) = msg {
|
||||||
|
return message.return_to_c();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(frame);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(frame: &mut impl Component<Msg = impl ReturnToC>) -> u32 {
|
pub fn run(frame: &mut impl Component<Msg = impl ReturnToC>) -> u32 {
|
||||||
frame.place(ModelUI::SCREEN);
|
frame.place(ModelUI::SCREEN);
|
||||||
ModelUI::fadeout();
|
ModelUI::fadeout();
|
||||||
render(frame);
|
render(frame);
|
||||||
ModelUI::fadein();
|
ModelUI::fadein();
|
||||||
|
|
||||||
|
// flush any pending events
|
||||||
#[cfg(feature = "button")]
|
#[cfg(feature = "button")]
|
||||||
while button_eval().is_some() {}
|
while button_eval().is_some() {}
|
||||||
|
|
||||||
loop {
|
let mut ifaces: Vec<Syshandle, 16> = Vec::new();
|
||||||
#[cfg(all(feature = "button", not(feature = "touch")))]
|
|
||||||
let event = button_eval();
|
#[cfg(feature = "ble")]
|
||||||
|
unwrap!(ifaces.push(Syshandle::Ble));
|
||||||
|
|
||||||
|
#[cfg(feature = "button")]
|
||||||
|
unwrap!(ifaces.push(Syshandle::Button));
|
||||||
|
|
||||||
#[cfg(feature = "touch")]
|
#[cfg(feature = "touch")]
|
||||||
let event = touch_unpack(touch_get_event());
|
unwrap!(ifaces.push(Syshandle::Touch));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let event = sysevents_poll(ifaces.as_slice());
|
||||||
|
|
||||||
if let Some(e) = event {
|
if let Some(e) = event {
|
||||||
let mut ctx = EventCtx::new();
|
let mut ctx = EventCtx::new();
|
||||||
#[cfg(all(feature = "button", not(feature = "touch")))]
|
|
||||||
let msg = frame.event(&mut ctx, Event::Button(e));
|
let msg = frame.event(&mut ctx, e);
|
||||||
#[cfg(feature = "touch")]
|
|
||||||
let msg = frame.event(&mut ctx, Event::Touch(e));
|
|
||||||
|
|
||||||
if let Some(message) = msg {
|
if let Some(message) = msg {
|
||||||
return message.return_to_c();
|
return message.return_to_c();
|
||||||
|
132
core/embed/rust/src/ui/layout_bolt/bootloader/connect.rs
Normal file
132
core/embed/rust/src/ui/layout_bolt/bootloader/connect.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
display::{Color, Font, Icon},
|
||||||
|
geometry::{Alignment, Insets, Offset, Point, Rect},
|
||||||
|
layout_bolt::{
|
||||||
|
component::{Button, ButtonMsg},
|
||||||
|
constant::WIDTH,
|
||||||
|
theme::bootloader::{
|
||||||
|
button_bld, button_bld_menu, button_initial, BLD_BG, BUTTON_AREA_START,
|
||||||
|
BUTTON_HEIGHT, CONTENT_PADDING, CORNER_BUTTON_AREA, MENU32, WELCOME_COLOR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape::{self, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
|
pub enum ConnectMsg {
|
||||||
|
Cancel = 1,
|
||||||
|
PairingMode = 2,
|
||||||
|
Menu = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connect {
|
||||||
|
fg: Color,
|
||||||
|
bg: Pad,
|
||||||
|
message: TString<'static>,
|
||||||
|
font: Font,
|
||||||
|
button: Button,
|
||||||
|
menu: Option<Button>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connect {
|
||||||
|
pub fn new<T>(message: T, font: Font, fg: Color, initial_setup: bool, auto_update: bool) -> Self
|
||||||
|
where
|
||||||
|
T: Into<TString<'static>>,
|
||||||
|
{
|
||||||
|
let (bg, menu_btn, cancel) = if initial_setup {
|
||||||
|
(
|
||||||
|
WELCOME_COLOR,
|
||||||
|
Button::with_icon(Icon::new(MENU32))
|
||||||
|
.styled(button_initial())
|
||||||
|
.with_expanded_touch_area(Insets::uniform(13)),
|
||||||
|
Button::with_text("Cancel".into()).styled(button_initial()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
BLD_BG,
|
||||||
|
Button::with_icon(Icon::new(MENU32))
|
||||||
|
.styled(button_bld_menu())
|
||||||
|
.with_expanded_touch_area(Insets::uniform(13)),
|
||||||
|
Button::with_text("Cancel".into()).styled(button_bld()),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let menu = if auto_update { Some(menu_btn) } else { None };
|
||||||
|
|
||||||
|
let mut instance = Self {
|
||||||
|
fg,
|
||||||
|
bg: Pad::with_background(bg),
|
||||||
|
message: message.into(),
|
||||||
|
font,
|
||||||
|
button: cancel,
|
||||||
|
menu,
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.bg.clear();
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Connect {
|
||||||
|
type Msg = ConnectMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.bg.place(bounds);
|
||||||
|
self.menu.place(CORNER_BUTTON_AREA);
|
||||||
|
self.button.place(Rect::new(
|
||||||
|
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
||||||
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
||||||
|
));
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
|
||||||
|
return Some(ConnectMsg::Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ble", feature = "button"))]
|
||||||
|
if let Event::Button(_) = event {
|
||||||
|
return Some(ConnectMsg::PairingMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "powerctl")]
|
||||||
|
if let Some(ButtonMsg::Clicked) = self.menu.event(ctx, event) {
|
||||||
|
return Some(ConnectMsg::Menu);
|
||||||
|
};
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.bg.render(target);
|
||||||
|
self.button.render(target);
|
||||||
|
#[cfg(feature = "powerctl")]
|
||||||
|
self.menu.render(target);
|
||||||
|
|
||||||
|
self.message.map(|t| {
|
||||||
|
shape::Text::new(
|
||||||
|
self.bg.area.center() + Offset::y(self.font.text_height() / 2),
|
||||||
|
t,
|
||||||
|
self.font,
|
||||||
|
)
|
||||||
|
.with_fg(self.fg)
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for Connect {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("Connect");
|
||||||
|
t.string("message", self.message);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
strutil::TString,
|
strutil::TString,
|
||||||
ui::{
|
ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
component::{Component, Event, EventCtx, Label, Pad},
|
||||||
constant::screen,
|
constant::screen,
|
||||||
display::Icon,
|
display::Icon,
|
||||||
geometry::{Alignment, Insets, Point, Rect},
|
geometry::{Alignment, Insets, Point, Rect},
|
||||||
@ -27,29 +27,27 @@ pub enum IntroMsg {
|
|||||||
|
|
||||||
pub struct Intro<'a> {
|
pub struct Intro<'a> {
|
||||||
bg: Pad,
|
bg: Pad,
|
||||||
title: Child<Label<'a>>,
|
title: Label<'a>,
|
||||||
menu: Child<Button>,
|
menu: Button,
|
||||||
host: Child<Button>,
|
host: Button,
|
||||||
text: Child<Label<'a>>,
|
text: Label<'a>,
|
||||||
warn: Option<Child<Label<'a>>>,
|
warn: Option<Label<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Intro<'a> {
|
impl<'a> Intro<'a> {
|
||||||
pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
|
pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||||
title: Child::new(Label::left_aligned(title, text_title(BLD_BG)).vertically_centered()),
|
title: Label::left_aligned(title, text_title(BLD_BG)).vertically_centered(),
|
||||||
menu: Child::new(
|
menu: Button::with_icon(Icon::new(MENU32))
|
||||||
Button::with_icon(Icon::new(MENU32))
|
|
||||||
.styled(button_bld_menu())
|
.styled(button_bld_menu())
|
||||||
.with_expanded_touch_area(Insets::uniform(13)),
|
.with_expanded_touch_area(Insets::uniform(13)),
|
||||||
),
|
host: Button::with_text("INSTALL FIRMWARE".into()).styled(button_bld()),
|
||||||
host: Child::new(Button::with_text("INSTALL FIRMWARE".into()).styled(button_bld())),
|
text: Label::left_aligned(content, TEXT_NORMAL).vertically_centered(),
|
||||||
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
warn: (!fw_ok).then_some(
|
||||||
warn: (!fw_ok).then_some(Child::new(
|
|
||||||
Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_WARNING)
|
Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_WARNING)
|
||||||
.vertically_centered(),
|
.vertically_centered(),
|
||||||
)),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
trezorhal::secbool::{secbool, sectrue},
|
trezorhal::secbool::{secbool, sectrue},
|
||||||
ui::{
|
ui::{
|
||||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
component::{Component, Event, EventCtx, Label, Pad},
|
||||||
constant::{screen, WIDTH},
|
constant::{screen, WIDTH},
|
||||||
display::Icon,
|
display::Icon,
|
||||||
geometry::{Insets, Point, Rect},
|
geometry::{Insets, Point, Rect},
|
||||||
@ -26,41 +26,56 @@ pub enum MenuMsg {
|
|||||||
Close = 0xAABBCCDD,
|
Close = 0xAABBCCDD,
|
||||||
Reboot = 0x11223344,
|
Reboot = 0x11223344,
|
||||||
FactoryReset = 0x55667788,
|
FactoryReset = 0x55667788,
|
||||||
|
Bluetooth = 0x99AABBCC,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Menu {
|
pub struct Menu {
|
||||||
bg: Pad,
|
bg: Pad,
|
||||||
title: Child<Label<'static>>,
|
title: Label<'static>,
|
||||||
close: Child<Button>,
|
close: Button,
|
||||||
reboot: Child<Button>,
|
reboot: Button,
|
||||||
reset: Child<Button>,
|
reset: Button,
|
||||||
|
bluetooth: Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Menu {
|
impl Menu {
|
||||||
pub fn new(firmware_present: secbool) -> Self {
|
pub fn new(firmware_present: secbool) -> Self {
|
||||||
let content_reboot = IconText::new("REBOOT TREZOR", Icon::new(REFRESH24));
|
let content_reboot = IconText::new("REBOOT TREZOR", Icon::new(REFRESH24));
|
||||||
let content_reset = IconText::new("FACTORY RESET", Icon::new(FIRE24));
|
let content_reset = IconText::new("FACTORY RESET", Icon::new(FIRE24));
|
||||||
|
let content_bluetooth = IconText::new("BLUETOOTH", Icon::new(FIRE24));
|
||||||
|
|
||||||
let mut instance = Self {
|
let mut instance = Self {
|
||||||
bg: Pad::with_background(BLD_BG),
|
bg: Pad::with_background(BLD_BG),
|
||||||
title: Child::new(
|
title: Label::left_aligned("BOOTLOADER".into(), text_title(BLD_BG))
|
||||||
Label::left_aligned("BOOTLOADER".into(), text_title(BLD_BG)).vertically_centered(),
|
.vertically_centered(),
|
||||||
),
|
|
||||||
close: Child::new(
|
close: Button::with_icon(Icon::new(X32))
|
||||||
Button::with_icon(Icon::new(X32))
|
|
||||||
.styled(button_bld_menu())
|
.styled(button_bld_menu())
|
||||||
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
||||||
),
|
|
||||||
reboot: Child::new(
|
reboot: Button::with_icon_and_text(content_reboot)
|
||||||
Button::with_icon_and_text(content_reboot)
|
|
||||||
.styled(button_bld())
|
.styled(button_bld())
|
||||||
.initially_enabled(sectrue == firmware_present),
|
.initially_enabled(sectrue == firmware_present),
|
||||||
),
|
|
||||||
reset: Child::new(Button::with_icon_and_text(content_reset).styled(button_bld())),
|
reset: Button::with_icon_and_text(content_reset).styled(button_bld()),
|
||||||
|
bluetooth: Button::with_icon_and_text(content_bluetooth).styled(button_bld()),
|
||||||
};
|
};
|
||||||
instance.bg.clear();
|
instance.bg.clear();
|
||||||
instance
|
instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_button_pos(&self, i: u8) -> Rect {
|
||||||
|
Rect::new(
|
||||||
|
Point::new(
|
||||||
|
CONTENT_PADDING,
|
||||||
|
BUTTON_AREA_START + i as i16 * (BUTTON_HEIGHT + BUTTON_SPACING),
|
||||||
|
),
|
||||||
|
Point::new(
|
||||||
|
WIDTH - CONTENT_PADDING,
|
||||||
|
BUTTON_AREA_START + (i + 1) as i16 * BUTTON_HEIGHT + i as i16 * BUTTON_SPACING,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Menu {
|
impl Component for Menu {
|
||||||
@ -70,20 +85,10 @@ impl Component for Menu {
|
|||||||
self.bg.place(screen());
|
self.bg.place(screen());
|
||||||
self.title.place(TITLE_AREA);
|
self.title.place(TITLE_AREA);
|
||||||
self.close.place(CORNER_BUTTON_AREA);
|
self.close.place(CORNER_BUTTON_AREA);
|
||||||
self.reboot.place(Rect::new(
|
self.reboot.place(self.get_button_pos(0));
|
||||||
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
self.reset.place(self.get_button_pos(1));
|
||||||
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
#[cfg(feature = "ble")]
|
||||||
));
|
self.bluetooth.place(self.get_button_pos(2));
|
||||||
self.reset.place(Rect::new(
|
|
||||||
Point::new(
|
|
||||||
CONTENT_PADDING,
|
|
||||||
BUTTON_AREA_START + BUTTON_HEIGHT + BUTTON_SPACING,
|
|
||||||
),
|
|
||||||
Point::new(
|
|
||||||
WIDTH - CONTENT_PADDING,
|
|
||||||
BUTTON_AREA_START + 2 * BUTTON_HEIGHT + BUTTON_SPACING,
|
|
||||||
),
|
|
||||||
));
|
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +102,10 @@ impl Component for Menu {
|
|||||||
if let Some(Clicked) = self.reset.event(ctx, event) {
|
if let Some(Clicked) = self.reset.event(ctx, event) {
|
||||||
return Some(Self::Msg::FactoryReset);
|
return Some(Self::Msg::FactoryReset);
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
if let Some(Clicked) = self.bluetooth.event(ctx, event) {
|
||||||
|
return Some(Self::Msg::Bluetooth);
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -107,5 +116,7 @@ impl Component for Menu {
|
|||||||
self.close.render(target);
|
self.close.render(target);
|
||||||
self.reboot.render(target);
|
self.reboot.render(target);
|
||||||
self.reset.render(target);
|
self.reset.render(target);
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
self.bluetooth.render(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,57 @@
|
|||||||
use heapless::String;
|
pub mod connect;
|
||||||
|
pub mod intro;
|
||||||
|
pub mod menu;
|
||||||
|
pub mod welcome;
|
||||||
|
|
||||||
use crate::{
|
#[cfg(feature = "ble")]
|
||||||
trezorhal::secbool::secbool,
|
pub mod pairing_finalization;
|
||||||
ui::{
|
|
||||||
component::{connect::Connect, Label},
|
use heapless::String;
|
||||||
display::{self, Color, Icon},
|
use ufmt::uwrite;
|
||||||
geometry::{Point, Rect},
|
|
||||||
layout::simplified::{run, show},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
bootloader::welcome::Welcome,
|
bootloader::{connect::Connect, welcome::Welcome},
|
||||||
component::{
|
component::{
|
||||||
bl_confirm::{Confirm, ConfirmTitle},
|
bl_confirm::{Confirm, ConfirmTitle},
|
||||||
Button, ResultScreen, WelcomeScreen,
|
Button, ResultScreen, WelcomeScreen,
|
||||||
},
|
},
|
||||||
|
cshape::{render_loader, LoaderRange},
|
||||||
fonts,
|
fonts,
|
||||||
theme::{
|
theme::{
|
||||||
self,
|
self,
|
||||||
bootloader::{
|
bootloader::{
|
||||||
button_bld, button_bld_menu, button_confirm, button_wipe_cancel, button_wipe_confirm,
|
button_bld, button_bld_menu, button_confirm, button_wipe_cancel, button_wipe_confirm,
|
||||||
BLD_BG, BLD_FG, BLD_TITLE_COLOR, BLD_WIPE_COLOR, CHECK24, CHECK40, DOWNLOAD32, FIRE32,
|
BLD_BG, BLD_FG, BLD_TITLE_COLOR, BLD_WARN_COLOR, BLD_WIPE_COLOR, CHECK24, CHECK40,
|
||||||
FIRE40, RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE, TEXT_BOLD, TEXT_NORMAL,
|
DOWNLOAD32, FIRE32, FIRE40, RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE, TEXT_BOLD,
|
||||||
TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
TEXT_NORMAL, TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
||||||
},
|
},
|
||||||
FG,
|
FG,
|
||||||
},
|
},
|
||||||
UIBolt,
|
UIBolt,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
use crate::ui::{ui_bootloader::BootloaderUI, CommonUI};
|
trezorhal::secbool::secbool,
|
||||||
|
ui::{
|
||||||
use crate::ui::{
|
component::{Event, Label},
|
||||||
display::toif::Toif,
|
display::{self, toif::Toif, Color, Icon, LOADER_MAX},
|
||||||
geometry::{Alignment, Alignment2D, Offset},
|
geometry::{Alignment, Alignment2D, Offset, Point, Rect},
|
||||||
shape,
|
layout::simplified::{process_frame_event, run, show},
|
||||||
shape::render_on_display,
|
shape::{self, render_on_display},
|
||||||
|
ui_bootloader::{BootloaderLayoutType, BootloaderUI},
|
||||||
|
CommonUI,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ufmt::uwrite;
|
|
||||||
|
|
||||||
use super::theme::bootloader::BLD_WARN_COLOR;
|
|
||||||
|
|
||||||
use intro::Intro;
|
use intro::Intro;
|
||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
|
|
||||||
use super::cshape::{render_loader, LoaderRange};
|
#[cfg(feature = "ble")]
|
||||||
use crate::ui::display::LOADER_MAX;
|
use super::theme::bootloader::{button_confirm_initial, button_initial};
|
||||||
|
|
||||||
pub mod intro;
|
#[cfg(feature = "ble")]
|
||||||
pub mod menu;
|
use crate::ui::layout_bolt::{
|
||||||
pub mod welcome;
|
bootloader::pairing_finalization::PairingFinalization,
|
||||||
|
component::{confirm_pairing::ConfirmPairing, pairing_mode::PairingMode},
|
||||||
|
};
|
||||||
|
|
||||||
pub type BootloaderString = String<128>;
|
pub type BootloaderString = String<128>;
|
||||||
|
|
||||||
@ -137,12 +137,78 @@ impl UIBolt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootloaderUI for UIBolt {
|
#[allow(clippy::large_enum_variant)]
|
||||||
fn screen_welcome() {
|
pub enum BootloaderLayout {
|
||||||
let mut frame = Welcome::new();
|
Welcome(Welcome),
|
||||||
show(&mut frame, true);
|
Menu(Menu),
|
||||||
|
Connect(Connect),
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
PairingMode(PairingMode),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootloaderLayoutType for BootloaderLayout {
|
||||||
|
fn event(&mut self, event: Option<Event>) -> u32 {
|
||||||
|
match self {
|
||||||
|
BootloaderLayout::Welcome(f) => process_frame_event::<Welcome>(f, event),
|
||||||
|
BootloaderLayout::Menu(f) => process_frame_event::<Menu>(f, event),
|
||||||
|
BootloaderLayout::Connect(f) => process_frame_event::<Connect>(f, event),
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
BootloaderLayout::PairingMode(f) => process_frame_event::<PairingMode>(f, event),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show(&mut self) {
|
||||||
|
match self {
|
||||||
|
BootloaderLayout::Welcome(f) => show(f, true),
|
||||||
|
BootloaderLayout::Menu(f) => show(f, true),
|
||||||
|
BootloaderLayout::Connect(f) => show(f, true),
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
BootloaderLayout::PairingMode(f) => show(f, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_welcome() -> Self {
|
||||||
|
Self::Welcome(Welcome::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_menu(_initial_setup: bool, firmware_present: secbool) -> Self {
|
||||||
|
Self::Menu(Menu::new(firmware_present))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_connect(initial_setup: bool, auto_update: bool) -> Self {
|
||||||
|
let frame = Connect::new(
|
||||||
|
"Waiting for host...",
|
||||||
|
fonts::FONT_NORMAL,
|
||||||
|
BLD_TITLE_COLOR,
|
||||||
|
initial_setup,
|
||||||
|
auto_update,
|
||||||
|
);
|
||||||
|
Self::Connect(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn init_pairing_mode(initial_setup: bool) -> Self {
|
||||||
|
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||||
|
|
||||||
|
let btn = if initial_setup {
|
||||||
|
Button::with_text("Cancel".into()).styled(button_initial())
|
||||||
|
} else {
|
||||||
|
Button::with_text("Cancel".into()).styled(button_bld())
|
||||||
|
};
|
||||||
|
|
||||||
|
let frame = PairingMode::new(
|
||||||
|
"Waiting for pairing...".into(),
|
||||||
|
fonts::FONT_NORMAL,
|
||||||
|
BLD_TITLE_COLOR,
|
||||||
|
bg,
|
||||||
|
btn,
|
||||||
|
);
|
||||||
|
Self::PairingMode(frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootloaderUI for UIBolt {
|
||||||
|
type CLayoutType = BootloaderLayout;
|
||||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
||||||
let mut reboot_msg = BootloaderString::new();
|
let mut reboot_msg = BootloaderString::new();
|
||||||
|
|
||||||
@ -257,10 +323,6 @@ impl BootloaderUI for UIBolt {
|
|||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
|
||||||
run(&mut Menu::new(firmware_present))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||||
let mut title_str: BootloaderString = String::new();
|
let mut title_str: BootloaderString = String::new();
|
||||||
unwrap!(title_str.push_str("BOOTLOADER "));
|
unwrap!(title_str.push_str("BOOTLOADER "));
|
||||||
@ -321,17 +383,6 @@ impl BootloaderUI for UIBolt {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_connect(initial_setup: bool) {
|
|
||||||
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
|
||||||
let mut frame = Connect::new(
|
|
||||||
"Waiting for host...",
|
|
||||||
fonts::FONT_NORMAL,
|
|
||||||
BLD_TITLE_COLOR,
|
|
||||||
bg,
|
|
||||||
);
|
|
||||||
show(&mut frame, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_wipe_success() {
|
fn screen_wipe_success() {
|
||||||
let mut frame = ResultScreen::new(
|
let mut frame = ResultScreen::new(
|
||||||
&RESULT_WIPE,
|
&RESULT_WIPE,
|
||||||
@ -439,4 +490,47 @@ impl BootloaderUI for UIBolt {
|
|||||||
|
|
||||||
display::refresh();
|
display::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_confirm_pairing(code: u32, initial_setup: bool) -> u32 {
|
||||||
|
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||||
|
let title = Label::centered("Pair device".into(), TEXT_NORMAL);
|
||||||
|
|
||||||
|
let (right, left) = if initial_setup {
|
||||||
|
(
|
||||||
|
Button::with_text("CONFIRM".into()).styled(button_confirm_initial()),
|
||||||
|
Button::with_text("REJECT".into()).styled(button_initial()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
Button::with_text("CONFIRM".into()).styled(button_confirm()),
|
||||||
|
Button::with_text("REJECT".into()).styled(button_bld()),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut frame = ConfirmPairing::new(bg, left, right, title, code);
|
||||||
|
|
||||||
|
run(&mut frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_pairing_mode_finalizing(initial_setup: bool) -> u32 {
|
||||||
|
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||||
|
|
||||||
|
let btn = if initial_setup {
|
||||||
|
Button::with_text("Cancel".into()).styled(button_initial())
|
||||||
|
} else {
|
||||||
|
Button::with_text("Cancel".into()).styled(button_bld())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut frame = PairingFinalization::new(
|
||||||
|
"Waiting for host confirmation...",
|
||||||
|
fonts::FONT_NORMAL,
|
||||||
|
BLD_TITLE_COLOR,
|
||||||
|
bg,
|
||||||
|
btn,
|
||||||
|
);
|
||||||
|
|
||||||
|
run(&mut frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,107 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
display::{Color, Font},
|
||||||
|
event::BLEEvent,
|
||||||
|
geometry::{Alignment, Offset, Point, Rect},
|
||||||
|
layout_bolt::{
|
||||||
|
component::{Button, ButtonMsg},
|
||||||
|
constant::WIDTH,
|
||||||
|
theme::bootloader::{BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING},
|
||||||
|
},
|
||||||
|
shape::{self, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
|
pub enum PairingFinalizationMsg {
|
||||||
|
Completed = 1,
|
||||||
|
Cancel = 2,
|
||||||
|
Failed = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PairingFinalization {
|
||||||
|
fg: Color,
|
||||||
|
bg: Pad,
|
||||||
|
message: TString<'static>,
|
||||||
|
font: Font,
|
||||||
|
button: Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PairingFinalization {
|
||||||
|
pub fn new<T>(message: T, font: Font, fg: Color, bg: Color, button: Button) -> Self
|
||||||
|
where
|
||||||
|
T: Into<TString<'static>>,
|
||||||
|
{
|
||||||
|
let mut instance = Self {
|
||||||
|
fg,
|
||||||
|
bg: Pad::with_background(bg),
|
||||||
|
message: message.into(),
|
||||||
|
font,
|
||||||
|
button,
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.bg.clear();
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for PairingFinalization {
|
||||||
|
type Msg = PairingFinalizationMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.bg.place(bounds);
|
||||||
|
self.button.place(Rect::new(
|
||||||
|
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
||||||
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
||||||
|
));
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
|
||||||
|
return Some(PairingFinalizationMsg::Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Event::BLE(BLEEvent::PairingCanceled) = event {
|
||||||
|
return Some(PairingFinalizationMsg::Failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Event::BLE(BLEEvent::Disconnected) = event {
|
||||||
|
return Some(PairingFinalizationMsg::Failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Event::BLE(BLEEvent::PairingCompleted) = event {
|
||||||
|
return Some(PairingFinalizationMsg::Completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.bg.render(target);
|
||||||
|
self.button.render(target);
|
||||||
|
|
||||||
|
self.message.map(|t| {
|
||||||
|
shape::Text::new(
|
||||||
|
self.bg.area.center() + Offset::y(self.font.text_height() / 2),
|
||||||
|
t,
|
||||||
|
self.font,
|
||||||
|
)
|
||||||
|
.with_fg(self.fg)
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for PairingFinalization {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("ParingFinalization");
|
||||||
|
t.string("message", self.message);
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,3 @@
|
|||||||
use crate::ui::{
|
|
||||||
component::{Component, Event, EventCtx, Never, Pad},
|
|
||||||
constant::screen,
|
|
||||||
display::toif::Toif,
|
|
||||||
geometry::{Alignment, Alignment2D, Offset, Rect},
|
|
||||||
shape::{self, Renderer},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::super::{
|
use super::super::{
|
||||||
fonts,
|
fonts,
|
||||||
theme::{
|
theme::{
|
||||||
@ -13,33 +5,70 @@ use super::super::{
|
|||||||
GREY_MEDIUM, WHITE,
|
GREY_MEDIUM, WHITE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use crate::ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
constant::screen,
|
||||||
|
display::{toif::Toif, Icon},
|
||||||
|
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
|
||||||
|
layout_bolt::{
|
||||||
|
component::Button,
|
||||||
|
theme::bootloader::{button_initial, CORNER_BUTTON_AREA, MENU32},
|
||||||
|
},
|
||||||
|
shape::{self, Renderer},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "powerctl")]
|
||||||
|
use crate::ui::layout_bolt::component::ButtonMsg::Clicked;
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
|
pub enum WelcomeMsg {
|
||||||
|
Cancel = 1,
|
||||||
|
PairingMode = 2,
|
||||||
|
Menu = 3,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Welcome {
|
pub struct Welcome {
|
||||||
bg: Pad,
|
bg: Pad,
|
||||||
|
menu: Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Welcome {
|
impl Welcome {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
bg: Pad::with_background(WELCOME_COLOR).with_clear(),
|
bg: Pad::with_background(WELCOME_COLOR).with_clear(),
|
||||||
|
menu: Button::with_icon(Icon::new(MENU32))
|
||||||
|
.styled(button_initial())
|
||||||
|
.with_expanded_touch_area(Insets::uniform(13)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Welcome {
|
impl Component for Welcome {
|
||||||
type Msg = Never;
|
type Msg = WelcomeMsg;
|
||||||
|
|
||||||
fn place(&mut self, bounds: Rect) -> Rect {
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
self.bg.place(screen());
|
self.bg.place(screen());
|
||||||
|
self.menu.place(CORNER_BUTTON_AREA);
|
||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
||||||
|
#[cfg(all(feature = "ble", feature = "button"))]
|
||||||
|
if let Event::Button(_) = _event {
|
||||||
|
return Some(WelcomeMsg::PairingMode);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "powerctl")]
|
||||||
|
if let Some(Clicked) = self.menu.event(_ctx, _event) {
|
||||||
|
return Some(WelcomeMsg::Menu);
|
||||||
|
};
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
self.bg.render(target);
|
self.bg.render(target);
|
||||||
|
#[cfg(feature = "powerctl")]
|
||||||
|
self.menu.render(target);
|
||||||
|
|
||||||
shape::Text::new(
|
shape::Text::new(
|
||||||
screen().top_center() + Offset::y(102),
|
screen().top_center() + Offset::y(102),
|
||||||
|
130
core/embed/rust/src/ui/layout_bolt/component/confirm_pairing.rs
Normal file
130
core/embed/rust/src/ui/layout_bolt/component/confirm_pairing.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use super::{
|
||||||
|
super::{
|
||||||
|
constant::WIDTH,
|
||||||
|
theme::bootloader::{BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING, TITLE_AREA},
|
||||||
|
},
|
||||||
|
Button,
|
||||||
|
ButtonMsg::Clicked,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
strutil::format_i64,
|
||||||
|
ui::{
|
||||||
|
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||||
|
constant,
|
||||||
|
display::Color,
|
||||||
|
event::BLEEvent,
|
||||||
|
geometry::{Offset, Point, Rect},
|
||||||
|
layout_bolt::{fonts, theme::WHITE},
|
||||||
|
shape,
|
||||||
|
shape::Renderer,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ICON_TOP: i16 = 17;
|
||||||
|
const CONTENT_START: i16 = 72;
|
||||||
|
|
||||||
|
const CONTENT_AREA: Rect = Rect::new(
|
||||||
|
Point::new(CONTENT_PADDING, CONTENT_START),
|
||||||
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START - CONTENT_PADDING),
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
|
#[cfg_attr(feature = "debug", derive(ufmt::derive::uDebug))]
|
||||||
|
pub enum ConfirmPairingMsg {
|
||||||
|
Cancel = 1,
|
||||||
|
Confirm = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ConfirmPairing<'a> {
|
||||||
|
bg: Pad,
|
||||||
|
content_pad: Pad,
|
||||||
|
bg_color: Color,
|
||||||
|
title: Label<'a>,
|
||||||
|
code: u32,
|
||||||
|
left_button: Child<Button>,
|
||||||
|
right_button: Child<Button>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ConfirmPairing<'a> {
|
||||||
|
pub fn new(
|
||||||
|
bg_color: Color,
|
||||||
|
left_button: Button,
|
||||||
|
right_button: Button,
|
||||||
|
title: Label<'a>,
|
||||||
|
code: u32,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
bg: Pad::with_background(bg_color).with_clear(),
|
||||||
|
content_pad: Pad::with_background(bg_color),
|
||||||
|
bg_color,
|
||||||
|
title,
|
||||||
|
left_button: Child::new(left_button),
|
||||||
|
right_button: Child::new(right_button),
|
||||||
|
code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for ConfirmPairing<'_> {
|
||||||
|
type Msg = ConfirmPairingMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.bg.place(constant::screen());
|
||||||
|
self.content_pad.place(Rect::new(
|
||||||
|
Point::zero(),
|
||||||
|
Point::new(WIDTH, BUTTON_AREA_START),
|
||||||
|
));
|
||||||
|
|
||||||
|
self.title.place(TITLE_AREA);
|
||||||
|
|
||||||
|
let button_size = Offset::new((WIDTH - 3 * CONTENT_PADDING) / 2, BUTTON_HEIGHT);
|
||||||
|
self.left_button.place(Rect::from_top_left_and_size(
|
||||||
|
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
||||||
|
button_size,
|
||||||
|
));
|
||||||
|
self.right_button.place(Rect::from_top_left_and_size(
|
||||||
|
Point::new(2 * CONTENT_PADDING + button_size.x, BUTTON_AREA_START),
|
||||||
|
button_size,
|
||||||
|
));
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(Clicked) = self.left_button.event(ctx, event) {
|
||||||
|
return Some(Self::Msg::Cancel);
|
||||||
|
};
|
||||||
|
if let Some(Clicked) = self.right_button.event(ctx, event) {
|
||||||
|
return Some(Self::Msg::Confirm);
|
||||||
|
};
|
||||||
|
match event {
|
||||||
|
Event::BLE(BLEEvent::PairingCanceled) => Some(Self::Msg::Cancel),
|
||||||
|
Event::BLE(BLEEvent::Disconnected) => Some(Self::Msg::Cancel),
|
||||||
|
Event::BLE(BLEEvent::Connected) => Some(Self::Msg::Cancel),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.bg.render(target);
|
||||||
|
self.content_pad.render(target);
|
||||||
|
|
||||||
|
self.left_button.render(target);
|
||||||
|
self.right_button.render(target);
|
||||||
|
self.title.render(target);
|
||||||
|
|
||||||
|
let mut buf = [0; 20];
|
||||||
|
let text = unwrap!(format_i64(self.code as _, &mut buf));
|
||||||
|
|
||||||
|
shape::Text::new(CONTENT_AREA.left_center(), text, fonts::FONT_BOLD_UPPER)
|
||||||
|
.with_fg(WHITE)
|
||||||
|
.render(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for ConfirmPairing<'_> {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("ConfirmPairing");
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,16 @@
|
|||||||
#[cfg(all(feature = "micropython", feature = "translations"))]
|
#[cfg(all(feature = "micropython", feature = "translations"))]
|
||||||
mod address_details;
|
mod address_details;
|
||||||
pub mod bl_confirm;
|
pub mod bl_confirm;
|
||||||
|
|
||||||
mod button;
|
mod button;
|
||||||
#[cfg(feature = "translations")]
|
#[cfg(feature = "translations")]
|
||||||
mod coinjoin_progress;
|
mod coinjoin_progress;
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
pub mod confirm_pairing;
|
||||||
mod dialog;
|
mod dialog;
|
||||||
mod fido;
|
mod fido;
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
pub mod pairing_mode;
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
mod fido_icons;
|
mod fido_icons;
|
||||||
mod error;
|
mod error;
|
||||||
|
104
core/embed/rust/src/ui/layout_bolt/component/pairing_mode.rs
Normal file
104
core/embed/rust/src/ui/layout_bolt/component/pairing_mode.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
display::{Color, Font},
|
||||||
|
event::BLEEvent,
|
||||||
|
geometry::{Alignment, Offset, Rect},
|
||||||
|
layout::simplified::ReturnToC,
|
||||||
|
layout_bolt::component::{Button, ButtonMsg},
|
||||||
|
shape::{self, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum PairingMsg {
|
||||||
|
Cancel,
|
||||||
|
Pairing(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReturnToC for PairingMsg {
|
||||||
|
fn return_to_c(self) -> u32 {
|
||||||
|
match self {
|
||||||
|
PairingMsg::Cancel => 1000000,
|
||||||
|
PairingMsg::Pairing(code) => code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PairingMode {
|
||||||
|
fg: Color,
|
||||||
|
bg: Pad,
|
||||||
|
message: TString<'static>,
|
||||||
|
font: Font,
|
||||||
|
button: Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PairingMode {
|
||||||
|
pub fn new(
|
||||||
|
message: TString<'static>,
|
||||||
|
font: Font,
|
||||||
|
fg: Color,
|
||||||
|
bg: Color,
|
||||||
|
button: Button,
|
||||||
|
) -> Self {
|
||||||
|
let mut instance = Self {
|
||||||
|
fg,
|
||||||
|
bg: Pad::with_background(bg),
|
||||||
|
message,
|
||||||
|
font,
|
||||||
|
button,
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.bg.clear();
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for PairingMode {
|
||||||
|
type Msg = PairingMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.bg.place(bounds);
|
||||||
|
self.button.place(bounds.split_bottom(60).1);
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
|
||||||
|
return Some(PairingMsg::Cancel);
|
||||||
|
}
|
||||||
|
if let Event::BLE(BLEEvent::PairingRequest(code)) = event {
|
||||||
|
return Some(PairingMsg::Pairing(code));
|
||||||
|
}
|
||||||
|
if let Event::BLE(BLEEvent::PairingCanceled) = event {
|
||||||
|
return Some(PairingMsg::Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.bg.render(target);
|
||||||
|
self.button.render(target);
|
||||||
|
|
||||||
|
self.message.map(|t| {
|
||||||
|
shape::Text::new(
|
||||||
|
self.bg.area.center() + Offset::y(self.font.text_height() / 2),
|
||||||
|
t,
|
||||||
|
self.font,
|
||||||
|
)
|
||||||
|
.with_fg(self.fg)
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for PairingMode {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("PairingMode");
|
||||||
|
t.string("message", self.message);
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,17 @@
|
|||||||
|
use super::super::{
|
||||||
|
component::{ButtonStyle, ButtonStyleSheet, ResultStyle},
|
||||||
|
fonts,
|
||||||
|
theme::{BLACK, FG, GREY_DARK, GREY_LIGHT, WHITE},
|
||||||
|
};
|
||||||
use crate::ui::{
|
use crate::ui::{
|
||||||
component::{text::TextStyle, LineBreaking::BreakWordsNoHyphen},
|
component::{text::TextStyle, LineBreaking::BreakWordsNoHyphen},
|
||||||
constant::{HEIGHT, WIDTH},
|
constant::{HEIGHT, WIDTH},
|
||||||
display::Color,
|
display::Color,
|
||||||
geometry::{Offset, Point, Rect},
|
geometry::{Offset, Point, Rect},
|
||||||
|
layout_bolt::theme::GREY_MEDIUM,
|
||||||
util::include_res,
|
util::include_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::super::{
|
|
||||||
component::{ButtonStyle, ButtonStyleSheet, ResultStyle},
|
|
||||||
fonts,
|
|
||||||
theme::{BLACK, FG, GREY_DARK, GREY_LIGHT, WHITE},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BLD_BG: Color = Color::rgb(0x00, 0x1E, 0xAD);
|
pub const BLD_BG: Color = Color::rgb(0x00, 0x1E, 0xAD);
|
||||||
pub const BLD_FG: Color = WHITE;
|
pub const BLD_FG: Color = WHITE;
|
||||||
pub const BLD_WIPE_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
pub const BLD_WIPE_COLOR: Color = Color::rgb(0xE7, 0x0E, 0x0E);
|
||||||
@ -30,6 +30,9 @@ pub const BLD_INSTALL_BTN_COLOR_ACTIVE: Color = Color::rgb(0xCD, 0xD2, 0xEF);
|
|||||||
pub const BLD_BTN_COLOR: Color = Color::rgb(0x2D, 0x42, 0xBF);
|
pub const BLD_BTN_COLOR: Color = Color::rgb(0x2D, 0x42, 0xBF);
|
||||||
pub const BLD_BTN_COLOR_ACTIVE: Color = Color::rgb(0x04, 0x10, 0x58);
|
pub const BLD_BTN_COLOR_ACTIVE: Color = Color::rgb(0x04, 0x10, 0x58);
|
||||||
|
|
||||||
|
pub const BLD_BTN_INITIAL_COLOR: Color = GREY_MEDIUM;
|
||||||
|
pub const BLD_BTN_INITIAL_ACTIVE: Color = GREY_DARK;
|
||||||
|
|
||||||
pub const BLD_TITLE_COLOR: Color = WHITE;
|
pub const BLD_TITLE_COLOR: Color = WHITE;
|
||||||
|
|
||||||
pub const WELCOME_COLOR: Color = BLACK;
|
pub const WELCOME_COLOR: Color = BLACK;
|
||||||
@ -234,6 +237,70 @@ pub fn button_bld() -> ButtonStyleSheet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn button_confirm_initial() -> ButtonStyleSheet {
|
||||||
|
ButtonStyleSheet {
|
||||||
|
normal: &ButtonStyle {
|
||||||
|
font: fonts::FONT_BOLD_UPPER,
|
||||||
|
text_color: WELCOME_COLOR,
|
||||||
|
button_color: WHITE,
|
||||||
|
background_color: WELCOME_COLOR,
|
||||||
|
border_color: WELCOME_COLOR,
|
||||||
|
border_radius: RADIUS,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
active: &ButtonStyle {
|
||||||
|
font: fonts::FONT_BOLD_UPPER,
|
||||||
|
text_color: WELCOME_COLOR,
|
||||||
|
button_color: GREY_LIGHT,
|
||||||
|
background_color: WELCOME_COLOR,
|
||||||
|
border_color: WELCOME_COLOR,
|
||||||
|
border_radius: RADIUS,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
disabled: &ButtonStyle {
|
||||||
|
font: fonts::FONT_BOLD_UPPER,
|
||||||
|
text_color: FG,
|
||||||
|
button_color: GREY_DARK,
|
||||||
|
background_color: FG,
|
||||||
|
border_color: FG,
|
||||||
|
border_radius: RADIUS,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button_initial() -> ButtonStyleSheet {
|
||||||
|
ButtonStyleSheet {
|
||||||
|
normal: &ButtonStyle {
|
||||||
|
font: fonts::FONT_BOLD_UPPER,
|
||||||
|
text_color: WHITE,
|
||||||
|
button_color: BLD_BTN_INITIAL_COLOR,
|
||||||
|
background_color: WELCOME_COLOR,
|
||||||
|
border_color: WELCOME_COLOR,
|
||||||
|
border_radius: 4,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
active: &ButtonStyle {
|
||||||
|
font: fonts::FONT_BOLD_UPPER,
|
||||||
|
text_color: WHITE,
|
||||||
|
button_color: BLD_BTN_INITIAL_ACTIVE,
|
||||||
|
background_color: WELCOME_COLOR,
|
||||||
|
border_color: WELCOME_COLOR,
|
||||||
|
border_radius: 4,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
disabled: &ButtonStyle {
|
||||||
|
font: fonts::FONT_BOLD_UPPER,
|
||||||
|
text_color: GREY_LIGHT,
|
||||||
|
button_color: BLD_BTN_COLOR,
|
||||||
|
background_color: WELCOME_COLOR,
|
||||||
|
border_color: WELCOME_COLOR,
|
||||||
|
border_radius: 4,
|
||||||
|
border_width: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn text_title(bg: Color) -> TextStyle {
|
pub const fn text_title(bg: Color) -> TextStyle {
|
||||||
TextStyle::new(
|
TextStyle::new(
|
||||||
fonts::FONT_BOLD_UPPER,
|
fonts::FONT_BOLD_UPPER,
|
||||||
|
96
core/embed/rust/src/ui/layout_caesar/bootloader/connect.rs
Normal file
96
core/embed/rust/src/ui/layout_caesar/bootloader/connect.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
display::{Color, Font},
|
||||||
|
geometry::{Alignment, Offset, Rect},
|
||||||
|
layout_caesar::{
|
||||||
|
component::{ButtonController, ButtonControllerMsg, ButtonLayout, ButtonPos},
|
||||||
|
theme::{BUTTON_HEIGHT, TITLE_AREA_HEIGHT},
|
||||||
|
},
|
||||||
|
shape::{self, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
|
pub enum ConnectMsg {
|
||||||
|
Cancel = 1,
|
||||||
|
PairingMode = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connect {
|
||||||
|
fg: Color,
|
||||||
|
bg: Pad,
|
||||||
|
message: TString<'static>,
|
||||||
|
font: Font,
|
||||||
|
buttons: ButtonController,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connect {
|
||||||
|
pub fn new<T>(message: T, font: Font, fg: Color, bg: Color) -> Self
|
||||||
|
where
|
||||||
|
T: Into<TString<'static>>,
|
||||||
|
{
|
||||||
|
let mut instance = Self {
|
||||||
|
fg,
|
||||||
|
bg: Pad::with_background(bg),
|
||||||
|
message: message.into(),
|
||||||
|
font,
|
||||||
|
buttons: ButtonController::new(ButtonLayout::none_none_text("Cancel".into())),
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.bg.clear();
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Connect {
|
||||||
|
type Msg = ConnectMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.bg.place(bounds);
|
||||||
|
|
||||||
|
// Title on top, buttons on bottom, text in between
|
||||||
|
let (_, text_and_buttons_area) = bounds.split_top(TITLE_AREA_HEIGHT);
|
||||||
|
let (_, buttons_area) = text_and_buttons_area.split_bottom(BUTTON_HEIGHT);
|
||||||
|
|
||||||
|
self.buttons.place(buttons_area);
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(ButtonControllerMsg::Triggered(ButtonPos::Right, false)) =
|
||||||
|
self.buttons.event(ctx, event)
|
||||||
|
{
|
||||||
|
return Some(ConnectMsg::Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.bg.render(target);
|
||||||
|
self.buttons.render(target);
|
||||||
|
|
||||||
|
self.message.map(|t| {
|
||||||
|
shape::Text::new(
|
||||||
|
self.bg.area.center() + Offset::y(self.font.text_height() / 2),
|
||||||
|
t,
|
||||||
|
self.font,
|
||||||
|
)
|
||||||
|
.with_fg(self.fg)
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for Connect {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("Connect");
|
||||||
|
t.string("message", self.message);
|
||||||
|
}
|
||||||
|
}
|
@ -3,12 +3,12 @@ use heapless::String;
|
|||||||
use crate::{
|
use crate::{
|
||||||
trezorhal::secbool::secbool,
|
trezorhal::secbool::secbool,
|
||||||
ui::{
|
ui::{
|
||||||
component::{connect::Connect, Label, LineBreaking::BreakWordsNoHyphen},
|
component::{Label, LineBreaking::BreakWordsNoHyphen},
|
||||||
constant,
|
constant::{self, HEIGHT, SCREEN},
|
||||||
constant::{HEIGHT, SCREEN},
|
|
||||||
display::{self, Color, Icon},
|
display::{self, Color, Icon},
|
||||||
geometry::{Alignment2D, Offset, Point},
|
geometry::{Alignment2D, Offset, Point},
|
||||||
layout::simplified::{run, show, ReturnToC},
|
layout::simplified::{run, show, ReturnToC},
|
||||||
|
ui_bootloader::BootloaderLayoutType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,7 +33,12 @@ mod intro;
|
|||||||
mod menu;
|
mod menu;
|
||||||
mod welcome;
|
mod welcome;
|
||||||
|
|
||||||
use crate::ui::ui_bootloader::BootloaderUI;
|
mod connect;
|
||||||
|
|
||||||
|
use crate::ui::{
|
||||||
|
component::Event, layout::simplified::process_frame_event, ui_bootloader::BootloaderUI,
|
||||||
|
};
|
||||||
|
use connect::Connect;
|
||||||
use intro::Intro;
|
use intro::Intro;
|
||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
use welcome::Welcome;
|
use welcome::Welcome;
|
||||||
@ -89,11 +94,54 @@ impl UICaesar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootloaderUI for UICaesar {
|
pub enum BootloaderLayout {
|
||||||
fn screen_welcome() {
|
Welcome(Welcome),
|
||||||
let mut frame = Welcome::new();
|
Menu(Menu),
|
||||||
show(&mut frame, true);
|
Connect(Connect),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootloaderLayoutType for BootloaderLayout {
|
||||||
|
fn event(&mut self, event: Option<Event>) -> u32 {
|
||||||
|
match self {
|
||||||
|
BootloaderLayout::Welcome(f) => process_frame_event::<Welcome>(f, event),
|
||||||
|
BootloaderLayout::Menu(f) => process_frame_event::<Menu>(f, event),
|
||||||
|
BootloaderLayout::Connect(f) => process_frame_event::<Connect>(f, event),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self) {
|
||||||
|
match self {
|
||||||
|
BootloaderLayout::Welcome(f) => show(f, false),
|
||||||
|
BootloaderLayout::Menu(f) => show(f, false),
|
||||||
|
BootloaderLayout::Connect(f) => show(f, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_welcome() -> Self {
|
||||||
|
Self::Welcome(Welcome::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_menu(_initial_setup: bool, firmware_present: secbool) -> Self {
|
||||||
|
Self::Menu(Menu::new(firmware_present))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_connect(_initial_setup: bool, _auto_update: bool) -> Self {
|
||||||
|
Self::Connect(Connect::new(
|
||||||
|
"Waiting for host...",
|
||||||
|
fonts::FONT_NORMAL,
|
||||||
|
BLD_FG,
|
||||||
|
BLD_BG,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn init_pairing_mode(_initial_setup: bool) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootloaderUI for UICaesar {
|
||||||
|
type CLayoutType = BootloaderLayout;
|
||||||
|
|
||||||
fn screen_install_success(restart_seconds: u8, _initial_setup: bool, complete_draw: bool) {
|
fn screen_install_success(restart_seconds: u8, _initial_setup: bool, complete_draw: bool) {
|
||||||
let mut reboot_msg = BootloaderString::new();
|
let mut reboot_msg = BootloaderString::new();
|
||||||
@ -221,10 +269,6 @@ impl BootloaderUI for UICaesar {
|
|||||||
show(&mut frame, false);
|
show(&mut frame, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
|
||||||
run(&mut Menu::new(firmware_present))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||||
let mut title_str: BootloaderString = String::new();
|
let mut title_str: BootloaderString = String::new();
|
||||||
unwrap!(title_str.push_str("BOOTLOADER "));
|
unwrap!(title_str.push_str("BOOTLOADER "));
|
||||||
@ -273,11 +317,6 @@ impl BootloaderUI for UICaesar {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_connect(_initial_setup: bool) {
|
|
||||||
let mut frame = Connect::new("Waiting for host...", fonts::FONT_NORMAL, BLD_FG, BLD_BG);
|
|
||||||
show(&mut frame, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_wipe_success() {
|
fn screen_wipe_success() {
|
||||||
let title = Label::centered("Trezor Reset".into(), TEXT_BOLD).vertically_centered();
|
let title = Label::centered("Trezor Reset".into(), TEXT_BOLD).vertically_centered();
|
||||||
|
|
||||||
@ -381,4 +420,14 @@ impl BootloaderUI for UICaesar {
|
|||||||
|
|
||||||
display::refresh();
|
display::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_confirm_pairing(_code: u32, _initial_setup: bool) -> u32 {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_pairing_mode_finalizing(_initial_setup: bool) -> u32 {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
95
core/embed/rust/src/ui/layout_delizia/bootloader/connect.rs
Normal file
95
core/embed/rust/src/ui/layout_delizia/bootloader/connect.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
use crate::{
|
||||||
|
strutil::TString,
|
||||||
|
ui::{
|
||||||
|
component::{Component, Event, EventCtx, Pad},
|
||||||
|
display::{Color, Font},
|
||||||
|
geometry::{Alignment, Offset, Point, Rect},
|
||||||
|
layout_delizia::{
|
||||||
|
component::{Button, ButtonMsg},
|
||||||
|
constant::WIDTH,
|
||||||
|
theme::bootloader::{button_bld, BUTTON_AREA_START, BUTTON_HEIGHT, CONTENT_PADDING},
|
||||||
|
},
|
||||||
|
shape::{self, Renderer},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Copy, Clone, ToPrimitive)]
|
||||||
|
pub enum ConnectMsg {
|
||||||
|
Cancel = 1,
|
||||||
|
PairingMode = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connect {
|
||||||
|
fg: Color,
|
||||||
|
bg: Pad,
|
||||||
|
message: TString<'static>,
|
||||||
|
font: Font,
|
||||||
|
button: Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Connect {
|
||||||
|
pub fn new<T>(message: T, font: Font, fg: Color, bg: Color) -> Self
|
||||||
|
where
|
||||||
|
T: Into<TString<'static>>,
|
||||||
|
{
|
||||||
|
let mut instance = Self {
|
||||||
|
fg,
|
||||||
|
bg: Pad::with_background(bg),
|
||||||
|
message: message.into(),
|
||||||
|
font,
|
||||||
|
button: Button::with_text("Cancel".into())
|
||||||
|
.styled(button_bld())
|
||||||
|
.with_text_align(Alignment::Center),
|
||||||
|
};
|
||||||
|
|
||||||
|
instance.bg.clear();
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Connect {
|
||||||
|
type Msg = ConnectMsg;
|
||||||
|
|
||||||
|
fn place(&mut self, bounds: Rect) -> Rect {
|
||||||
|
self.bg.place(bounds);
|
||||||
|
self.button.place(Rect::new(
|
||||||
|
Point::new(CONTENT_PADDING, BUTTON_AREA_START),
|
||||||
|
Point::new(WIDTH - CONTENT_PADDING, BUTTON_AREA_START + BUTTON_HEIGHT),
|
||||||
|
));
|
||||||
|
|
||||||
|
bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
|
||||||
|
if let Some(ButtonMsg::Clicked) = self.button.event(ctx, event) {
|
||||||
|
return Some(ConnectMsg::Cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||||
|
self.bg.render(target);
|
||||||
|
self.button.render(target);
|
||||||
|
|
||||||
|
self.message.map(|t| {
|
||||||
|
shape::Text::new(
|
||||||
|
self.bg.area.center() + Offset::y(self.font.text_height() / 2),
|
||||||
|
t,
|
||||||
|
self.font,
|
||||||
|
)
|
||||||
|
.with_fg(self.fg)
|
||||||
|
.with_align(Alignment::Center)
|
||||||
|
.render(target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
impl crate::trace::Trace for Connect {
|
||||||
|
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
|
||||||
|
t.component("Connect");
|
||||||
|
t.string("message", self.message);
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,11 @@ use heapless::String;
|
|||||||
use crate::{
|
use crate::{
|
||||||
trezorhal::secbool::secbool,
|
trezorhal::secbool::secbool,
|
||||||
ui::{
|
ui::{
|
||||||
component::{connect::Connect, Label},
|
component::Label,
|
||||||
display::{self, Color, Icon},
|
display::{self, Color, Icon},
|
||||||
geometry::{Alignment, Offset, Point, Rect},
|
geometry::{Alignment, Offset, Point, Rect},
|
||||||
layout::simplified::{run, show},
|
layout::simplified::{run, show},
|
||||||
|
ui_bootloader::BootloaderLayoutType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,6 +44,8 @@ use crate::ui::{
|
|||||||
use ufmt::uwrite;
|
use ufmt::uwrite;
|
||||||
|
|
||||||
use super::theme::bootloader::BLD_WARN_COLOR;
|
use super::theme::bootloader::BLD_WARN_COLOR;
|
||||||
|
use crate::ui::{component::Event, layout::simplified::process_frame_event};
|
||||||
|
use connect::Connect;
|
||||||
use intro::Intro;
|
use intro::Intro;
|
||||||
use menu::Menu;
|
use menu::Menu;
|
||||||
|
|
||||||
@ -50,6 +53,8 @@ pub mod intro;
|
|||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod welcome;
|
pub mod welcome;
|
||||||
|
|
||||||
|
pub mod connect;
|
||||||
|
|
||||||
pub type BootloaderString = String<128>;
|
pub type BootloaderString = String<128>;
|
||||||
|
|
||||||
const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE";
|
const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE";
|
||||||
@ -122,11 +127,53 @@ impl UIDelizia {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BootloaderUI for UIDelizia {
|
#[allow(clippy::large_enum_variant)]
|
||||||
fn screen_welcome() {
|
pub enum BootloaderLayout {
|
||||||
let mut frame = Welcome::new();
|
Welcome(Welcome),
|
||||||
show(&mut frame, true);
|
Menu(Menu),
|
||||||
|
Connect(Connect),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BootloaderLayoutType for BootloaderLayout {
|
||||||
|
fn event(&mut self, event: Option<Event>) -> u32 {
|
||||||
|
match self {
|
||||||
|
BootloaderLayout::Welcome(f) => process_frame_event::<Welcome>(f, event),
|
||||||
|
BootloaderLayout::Menu(f) => process_frame_event::<Menu>(f, event),
|
||||||
|
BootloaderLayout::Connect(f) => process_frame_event::<Connect>(f, event),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self) {
|
||||||
|
match self {
|
||||||
|
BootloaderLayout::Welcome(f) => show(f, true),
|
||||||
|
BootloaderLayout::Menu(f) => show(f, true),
|
||||||
|
BootloaderLayout::Connect(f) => show(f, true),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn init_welcome() -> Self {
|
||||||
|
Self::Welcome(Welcome::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_menu(_initial_setup: bool, firmware_present: secbool) -> Self {
|
||||||
|
Self::Menu(Menu::new(firmware_present))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_connect(_initial_setup: bool, _auto_update: bool) -> Self {
|
||||||
|
Self::Connect(Connect::new(
|
||||||
|
"Waiting for host...",
|
||||||
|
fonts::FONT_DEMIBOLD,
|
||||||
|
BLD_TITLE_COLOR,
|
||||||
|
BLD_BG,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn init_pairing_mode(_initial_setup: bool) -> Self {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl BootloaderUI for UIDelizia {
|
||||||
|
type CLayoutType = BootloaderLayout;
|
||||||
|
|
||||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
||||||
let mut reboot_msg = BootloaderString::new();
|
let mut reboot_msg = BootloaderString::new();
|
||||||
@ -290,10 +337,6 @@ impl BootloaderUI for UIDelizia {
|
|||||||
show(&mut frame, true);
|
show(&mut frame, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
|
||||||
run(&mut Menu::new(firmware_present))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||||
let mut title_str: BootloaderString = String::new();
|
let mut title_str: BootloaderString = String::new();
|
||||||
unwrap!(title_str.push_str("BOOTLOADER "));
|
unwrap!(title_str.push_str("BOOTLOADER "));
|
||||||
@ -357,17 +400,6 @@ impl BootloaderUI for UIDelizia {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn screen_connect(initial_setup: bool) {
|
|
||||||
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
|
||||||
let mut frame = Connect::new(
|
|
||||||
"Waiting for host...",
|
|
||||||
fonts::FONT_DEMIBOLD,
|
|
||||||
BLD_TITLE_COLOR,
|
|
||||||
bg,
|
|
||||||
);
|
|
||||||
show(&mut frame, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_wipe_success() {
|
fn screen_wipe_success() {
|
||||||
let mut frame = ResultScreen::new(
|
let mut frame = ResultScreen::new(
|
||||||
&RESULT_WIPE,
|
&RESULT_WIPE,
|
||||||
@ -475,4 +507,14 @@ impl BootloaderUI for UIDelizia {
|
|||||||
|
|
||||||
display::refresh();
|
display::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_confirm_pairing(_code: u32, _initial_setup: bool) -> u32 {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_pairing_mode_finalizing(_initial_setup: bool) -> u32 {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,20 @@
|
|||||||
#[cfg(feature = "bootloader")]
|
#[cfg(feature = "bootloader")]
|
||||||
use crate::trezorhal::secbool::secbool;
|
use crate::trezorhal::secbool::secbool;
|
||||||
|
use crate::ui::component::Event;
|
||||||
|
|
||||||
|
pub trait BootloaderLayoutType {
|
||||||
|
fn event(&mut self, event: Option<Event>) -> u32;
|
||||||
|
fn show(&mut self);
|
||||||
|
fn init_welcome() -> Self;
|
||||||
|
fn init_menu(initial_setup: bool, firmware_present: secbool) -> Self;
|
||||||
|
fn init_connect(initial_setup: bool, auto_update: bool) -> Self;
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn init_pairing_mode(initial_setup: bool) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bootloader")]
|
#[cfg(feature = "bootloader")]
|
||||||
pub trait BootloaderUI {
|
pub trait BootloaderUI {
|
||||||
fn screen_welcome();
|
type CLayoutType: BootloaderLayoutType;
|
||||||
|
|
||||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool);
|
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool);
|
||||||
|
|
||||||
@ -25,8 +36,6 @@ pub trait BootloaderUI {
|
|||||||
|
|
||||||
fn screen_unlock_bootloader_success();
|
fn screen_unlock_bootloader_success();
|
||||||
|
|
||||||
fn screen_menu(firmware_present: secbool) -> u32;
|
|
||||||
|
|
||||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32;
|
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32;
|
||||||
|
|
||||||
fn screen_boot_stage_1(fading: bool);
|
fn screen_boot_stage_1(fading: bool);
|
||||||
@ -35,8 +44,6 @@ pub trait BootloaderUI {
|
|||||||
|
|
||||||
fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool);
|
fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool);
|
||||||
|
|
||||||
fn screen_connect(initial_setup: bool);
|
|
||||||
|
|
||||||
fn screen_wipe_success();
|
fn screen_wipe_success();
|
||||||
|
|
||||||
fn screen_wipe_fail();
|
fn screen_wipe_fail();
|
||||||
@ -48,4 +55,10 @@ pub trait BootloaderUI {
|
|||||||
vendor_img: &'static [u8],
|
vendor_img: &'static [u8],
|
||||||
wait: i32,
|
wait: i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_confirm_pairing(code: u32, initial_setup: bool) -> u32;
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
|
fn screen_pairing_mode_finalizing(initial_setup: bool) -> u32;
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
#include <io/usb.h>
|
#include <io/usb.h>
|
||||||
#include <rtl/secbool.h>
|
#include <rtl/secbool.h>
|
||||||
#include <sec/entropy.h>
|
#include <sec/entropy.h>
|
||||||
|
#include <sys/sysevent.h>
|
||||||
#include <sys/systick.h>
|
#include <sys/systick.h>
|
||||||
#include <util/flash.h>
|
#include <util/flash.h>
|
||||||
#include <util/translations.h>
|
#include <util/translations.h>
|
||||||
|
#include "rust_types.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
|
||||||
#ifdef USE_HW_JPEG_DECODER
|
#ifdef USE_HW_JPEG_DECODER
|
||||||
|
@ -110,7 +110,7 @@ void rsod_terminal(const systask_postmortem_t* pminfo) {
|
|||||||
|
|
||||||
#ifdef FANCY_FATAL_ERROR
|
#ifdef FANCY_FATAL_ERROR
|
||||||
|
|
||||||
#include "rust_ui.h"
|
#include "rust_ui_common.h"
|
||||||
|
|
||||||
void rsod_gui(const systask_postmortem_t* pminfo) {
|
void rsod_gui(const systask_postmortem_t* pminfo) {
|
||||||
const char* title = RSOD_DEFAULT_TITLE;
|
const char* title = RSOD_DEFAULT_TITLE;
|
||||||
|
Loading…
Reference in New Issue
Block a user