mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-22 18:19:03 +00:00
feat(core): add BLE to bootloader
This commit is contained in:
parent
c00c8487be
commit
03f736d39a
@ -11,7 +11,7 @@ PRODUCTION = 0 if BOOTLOADER_QA else ARGUMENTS.get('PRODUCTION', '0') == '1'
|
||||
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
|
||||
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 = ''
|
||||
CPPPATH_MOD = []
|
||||
@ -105,7 +105,6 @@ SOURCE_BOOTLOADER = [
|
||||
'embed/projects/bootloader/header.S',
|
||||
'embed/projects/bootloader/bootui.c',
|
||||
'embed/projects/bootloader/main.c',
|
||||
'embed/projects/bootloader/poll.c',
|
||||
'embed/projects/bootloader/antiglitch.c',
|
||||
'embed/projects/bootloader/workflow/wf_firmware_update.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_auto_update.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/wire_iface_usb.c',
|
||||
'embed/projects/bootloader/wire/wire_iface_ble.c',
|
||||
'embed/projects/bootloader/protob/protob.c',
|
||||
'embed/projects/bootloader/protob/pb/messages.pb.c',
|
||||
'embed/projects/bootloader/version_check.c',
|
||||
|
@ -99,7 +99,6 @@ SOURCE_NANOPB = [
|
||||
SOURCE_BOOTLOADER = [
|
||||
'embed/projects/bootloader/bootui.c',
|
||||
'embed/projects/bootloader/main.c',
|
||||
'embed/projects/bootloader/poll.c',
|
||||
'embed/projects/bootloader/antiglitch.c',
|
||||
'embed/projects/bootloader/workflow/wf_firmware_update.c',
|
||||
'embed/projects/bootloader/workflow/wf_wipe_device.c',
|
||||
@ -184,6 +183,7 @@ ALLPATHS = ['embed/rust',
|
||||
'embed/gfx/inc',
|
||||
'embed/io/display/inc',
|
||||
'embed/io/usb/inc',
|
||||
'embed/io/poll/inc',
|
||||
'embed/sec/entropy/inc',
|
||||
'embed/sec/monoctr/inc',
|
||||
'embed/sec/random_delays/inc',
|
||||
|
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 "bootui.h"
|
||||
#include "rust_ui.h"
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "version.h"
|
||||
|
||||
#define BACKLIGHT_NORMAL 150
|
||||
@ -47,6 +47,8 @@ static bool initial_setup = true;
|
||||
|
||||
void ui_set_initial_setup(bool initial) { initial_setup = initial; }
|
||||
|
||||
bool ui_get_initial_setup(void) { return initial_setup; }
|
||||
|
||||
#if defined USE_TOUCH
|
||||
#include <io/touch.h>
|
||||
|
||||
@ -97,10 +99,6 @@ void ui_screen_boot(const vendor_header *const vhdr,
|
||||
vimg_len, wait);
|
||||
}
|
||||
|
||||
// welcome UI
|
||||
|
||||
void ui_screen_welcome(void) { screen_welcome(); }
|
||||
|
||||
uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
||||
const image_header *const hdr, bool fw_ok) {
|
||||
char bld_ver[32];
|
||||
@ -111,12 +109,6 @@ uint32_t ui_screen_intro(const vendor_header *const vhdr,
|
||||
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
|
||||
|
||||
ui_result_t ui_screen_install_confirm(const vendor_header *const vhdr,
|
||||
@ -180,3 +172,9 @@ void ui_screen_install_restricted(void) { screen_install_fail(); }
|
||||
void ui_fadein(void) { display_fade(0, BACKLIGHT_NORMAL, 1000); }
|
||||
|
||||
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
|
||||
|
@ -34,6 +34,7 @@ typedef enum {
|
||||
MENU_EXIT = 0xAABBCCDD,
|
||||
MENU_REBOOT = 0x11223344,
|
||||
MENU_WIPE = 0x55667788,
|
||||
MENU_BLUETOOTH = 0x99AABBCC,
|
||||
} menu_result_t;
|
||||
|
||||
// todo: use bindgen to tie this to rust
|
||||
@ -42,6 +43,22 @@ typedef enum {
|
||||
INTRO_HOST = 2,
|
||||
} intro_result_t;
|
||||
|
||||
typedef enum {
|
||||
WAIT_CANCEL = 1,
|
||||
WAIT_PAIRING_MODE = 2,
|
||||
WAIT_MENU = 3,
|
||||
} wait_result_t;
|
||||
|
||||
typedef enum {
|
||||
PAIRING_MODE_CANCEL = 1000000,
|
||||
} pairing_mode_result_t;
|
||||
|
||||
typedef enum {
|
||||
PAIRING_FINALIZATION_COMPLETED = 1,
|
||||
PAIRING_FINALIZATION_CANCEL = 2,
|
||||
PAIRING_FINALIZATION_FAILED = 3,
|
||||
} pairing_mode_finalization_result_t;
|
||||
|
||||
// Displays a warning screen before jumping to the untrusted firmware
|
||||
//
|
||||
// Shows vendor image, vendor string and firmware version
|
||||
@ -60,15 +77,9 @@ void ui_screen_boot(const vendor_header* const vhdr,
|
||||
// the user presses a button, touches the display
|
||||
void ui_click(void);
|
||||
|
||||
void ui_screen_welcome(void);
|
||||
|
||||
uint32_t ui_screen_intro(const vendor_header* const vhdr,
|
||||
const image_header* const hdr, bool fw_ok);
|
||||
|
||||
uint32_t ui_screen_menu(secbool firmware_present);
|
||||
|
||||
void ui_screen_connect(void);
|
||||
|
||||
ui_result_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
||||
const image_header* const hdr,
|
||||
secbool shold_keep_seed,
|
||||
@ -89,9 +100,14 @@ void ui_screen_fail(void);
|
||||
void ui_fadein(void);
|
||||
void ui_fadeout(void);
|
||||
void ui_set_initial_setup(bool initial);
|
||||
bool ui_get_initial_setup(void);
|
||||
|
||||
void ui_screen_boot_stage_1(bool fading);
|
||||
|
||||
#ifdef USE_OPTIGA
|
||||
uint32_t ui_screen_unlock_bootloader_confirm(void);
|
||||
#endif
|
||||
|
||||
#ifdef USE_BLE
|
||||
uint32_t ui_screen_confirm_pairing(uint32_t code);
|
||||
#endif
|
||||
|
@ -59,10 +59,18 @@
|
||||
#ifdef USE_TAMPER
|
||||
#include <sys/tamper.h>
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
#include <io/ble.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_BLE
|
||||
#include "wire/wire_iface_ble.h"
|
||||
#endif
|
||||
|
||||
#include "antiglitch.h"
|
||||
#include "bootui.h"
|
||||
#include "version_check.h"
|
||||
#include "wire/wire_iface_usb.h"
|
||||
#include "workflow/workflow.h"
|
||||
|
||||
#ifdef TREZOR_EMULATOR
|
||||
@ -122,6 +130,9 @@ static void drivers_init(secbool *touch_initialized) {
|
||||
#ifdef USE_RGB_LED
|
||||
rgb_led_init();
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
ble_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void drivers_deinit(void) {
|
||||
@ -381,7 +392,7 @@ int bootloader_main(void) {
|
||||
|
||||
jump_reset();
|
||||
if (header_present == sectrue) {
|
||||
if (auto_upgrade == sectrue) {
|
||||
if (auto_upgrade == sectrue && firmware_present == sectrue) {
|
||||
result = workflow_auto_update(&vhdr, hdr);
|
||||
} else {
|
||||
result = workflow_bootloader(&vhdr, hdr, firmware_present);
|
||||
@ -390,6 +401,11 @@ int bootloader_main(void) {
|
||||
result = workflow_empty_device();
|
||||
}
|
||||
|
||||
usb_iface_deinit();
|
||||
#ifdef USE_BLE
|
||||
ble_iface_deinit();
|
||||
#endif
|
||||
|
||||
switch (result) {
|
||||
case WF_OK_FIRMWARE_INSTALLED:
|
||||
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
|
||||
|
||||
typedef struct {
|
||||
// initialized flag
|
||||
bool initialized;
|
||||
// identifier of the interface used for polling communication events
|
||||
uint8_t poll_iface_id;
|
||||
// size of TX packet
|
||||
|
173
core/embed/projects/bootloader/wire/wire_iface_ble.c
Normal file
173
core/embed/projects/bootloader/wire/wire_iface_ble.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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 false;
|
||||
}
|
||||
if (!is_connected()) {
|
||||
return false;
|
||||
}
|
||||
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_start_pairing(void) {
|
||||
ble_state_t state = {0};
|
||||
|
||||
ble_get_state(&state);
|
||||
|
||||
while (state.connected) {
|
||||
ble_command_t cmd_disconnect = {
|
||||
.cmd_type = BLE_DISCONNECT,
|
||||
};
|
||||
ble_issue_command(&cmd_disconnect);
|
||||
systick_delay_ms(20);
|
||||
ble_get_state(&state);
|
||||
}
|
||||
|
||||
ble_command_t cmd = {
|
||||
.cmd_type = BLE_PAIRING_MODE,
|
||||
.data = {.adv_start =
|
||||
{
|
||||
.name = "Trezor Bootloader",
|
||||
.static_mac = false,
|
||||
}},
|
||||
.data_len = 0,
|
||||
};
|
||||
ble_issue_command(&cmd);
|
||||
}
|
||||
|
||||
#endif
|
@ -19,21 +19,10 @@
|
||||
|
||||
#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
|
||||
#define MODE_WRITE 0x0100
|
||||
void ble_iface_deinit(void);
|
||||
|
||||
typedef enum {
|
||||
EVENT_NONE = 0,
|
||||
EVENT_USB_CAN_READ = 0x01,
|
||||
} poll_event_type_t;
|
||||
|
||||
typedef struct {
|
||||
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);
|
||||
void ble_iface_start_pairing(void);
|
@ -23,19 +23,22 @@
|
||||
#include "wire_iface_usb.h"
|
||||
|
||||
#include <io/usb.h>
|
||||
#include <sys/sysevent.h>
|
||||
|
||||
#define USB_TIMEOUT 500
|
||||
#define USB_PACKET_SIZE 64
|
||||
#define USB_IFACE_NUM 0
|
||||
|
||||
_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) {
|
||||
if (size != USB_PACKET_SIZE) {
|
||||
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;
|
||||
}
|
||||
@ -45,8 +48,8 @@ static int usb_read(uint8_t* buffer, size_t buffer_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int r = usb_webusb_read_blocking(USB_IFACE_NUM, buffer, USB_PACKET_SIZE,
|
||||
USB_TIMEOUT);
|
||||
int r = usb_webusb_read_blocking(SYSHANDLE_USB_IFACE_0, buffer,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -75,7 +78,7 @@ static void usb_init_all(secbool usb21_landing) {
|
||||
static uint8_t rx_buffer[USB_PACKET_SIZE];
|
||||
|
||||
static const usb_webusb_info_t webusb_info = {
|
||||
.iface_num = USB_IFACE_NUM,
|
||||
.iface_num = SYSHANDLE_USB_IFACE_0,
|
||||
#ifdef TREZOR_EMULATOR
|
||||
.emu_port = 21324,
|
||||
#else
|
||||
@ -96,20 +99,35 @@ static void usb_init_all(secbool usb21_landing) {
|
||||
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);
|
||||
|
||||
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->rx_packet_size = USB_PACKET_SIZE;
|
||||
iface->write = &usb_write;
|
||||
iface->read = &usb_read;
|
||||
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));
|
||||
usb_deinit();
|
||||
}
|
||||
|
@ -21,6 +21,6 @@
|
||||
|
||||
#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/>.
|
||||
*/
|
||||
|
||||
#include <antiglitch.h>
|
||||
#include <sys/bootargs.h>
|
||||
#include <trezor_model.h>
|
||||
#include <trezor_rtl.h>
|
||||
|
||||
#include <util/image.h>
|
||||
|
||||
#include "bootui.h"
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "workflow.h"
|
||||
|
||||
workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
||||
@ -30,8 +33,18 @@ workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
||||
ui_set_initial_setup(true);
|
||||
|
||||
workflow_result_t res = WF_CANCELLED;
|
||||
while (res == WF_CANCELLED) {
|
||||
res = workflow_host_control(vhdr, hdr, ui_screen_connect);
|
||||
uint32_t ui_result = WAIT_CANCEL;
|
||||
|
||||
uint8_t buf[1024] = {0};
|
||||
screen_connect(true, false, buf, sizeof(buf));
|
||||
res = workflow_host_control(vhdr, hdr, buf, sizeof(buf), &ui_result);
|
||||
|
||||
if (res == WF_OK_UI_ACTION && ui_result == WAIT_CANCEL) {
|
||||
bootargs_set(BOOT_COMMAND_NONE, NULL, 0);
|
||||
jump_allow_1();
|
||||
jump_allow_2();
|
||||
return WF_OK_REBOOT_SELECTED;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
131
core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
Normal file
131
core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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 < 0 || 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;
|
||||
}
|
||||
|
||||
static void end_pairing_mode(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);
|
||||
}
|
||||
}
|
||||
|
||||
workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
ble_iface_start_pairing();
|
||||
|
||||
uint8_t buf[1024] = {0};
|
||||
screen_pairing_mode(ui_get_initial_setup(), buf, sizeof(buf));
|
||||
|
||||
uint32_t code = 0;
|
||||
workflow_result_t res =
|
||||
workflow_host_control(vhdr, hdr, buf, sizeof(buf), &code);
|
||||
|
||||
if (res != WF_OK_UI_ACTION) {
|
||||
end_pairing_mode();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (code == PAIRING_MODE_CANCEL) {
|
||||
end_pairing_mode();
|
||||
return WF_OK_PAIRING_FAILED;
|
||||
}
|
||||
|
||||
uint32_t result = ui_screen_confirm_pairing(code);
|
||||
|
||||
uint8_t pairing_code[BLE_PAIRING_CODE_LEN] = {0};
|
||||
|
||||
if (result != UI_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) {
|
||||
end_pairing_mode();
|
||||
return WF_OK_PAIRING_FAILED;
|
||||
}
|
||||
if (r == PAIRING_FINALIZATION_CANCEL) {
|
||||
ble_command_t disconnect = {.cmd_type = BLE_DISCONNECT};
|
||||
ble_issue_command(&disconnect);
|
||||
end_pairing_mode();
|
||||
return WF_OK_PAIRING_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return WF_OK_PAIRING_COMPLETED;
|
||||
}
|
||||
|
||||
#endif
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "antiglitch.h"
|
||||
#include "bootui.h"
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "workflow.h"
|
||||
|
||||
typedef enum {
|
||||
@ -33,6 +34,46 @@ typedef enum {
|
||||
SCREEN_WAIT_FOR_HOST,
|
||||
} screen_t;
|
||||
|
||||
workflow_result_t workflow_menu(const vendor_header *const vhdr,
|
||||
const image_header *const hdr,
|
||||
secbool firmware_present) {
|
||||
while (true) {
|
||||
uint8_t buf[1024];
|
||||
screen_menu(ui_get_initial_setup(), firmware_present, buf, sizeof(buf));
|
||||
uint32_t ui_result = 0;
|
||||
workflow_result_t result =
|
||||
workflow_host_control(vhdr, hdr, buf, sizeof(buf), &ui_result);
|
||||
|
||||
if (result != WF_OK_UI_ACTION) {
|
||||
return result;
|
||||
}
|
||||
|
||||
menu_result_t menu_result = (menu_result_t)ui_result;
|
||||
|
||||
if (menu_result == MENU_EXIT) { // exit menu
|
||||
return WF_OK;
|
||||
}
|
||||
#ifdef USE_BLE
|
||||
if (menu_result == MENU_BLUETOOTH) {
|
||||
workflow_ble_pairing_request(vhdr, hdr);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (menu_result == MENU_REBOOT) { // reboot
|
||||
jump_allow_1();
|
||||
jump_allow_2();
|
||||
return WF_OK_REBOOT_SELECTED;
|
||||
}
|
||||
if (menu_result == MENU_WIPE) { // wipe
|
||||
workflow_result_t r = workflow_wipe_device(NULL);
|
||||
if (r == WF_ERROR || r == WF_OK_DEVICE_WIPED || r == WF_CANCELLED) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return WF_ERROR_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
workflow_result_t workflow_bootloader(const vendor_header *const vhdr,
|
||||
const image_header *const hdr,
|
||||
secbool firmware_present) {
|
||||
@ -52,37 +93,50 @@ workflow_result_t workflow_bootloader(const vendor_header *const vhdr,
|
||||
}
|
||||
} break;
|
||||
case SCREEN_MENU: {
|
||||
menu_result_t menu_result = ui_screen_menu(firmware_present);
|
||||
if (menu_result == MENU_EXIT) { // exit menu
|
||||
workflow_result_t res = workflow_menu(vhdr, hdr, firmware_present);
|
||||
if (res == WF_OK) {
|
||||
screen = SCREEN_INTRO;
|
||||
continue;
|
||||
}
|
||||
if (menu_result == MENU_REBOOT) { // reboot
|
||||
#ifndef USE_HASH_PROCESSOR
|
||||
ui_screen_boot_stage_1(true);
|
||||
#endif
|
||||
jump_allow_1();
|
||||
jump_allow_2();
|
||||
return WF_OK_REBOOT_SELECTED;
|
||||
}
|
||||
if (menu_result == MENU_WIPE) { // wipe
|
||||
workflow_result_t r = workflow_wipe_device(NULL);
|
||||
if (r == WF_ERROR) {
|
||||
return r;
|
||||
}
|
||||
if (r == WF_OK_DEVICE_WIPED) {
|
||||
return r;
|
||||
}
|
||||
if (r == WF_CANCELLED) {
|
||||
screen = SCREEN_MENU;
|
||||
continue;
|
||||
}
|
||||
return WF_ERROR_FATAL;
|
||||
if (res == WF_CANCELLED) {
|
||||
screen = SCREEN_MENU;
|
||||
continue;
|
||||
}
|
||||
return res;
|
||||
} break;
|
||||
case SCREEN_WAIT_FOR_HOST: {
|
||||
uint8_t buf[1024] = {0};
|
||||
uint32_t ui_result = 0;
|
||||
screen_connect(false, true, buf, sizeof(buf));
|
||||
workflow_result_t res =
|
||||
workflow_host_control(vhdr, hdr, ui_screen_connect);
|
||||
workflow_host_control(vhdr, hdr, buf, sizeof(buf), &ui_result);
|
||||
switch (res) {
|
||||
case WF_OK_UI_ACTION:
|
||||
switch (ui_result) {
|
||||
case WAIT_CANCEL:
|
||||
screen = SCREEN_INTRO;
|
||||
break;
|
||||
#ifdef USE_BLE
|
||||
case WAIT_PAIRING_MODE:
|
||||
res = workflow_ble_pairing_request(vhdr, hdr);
|
||||
if (res == WF_OK_PAIRING_COMPLETED ||
|
||||
res == WF_OK_PAIRING_FAILED) {
|
||||
screen = SCREEN_WAIT_FOR_HOST;
|
||||
break;
|
||||
}
|
||||
if (res == WF_CANCELLED) {
|
||||
screen = SCREEN_INTRO;
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
#endif
|
||||
case WAIT_MENU:
|
||||
screen = SCREEN_MENU;
|
||||
break;
|
||||
default:
|
||||
return WF_ERROR_FATAL;
|
||||
}
|
||||
continue;
|
||||
case WF_CANCELLED:
|
||||
screen = SCREEN_INTRO;
|
||||
continue;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#endif
|
||||
|
||||
#include "bootui.h"
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "workflow.h"
|
||||
|
||||
workflow_result_t workflow_empty_device(void) {
|
||||
@ -50,8 +51,34 @@ workflow_result_t workflow_empty_device(void) {
|
||||
#endif
|
||||
|
||||
workflow_result_t res = WF_CANCELLED;
|
||||
while (res == WF_CANCELLED) {
|
||||
res = workflow_host_control(NULL, NULL, ui_screen_welcome);
|
||||
uint32_t ui_result = WAIT_CANCEL;
|
||||
while (res == WF_CANCELLED ||
|
||||
(res == WF_OK_UI_ACTION && ui_result == WAIT_CANCEL)) {
|
||||
uint8_t buf[1024] = {0};
|
||||
screen_welcome(buf, sizeof(buf));
|
||||
res = workflow_host_control(NULL, NULL, buf, sizeof(buf), &ui_result);
|
||||
#ifdef USE_BLE
|
||||
if (res == WF_OK_UI_ACTION && ui_result == WAIT_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 = WAIT_CANCEL;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (res == WF_OK_UI_ACTION && ui_result == WAIT_MENU) {
|
||||
do {
|
||||
res = workflow_menu(NULL, NULL, false);
|
||||
} while (res == WF_CANCELLED);
|
||||
|
||||
if (res == WF_OK) {
|
||||
res = WF_CANCELLED;
|
||||
ui_result = WAIT_CANCEL;
|
||||
continue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -29,8 +29,6 @@
|
||||
#include <sec/secret.h>
|
||||
#endif
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include "bootui.h"
|
||||
#include "protob/protob.h"
|
||||
#include "version_check.h"
|
||||
@ -519,11 +517,14 @@ workflow_result_t workflow_firmware_update(protob_io_t *iface) {
|
||||
upload_status_t s = UPLOAD_IN_PROGRESS;
|
||||
|
||||
while (true) {
|
||||
uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ};
|
||||
poll_event_t e = {0};
|
||||
uint8_t i = poll_events(ifaces, 1, &e, 100);
|
||||
sysevents_t awaited = {0};
|
||||
sysevents_t signalled = {0};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -17,57 +17,101 @@
|
||||
* 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_rtl.h>
|
||||
|
||||
#include <sys/sysevent.h>
|
||||
#include <sys/systick.h>
|
||||
#include <sys/types.h>
|
||||
#include <util/image.h>
|
||||
|
||||
#include "antiglitch.h"
|
||||
#include "poll.h"
|
||||
#include "protob/protob.h"
|
||||
#include "wire/wire_iface_usb.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,
|
||||
const image_header *const hdr,
|
||||
void (*redraw_wait_screen)(void)) {
|
||||
wire_iface_t usb_iface = {0};
|
||||
uint8_t *wait_layout,
|
||||
size_t wait_layout_len,
|
||||
uint32_t *ui_action_result) {
|
||||
protob_io_t protob_usb_iface = {0};
|
||||
|
||||
redraw_wait_screen();
|
||||
|
||||
// if both are NULL, we don't have a firmware installed
|
||||
// let's show a webusb landing page in this case
|
||||
usb_iface_init(&usb_iface,
|
||||
(vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
|
||||
wire_iface_t *usb_iface =
|
||||
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;
|
||||
|
||||
for (;;) {
|
||||
uint16_t ifaces[1] = {protob_get_iface_flag(&protob_usb_iface) | MODE_READ};
|
||||
poll_event_t e = {0};
|
||||
sysevents_t awaited = {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;
|
||||
protob_io_t *active_iface = NULL;
|
||||
|
||||
switch (e.type) {
|
||||
case EVENT_USB_CAN_READ:
|
||||
if (i == protob_get_iface_flag(&protob_usb_iface) &&
|
||||
sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) {
|
||||
active_iface = &protob_usb_iface;
|
||||
} else {
|
||||
continue;
|
||||
if (signalled.read_ready ==
|
||||
(1 << protob_get_iface_flag(&protob_usb_iface)) &&
|
||||
sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) {
|
||||
active_iface = &protob_usb_iface;
|
||||
}
|
||||
|
||||
#ifdef USE_BLE
|
||||
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, wait_layout_len, &signalled);
|
||||
|
||||
if (res != 0) {
|
||||
if (ui_action_result != NULL) {
|
||||
*ui_action_result = res;
|
||||
}
|
||||
break;
|
||||
case EVENT_NONE:
|
||||
default:
|
||||
continue;
|
||||
result = WF_OK_UI_ACTION;
|
||||
goto exit_host_control;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (msg_id) {
|
||||
@ -109,6 +153,9 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||
|
||||
exit_host_control:
|
||||
systick_delay_ms(100);
|
||||
usb_iface_deinit(&usb_iface);
|
||||
usb_iface_deinit();
|
||||
#ifdef USE_BLE
|
||||
ble_iface_init();
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "bootui.h"
|
||||
#include "protob.h"
|
||||
#include "rust_ui.h"
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "workflow.h"
|
||||
|
||||
workflow_result_t workflow_unlock_bootloader(protob_io_t *iface) {
|
||||
|
@ -22,16 +22,67 @@
|
||||
|
||||
#include <util/flash_utils.h>
|
||||
|
||||
#ifdef USE_BLE
|
||||
#include <io/ble.h>
|
||||
#endif
|
||||
|
||||
#include <sys/systick.h>
|
||||
|
||||
#include "bootui.h"
|
||||
#include "protob.h"
|
||||
#include "rust_ui.h"
|
||||
#include "rust_ui_bootloader.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;
|
||||
if (iface != NULL) {
|
||||
recv_msg_wipe_device(iface, &msg_recv);
|
||||
}
|
||||
|
||||
ui_result_t response = ui_screen_wipe_confirm();
|
||||
if (UI_RESULT_CONFIRM != response) {
|
||||
if (iface != NULL) {
|
||||
@ -42,11 +93,14 @@ workflow_result_t workflow_wipe_device(protob_io_t *iface) {
|
||||
ui_screen_wipe();
|
||||
secbool wipe_result = erase_device(ui_screen_wipe_progress);
|
||||
|
||||
#ifdef USE_BLE
|
||||
if (!wipe_bonds(iface)) {
|
||||
return WF_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sectrue != wipe_result) {
|
||||
if (iface != NULL) {
|
||||
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||
"Could not erase flash");
|
||||
}
|
||||
send_error_conditionally(iface, "Could not erase flash");
|
||||
screen_wipe_fail();
|
||||
return WF_ERROR;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <sys/sysevent.h>
|
||||
#include <util/image.h>
|
||||
|
||||
#include "protob/protob.h"
|
||||
@ -33,6 +34,9 @@ typedef enum {
|
||||
WF_OK_FIRMWARE_INSTALLED = 0x04D9D07F,
|
||||
WF_OK_DEVICE_WIPED = 0x30DC3841,
|
||||
WF_OK_BOOTLOADER_UNLOCKED = 0x23FCBD03,
|
||||
WF_OK_UI_ACTION = 0xAABBCCEE,
|
||||
WF_OK_PAIRING_COMPLETED = 0xAABBCCEF,
|
||||
WF_OK_PAIRING_FAILED = 0xAABBCCF0,
|
||||
WF_CANCELLED = 0x55667788,
|
||||
} workflow_result_t;
|
||||
|
||||
@ -54,6 +58,9 @@ workflow_result_t workflow_get_features(protob_io_t *iface,
|
||||
const vendor_header *const vhdr,
|
||||
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,
|
||||
const image_header *const hdr,
|
||||
secbool firmware_present);
|
||||
@ -62,7 +69,14 @@ workflow_result_t workflow_empty_device(void);
|
||||
|
||||
workflow_result_t workflow_host_control(const vendor_header *const vhdr,
|
||||
const image_header *const hdr,
|
||||
void (*redraw_wait_screen)(void));
|
||||
uint8_t *wait_layout,
|
||||
size_t wait_layout_len,
|
||||
uint32_t *ui_action_result);
|
||||
|
||||
workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
|
||||
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../sec/entropy/inc",
|
||||
"-I../sys/time/inc",
|
||||
"-I../sys/task/inc",
|
||||
"-I../util/flash/inc",
|
||||
"-I../util/translations/inc",
|
||||
"-I../models",
|
||||
@ -402,13 +403,16 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("usb_get_state")
|
||||
// ble
|
||||
.allowlist_function("ble_get_state")
|
||||
.allowlist_function("ble_get_event")
|
||||
.allowlist_function("ble_issue_command")
|
||||
.allowlist_type("ble_command_t")
|
||||
.allowlist_type("ble_state_t")
|
||||
.allowlist_type("ble_event_t")
|
||||
// touch
|
||||
.allowlist_function("touch_get_event")
|
||||
// button
|
||||
.allowlist_type("button_t")
|
||||
.allowlist_type("button_event_t")
|
||||
.allowlist_function("button_get_event")
|
||||
// haptic
|
||||
.allowlist_type("haptic_effect_t")
|
||||
@ -426,7 +430,11 @@ fn generate_trezorhal_bindings() {
|
||||
.allowlist_function("jpegdec_process")
|
||||
.allowlist_function("jpegdec_get_info")
|
||||
.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");
|
||||
|
||||
// Write the bindings to a file in the OUR_DIR.
|
||||
bindings
|
||||
|
@ -1,2 +0,0 @@
|
||||
#include "rust_ui_bootloader.h"
|
||||
#include "rust_ui_common.h"
|
@ -1,5 +1,9 @@
|
||||
#include <trezor_types.h>
|
||||
|
||||
#include <sys/sysevent.h>
|
||||
|
||||
uint32_t screen_event(uint8_t* mem, size_t mem_len, sysevents_t* signalled);
|
||||
|
||||
uint32_t screen_install_confirm(const char* vendor_str, uint8_t vendor_str_len,
|
||||
const char* version_str,
|
||||
const uint8_t* fingerprint,
|
||||
@ -12,14 +16,16 @@ void screen_wipe_progress(int16_t progress, bool initialize);
|
||||
uint32_t screen_intro(const char* bld_version_str, const char* vendor_str,
|
||||
uint8_t vendor_str_len, const char* version_str,
|
||||
bool fw_ok);
|
||||
uint32_t screen_menu(secbool firmware_present);
|
||||
void screen_connect(bool initial_setup);
|
||||
void screen_menu(bool initial_setup, secbool firmware_present, uint8_t* mem,
|
||||
size_t mem_len);
|
||||
void screen_connect(bool initial_setup, bool auto_update, uint8_t* mem,
|
||||
size_t mem_len);
|
||||
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_welcome(void);
|
||||
void screen_welcome(uint8_t* mem, size_t mem_len);
|
||||
void screen_boot_stage_1(bool fading);
|
||||
uint32_t screen_unlock_bootloader_confirm(void);
|
||||
void screen_unlock_bootloader_success(void);
|
||||
@ -27,3 +33,9 @@ void bld_continue_label(uint16_t bg_color);
|
||||
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);
|
||||
|
||||
uint32_t screen_pairing_mode(bool initial_setup, uint8_t* mem, size_t mem_len);
|
||||
|
||||
uint32_t screen_confirm_pairing(uint32_t code, bool initial_setup);
|
||||
|
||||
uint32_t screen_pairing_mode_finalizing(bool initial_setup);
|
||||
|
@ -1,4 +1,24 @@
|
||||
use super::ffi;
|
||||
use crate::ui::event::BLEEvent;
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connected() -> bool {
|
||||
unsafe {
|
||||
|
@ -17,6 +17,13 @@ pub enum PhysicalButtonEvent {
|
||||
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)> {
|
||||
unsafe {
|
||||
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,
|
||||
};
|
||||
if ffi::button_get_event(&mut e as _) {
|
||||
Some((
|
||||
unwrap!(PhysicalButton::from_u8(e.button as _)),
|
||||
unwrap!(PhysicalButtonEvent::from_u8(e.event_type as _)),
|
||||
))
|
||||
Some(button_parse_event(e))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -35,3 +35,6 @@ pub mod wordlist;
|
||||
pub mod secbool;
|
||||
|
||||
pub mod time;
|
||||
|
||||
#[cfg(feature = "ui")]
|
||||
pub mod sysevent;
|
||||
|
84
core/embed/rust/src/trezorhal/sysevent.rs
Normal file
84
core/embed/rust/src/trezorhal/sysevent.rs
Normal file
@ -0,0 +1,84 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use super::ffi;
|
||||
use core::mem;
|
||||
|
||||
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 _,
|
||||
}
|
||||
|
||||
pub fn parse_event(signalled: &sysevents_t) -> Option<Event> {
|
||||
#[cfg(feature = "ble")]
|
||||
if signalled.read_ready & (1 << ffi::syshandle_t_SYSHANDLE_BLE) != 0 {
|
||||
let mut ble_event: ffi::ble_event_t = unsafe { mem::zeroed() };
|
||||
let event_available = unsafe { ffi::ble_get_event(&mut ble_event) };
|
||||
if event_available {
|
||||
let ble_event = ble_parse_event(ble_event);
|
||||
return Some(Event::BLE(ble_event));
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "button")]
|
||||
if signalled.read_ready & (1 << ffi::syshandle_t_SYSHANDLE_BUTTON) != 0 {
|
||||
let mut button_event: ffi::button_event_t = unsafe { mem::zeroed() };
|
||||
let event_available = unsafe { button_get_event(&mut button_event) };
|
||||
if event_available {
|
||||
let (btn, evt) = button_parse_event(button_event);
|
||||
return Some(Event::Button(unwrap!(ButtonEvent::new(evt, btn))));
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "touch")]
|
||||
if signalled.read_ready & (1 << ffi::syshandle_t_SYSHANDLE_TOUCH) != 0 {
|
||||
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 mut awaited: sysevents_t = unsafe { mem::zeroed() };
|
||||
|
||||
for i in ifaces {
|
||||
let bit: u32 = 1 << *i as u32;
|
||||
awaited.read_ready |= bit;
|
||||
}
|
||||
|
||||
let mut signalled: sysevents_t = unsafe { mem::zeroed() };
|
||||
|
||||
unsafe { ffi::sysevents_poll(&awaited as _, &mut signalled as _, 100) };
|
||||
|
||||
parse_event(&signalled)
|
||||
}
|
@ -1,16 +1,31 @@
|
||||
use crate::{
|
||||
strutil::hexlify,
|
||||
trezorhal::secbool::secbool,
|
||||
trezorhal::{
|
||||
secbool::secbool,
|
||||
sysevent::{parse_event, sysevents_t},
|
||||
},
|
||||
ui::{
|
||||
ui_bootloader::BootloaderUI,
|
||||
util::{from_c_array, from_c_str},
|
||||
ModelUI,
|
||||
},
|
||||
};
|
||||
use core::slice;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_welcome() {
|
||||
ModelUI::screen_welcome();
|
||||
extern "C" fn screen_event(mem: *mut u8, mem_size: usize, signalled: &sysevents_t) -> u32 {
|
||||
let buf = unsafe { slice::from_raw_parts_mut(mem, mem_size) };
|
||||
|
||||
let e = parse_event(signalled);
|
||||
|
||||
ModelUI::screen_event(buf, e)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_welcome(mem: *mut u8, mem_size: usize) {
|
||||
let buf = unsafe { slice::from_raw_parts_mut(mem, mem_size) };
|
||||
|
||||
ModelUI::screen_welcome(buf);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -75,8 +90,15 @@ extern "C" fn screen_unlock_bootloader_success() {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
ModelUI::screen_menu(firmware_present)
|
||||
extern "C" fn screen_menu(
|
||||
initial_setup: bool,
|
||||
firmware_present: secbool,
|
||||
mem: *mut u8,
|
||||
mem_size: usize,
|
||||
) {
|
||||
let buf = unsafe { slice::from_raw_parts_mut(mem, mem_size) };
|
||||
|
||||
ModelUI::screen_menu(initial_setup, firmware_present, buf);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -131,8 +153,15 @@ extern "C" fn screen_install_progress(progress: u16, initialize: bool, initial_s
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_connect(initial_setup: bool) {
|
||||
ModelUI::screen_connect(initial_setup)
|
||||
extern "C" fn screen_connect(
|
||||
initial_setup: bool,
|
||||
auto_update: bool,
|
||||
mem: *mut u8,
|
||||
mem_size: usize,
|
||||
) {
|
||||
let buf = unsafe { slice::from_raw_parts_mut(mem, mem_size) };
|
||||
|
||||
ModelUI::screen_connect(initial_setup, auto_update, buf)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -144,3 +173,23 @@ extern "C" fn screen_wipe_success() {
|
||||
extern "C" fn 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, mem: *mut u8, mem_size: usize) {
|
||||
let buf = unsafe { slice::from_raw_parts_mut(mem, mem_size) };
|
||||
|
||||
ModelUI::screen_pairing_mode(initial_setup, buf);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_pairing_mode_finalizing(initial_setup: bool) {
|
||||
ModelUI::screen_pairing_mode_finalizing(initial_setup);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ pub mod obj;
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
pub mod result;
|
||||
|
||||
pub mod simplified;
|
||||
|
||||
#[cfg(feature = "micropython")]
|
||||
|
@ -1,19 +1,23 @@
|
||||
#[cfg(feature = "button")]
|
||||
use crate::trezorhal::button::button_get_event;
|
||||
#[cfg(feature = "touch")]
|
||||
use crate::trezorhal::touch::touch_get_event;
|
||||
#[cfg(feature = "button")]
|
||||
use crate::ui::event::ButtonEvent;
|
||||
#[cfg(feature = "touch")]
|
||||
use crate::ui::event::TouchEvent;
|
||||
use crate::ui::{
|
||||
component::{Component, Event, EventCtx, Never},
|
||||
component::{Component, EventCtx, Never},
|
||||
display, CommonUI, ModelUI,
|
||||
};
|
||||
use core::{
|
||||
mem::{align_of, size_of, MaybeUninit},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use crate::ui::{component::Event, display::color::Color, shape::render_on_display};
|
||||
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 {
|
||||
fn return_to_c(self) -> u32;
|
||||
@ -57,7 +61,7 @@ pub fn touch_unpack(event: u32) -> Option<TouchEvent> {
|
||||
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();
|
||||
render_on_display(None, Some(Color::black()), |target| {
|
||||
frame.render(target);
|
||||
@ -65,26 +69,98 @@ fn render(frame: &mut impl Component) {
|
||||
display::refresh();
|
||||
}
|
||||
|
||||
fn convert_layout<A>(buf: &mut [u8]) -> *mut MaybeUninit<A> {
|
||||
let required_size = size_of::<A>();
|
||||
let required_align = align_of::<A>();
|
||||
|
||||
let ptr = buf.as_mut_ptr();
|
||||
let addr = ptr as usize;
|
||||
|
||||
// Check alignment and size
|
||||
if addr % required_align != 0 || buf.len() < required_size {
|
||||
panic!("Buffer is not aligned or too small");
|
||||
}
|
||||
|
||||
// SAFETY: we have just verified alignment and size
|
||||
ptr as *mut MaybeUninit<A>
|
||||
}
|
||||
|
||||
pub fn get_layout<A>(buf: &mut [u8]) -> &mut A {
|
||||
let required_size = size_of::<A>();
|
||||
let required_align = align_of::<A>();
|
||||
|
||||
let ptr = buf.as_mut_ptr();
|
||||
let addr = ptr as usize;
|
||||
|
||||
// Check alignment and size
|
||||
if addr % required_align != 0 || buf.len() < required_size {
|
||||
panic!("Buffer is not aligned or too small");
|
||||
}
|
||||
|
||||
// SAFETY: we have just verified alignment and size
|
||||
let layout = ptr as *mut MaybeUninit<A>;
|
||||
|
||||
unsafe { &mut *layout.cast::<A>() }
|
||||
}
|
||||
|
||||
pub fn init_layout<A>(buf: &mut [u8], frame: A) -> &mut A {
|
||||
let frame_ptr: *mut MaybeUninit<A> = convert_layout(buf);
|
||||
|
||||
unsafe {
|
||||
// Construct in-place
|
||||
ptr::write(frame_ptr as *mut A, frame);
|
||||
|
||||
// Get mutable reference to initialized object
|
||||
&mut *frame_ptr.cast::<A>()
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
frame.place(ModelUI::SCREEN);
|
||||
ModelUI::fadeout();
|
||||
render(frame);
|
||||
ModelUI::fadein();
|
||||
|
||||
// flush any pending events
|
||||
#[cfg(feature = "button")]
|
||||
while button_eval().is_some() {}
|
||||
|
||||
let mut ifaces: Vec<Syshandle, 16> = Vec::new();
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
unwrap!(ifaces.push(Syshandle::Ble));
|
||||
|
||||
#[cfg(feature = "button")]
|
||||
unwrap!(ifaces.push(Syshandle::Button));
|
||||
|
||||
#[cfg(feature = "touch")]
|
||||
unwrap!(ifaces.push(Syshandle::Touch));
|
||||
|
||||
loop {
|
||||
#[cfg(all(feature = "button", not(feature = "touch")))]
|
||||
let event = button_eval();
|
||||
#[cfg(feature = "touch")]
|
||||
let event = touch_unpack(touch_get_event());
|
||||
let event = sysevents_poll(ifaces.as_slice());
|
||||
|
||||
if let Some(e) = event {
|
||||
let mut ctx = EventCtx::new();
|
||||
#[cfg(all(feature = "button", not(feature = "touch")))]
|
||||
let msg = frame.event(&mut ctx, Event::Button(e));
|
||||
#[cfg(feature = "touch")]
|
||||
let msg = frame.event(&mut ctx, Event::Touch(e));
|
||||
|
||||
let msg = frame.event(&mut ctx, e);
|
||||
|
||||
if let Some(message) = msg {
|
||||
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::{
|
||||
strutil::TString,
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
component::{Component, Event, EventCtx, Label, Pad},
|
||||
constant::screen,
|
||||
display::Icon,
|
||||
geometry::{Alignment, Insets, Point, Rect},
|
||||
@ -27,29 +27,27 @@ pub enum IntroMsg {
|
||||
|
||||
pub struct Intro<'a> {
|
||||
bg: Pad,
|
||||
title: Child<Label<'a>>,
|
||||
menu: Child<Button>,
|
||||
host: Child<Button>,
|
||||
text: Child<Label<'a>>,
|
||||
warn: Option<Child<Label<'a>>>,
|
||||
title: Label<'a>,
|
||||
menu: Button,
|
||||
host: Button,
|
||||
text: Label<'a>,
|
||||
warn: Option<Label<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Intro<'a> {
|
||||
pub fn new(title: TString<'a>, content: TString<'a>, fw_ok: bool) -> Self {
|
||||
Self {
|
||||
bg: Pad::with_background(BLD_BG).with_clear(),
|
||||
title: Child::new(Label::left_aligned(title, text_title(BLD_BG)).vertically_centered()),
|
||||
menu: Child::new(
|
||||
Button::with_icon(Icon::new(MENU32))
|
||||
.styled(button_bld_menu())
|
||||
.with_expanded_touch_area(Insets::uniform(13)),
|
||||
),
|
||||
host: Child::new(Button::with_text("INSTALL FIRMWARE".into()).styled(button_bld())),
|
||||
text: Child::new(Label::left_aligned(content, TEXT_NORMAL).vertically_centered()),
|
||||
warn: (!fw_ok).then_some(Child::new(
|
||||
title: Label::left_aligned(title, text_title(BLD_BG)).vertically_centered(),
|
||||
menu: Button::with_icon(Icon::new(MENU32))
|
||||
.styled(button_bld_menu())
|
||||
.with_expanded_touch_area(Insets::uniform(13)),
|
||||
host: Button::with_text("INSTALL FIRMWARE".into()).styled(button_bld()),
|
||||
text: Label::left_aligned(content, TEXT_NORMAL).vertically_centered(),
|
||||
warn: (!fw_ok).then_some(
|
||||
Label::new("FIRMWARE CORRUPTED".into(), Alignment::Start, TEXT_WARNING)
|
||||
.vertically_centered(),
|
||||
)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
trezorhal::secbool::{secbool, sectrue},
|
||||
ui::{
|
||||
component::{Child, Component, Event, EventCtx, Label, Pad},
|
||||
component::{Component, Event, EventCtx, Label, Pad},
|
||||
constant::{screen, WIDTH},
|
||||
display::Icon,
|
||||
geometry::{Insets, Point, Rect},
|
||||
@ -26,37 +26,39 @@ pub enum MenuMsg {
|
||||
Close = 0xAABBCCDD,
|
||||
Reboot = 0x11223344,
|
||||
FactoryReset = 0x55667788,
|
||||
Bluetooth = 0x99AABBCC,
|
||||
}
|
||||
|
||||
pub struct Menu {
|
||||
bg: Pad,
|
||||
title: Child<Label<'static>>,
|
||||
close: Child<Button>,
|
||||
reboot: Child<Button>,
|
||||
reset: Child<Button>,
|
||||
title: Label<'static>,
|
||||
close: Button,
|
||||
reboot: Button,
|
||||
reset: Button,
|
||||
bluetooth: Button,
|
||||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn new(firmware_present: secbool) -> Self {
|
||||
let content_reboot = IconText::new("REBOOT TREZOR", Icon::new(REFRESH24));
|
||||
let content_reset = IconText::new("FACTORY RESET", Icon::new(FIRE24));
|
||||
let content_bluetooth = IconText::new("BLUETOOTH", Icon::new(FIRE24));
|
||||
|
||||
let mut instance = Self {
|
||||
bg: Pad::with_background(BLD_BG),
|
||||
title: Child::new(
|
||||
Label::left_aligned("BOOTLOADER".into(), text_title(BLD_BG)).vertically_centered(),
|
||||
),
|
||||
close: Child::new(
|
||||
Button::with_icon(Icon::new(X32))
|
||||
.styled(button_bld_menu())
|
||||
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
||||
),
|
||||
reboot: Child::new(
|
||||
Button::with_icon_and_text(content_reboot)
|
||||
.styled(button_bld())
|
||||
.initially_enabled(sectrue == firmware_present),
|
||||
),
|
||||
reset: Child::new(Button::with_icon_and_text(content_reset).styled(button_bld())),
|
||||
title: Label::left_aligned("BOOTLOADER".into(), text_title(BLD_BG))
|
||||
.vertically_centered(),
|
||||
|
||||
close: Button::with_icon(Icon::new(X32))
|
||||
.styled(button_bld_menu())
|
||||
.with_expanded_touch_area(Insets::uniform(CORNER_BUTTON_TOUCH_EXPANSION)),
|
||||
|
||||
reboot: Button::with_icon_and_text(content_reboot)
|
||||
.styled(button_bld())
|
||||
.initially_enabled(sectrue == firmware_present),
|
||||
|
||||
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
|
||||
@ -84,6 +86,16 @@ impl Component for Menu {
|
||||
BUTTON_AREA_START + 2 * BUTTON_HEIGHT + BUTTON_SPACING,
|
||||
),
|
||||
));
|
||||
self.bluetooth.place(Rect::new(
|
||||
Point::new(
|
||||
CONTENT_PADDING,
|
||||
BUTTON_AREA_START + 2 * BUTTON_HEIGHT + 2 * BUTTON_SPACING,
|
||||
),
|
||||
Point::new(
|
||||
WIDTH - CONTENT_PADDING,
|
||||
BUTTON_AREA_START + 3 * BUTTON_HEIGHT + 2 * BUTTON_SPACING,
|
||||
),
|
||||
));
|
||||
bounds
|
||||
}
|
||||
|
||||
@ -97,6 +109,9 @@ impl Component for Menu {
|
||||
if let Some(Clicked) = self.reset.event(ctx, event) {
|
||||
return Some(Self::Msg::FactoryReset);
|
||||
}
|
||||
if let Some(Clicked) = self.bluetooth.event(ctx, event) {
|
||||
return Some(Self::Msg::Bluetooth);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
@ -107,5 +122,6 @@ impl Component for Menu {
|
||||
self.close.render(target);
|
||||
self.reboot.render(target);
|
||||
self.reset.render(target);
|
||||
self.bluetooth.render(target);
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,58 @@
|
||||
use heapless::String;
|
||||
pub mod connect;
|
||||
pub mod intro;
|
||||
pub mod menu;
|
||||
pub mod welcome;
|
||||
|
||||
use crate::{
|
||||
trezorhal::secbool::secbool,
|
||||
ui::{
|
||||
component::{connect::Connect, Label},
|
||||
display::{self, Color, Icon},
|
||||
geometry::{Point, Rect},
|
||||
layout::simplified::{run, show},
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "ble")]
|
||||
pub mod pairing_finalization;
|
||||
|
||||
use heapless::String;
|
||||
use ufmt::uwrite;
|
||||
|
||||
use super::{
|
||||
bootloader::welcome::Welcome,
|
||||
bootloader::{connect::Connect, welcome::Welcome},
|
||||
component::{
|
||||
bl_confirm::{Confirm, ConfirmTitle},
|
||||
Button, ResultScreen, WelcomeScreen,
|
||||
},
|
||||
cshape::{render_loader, LoaderRange},
|
||||
fonts,
|
||||
theme::{
|
||||
self,
|
||||
bootloader::{
|
||||
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,
|
||||
FIRE40, RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE, TEXT_BOLD, TEXT_NORMAL,
|
||||
TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
||||
BLD_BG, BLD_FG, BLD_TITLE_COLOR, BLD_WARN_COLOR, BLD_WIPE_COLOR, CHECK24, CHECK40,
|
||||
DOWNLOAD32, FIRE32, FIRE40, RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE, TEXT_BOLD,
|
||||
TEXT_NORMAL, TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
||||
},
|
||||
FG,
|
||||
},
|
||||
UIBolt,
|
||||
};
|
||||
|
||||
use crate::ui::{ui_bootloader::BootloaderUI, CommonUI};
|
||||
|
||||
use crate::ui::{
|
||||
display::toif::Toif,
|
||||
geometry::{Alignment, Alignment2D, Offset},
|
||||
shape,
|
||||
shape::render_on_display,
|
||||
use crate::{
|
||||
trezorhal::secbool::secbool,
|
||||
ui::{
|
||||
component::{Event, Label},
|
||||
display::{self, toif::Toif, Color, Icon, LOADER_MAX},
|
||||
geometry::{Alignment, Alignment2D, Offset, Point, Rect},
|
||||
layout::simplified::{get_layout, init_layout, process_frame_event, run, show},
|
||||
shape,
|
||||
shape::render_on_display,
|
||||
ui_bootloader::BootloaderUI,
|
||||
CommonUI,
|
||||
},
|
||||
};
|
||||
|
||||
use ufmt::uwrite;
|
||||
|
||||
use super::theme::bootloader::BLD_WARN_COLOR;
|
||||
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
|
||||
use super::cshape::{render_loader, LoaderRange};
|
||||
use crate::ui::display::LOADER_MAX;
|
||||
#[cfg(feature = "ble")]
|
||||
use super::theme::bootloader::{button_confirm_initial, button_initial};
|
||||
|
||||
pub mod intro;
|
||||
pub mod menu;
|
||||
pub mod welcome;
|
||||
#[cfg(feature = "ble")]
|
||||
use crate::ui::layout_bolt::{
|
||||
bootloader::pairing_finalization::PairingFinalization,
|
||||
component::{confirm_pairing::ConfirmPairing, pairing_mode::PairingMode},
|
||||
};
|
||||
|
||||
pub type BootloaderString = String<128>;
|
||||
|
||||
@ -137,10 +138,39 @@ impl UIBolt {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum BootloaderLayout {
|
||||
Welcome(Welcome),
|
||||
Menu(Menu),
|
||||
Connect(Connect),
|
||||
#[cfg(feature = "ble")]
|
||||
PairingMode(PairingMode),
|
||||
}
|
||||
|
||||
impl BootloaderLayout {
|
||||
fn process_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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BootloaderUI for UIBolt {
|
||||
fn screen_welcome() {
|
||||
let mut frame = Welcome::new();
|
||||
show(&mut frame, true);
|
||||
fn screen_event(buf: &mut [u8], event: Option<Event>) -> u32 {
|
||||
let layout = get_layout::<BootloaderLayout>(buf);
|
||||
layout.process_event(event)
|
||||
}
|
||||
|
||||
fn screen_welcome(buf: &mut [u8]) {
|
||||
let mut welcome = Welcome::new();
|
||||
|
||||
show(&mut welcome, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Welcome(welcome));
|
||||
}
|
||||
|
||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
||||
@ -257,8 +287,12 @@ impl BootloaderUI for UIBolt {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
fn screen_menu(_initial_setup: bool, firmware_present: secbool, buf: &mut [u8]) {
|
||||
let mut frame = Menu::new(firmware_present);
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Menu(frame));
|
||||
}
|
||||
|
||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||
@ -321,15 +355,18 @@ impl BootloaderUI for UIBolt {
|
||||
)
|
||||
}
|
||||
|
||||
fn screen_connect(initial_setup: bool) {
|
||||
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
fn screen_connect(initial_setup: bool, auto_update: bool, buf: &mut [u8]) {
|
||||
let mut frame = Connect::new(
|
||||
"Waiting for host...",
|
||||
fonts::FONT_NORMAL,
|
||||
BLD_TITLE_COLOR,
|
||||
bg,
|
||||
initial_setup,
|
||||
auto_update,
|
||||
);
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Connect(frame));
|
||||
}
|
||||
|
||||
fn screen_wipe_success() {
|
||||
@ -439,4 +476,70 @@ impl BootloaderUI for UIBolt {
|
||||
|
||||
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(initial_setup: bool, buf: &mut [u8]) {
|
||||
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 = PairingMode::new(
|
||||
"Waiting for pairing...".into(),
|
||||
fonts::FONT_NORMAL,
|
||||
BLD_TITLE_COLOR,
|
||||
bg,
|
||||
btn,
|
||||
);
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::PairingMode(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::{
|
||||
fonts,
|
||||
theme::{
|
||||
@ -13,33 +5,70 @@ use super::super::{
|
||||
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 {
|
||||
bg: Pad,
|
||||
menu: Button,
|
||||
}
|
||||
|
||||
impl Welcome {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
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 {
|
||||
type Msg = Never;
|
||||
type Msg = WelcomeMsg;
|
||||
|
||||
fn place(&mut self, bounds: Rect) -> Rect {
|
||||
self.bg.place(screen());
|
||||
self.menu.place(CORNER_BUTTON_AREA);
|
||||
bounds
|
||||
}
|
||||
|
||||
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(Self::Msg::Menu);
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
|
||||
self.bg.render(target);
|
||||
#[cfg(feature = "powerctl")]
|
||||
self.menu.render(target);
|
||||
|
||||
shape::Text::new(
|
||||
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(feature = "translations")]
|
||||
mod address_details;
|
||||
pub mod bl_confirm;
|
||||
|
||||
mod button;
|
||||
#[cfg(feature = "translations")]
|
||||
mod coinjoin_progress;
|
||||
#[cfg(feature = "ble")]
|
||||
pub mod confirm_pairing;
|
||||
mod dialog;
|
||||
mod fido;
|
||||
#[cfg(feature = "ble")]
|
||||
pub mod pairing_mode;
|
||||
#[rustfmt::skip]
|
||||
mod fido_icons;
|
||||
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::{
|
||||
component::{text::TextStyle, LineBreaking::BreakWordsNoHyphen},
|
||||
constant::{HEIGHT, WIDTH},
|
||||
display::Color,
|
||||
geometry::{Offset, Point, Rect},
|
||||
layout_bolt::theme::GREY_MEDIUM,
|
||||
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_FG: Color = WHITE;
|
||||
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_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 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 {
|
||||
TextStyle::new(
|
||||
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,7 +3,7 @@ use heapless::String;
|
||||
use crate::{
|
||||
trezorhal::secbool::secbool,
|
||||
ui::{
|
||||
component::{connect::Connect, Label, LineBreaking::BreakWordsNoHyphen},
|
||||
component::{Label, LineBreaking::BreakWordsNoHyphen},
|
||||
constant,
|
||||
constant::{HEIGHT, SCREEN},
|
||||
display::{self, Color, Icon},
|
||||
@ -33,7 +33,14 @@ mod intro;
|
||||
mod menu;
|
||||
mod welcome;
|
||||
|
||||
use crate::ui::ui_bootloader::BootloaderUI;
|
||||
mod connect;
|
||||
|
||||
use crate::ui::{
|
||||
component::Event,
|
||||
layout::simplified::{get_layout, init_layout, process_frame_event},
|
||||
ui_bootloader::BootloaderUI,
|
||||
};
|
||||
use connect::Connect;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
use welcome::Welcome;
|
||||
@ -88,11 +95,34 @@ impl UICaesar {
|
||||
display::refresh();
|
||||
}
|
||||
}
|
||||
enum BootloaderLayout {
|
||||
Welcome(Welcome),
|
||||
Menu(Menu),
|
||||
Connect(Connect),
|
||||
}
|
||||
|
||||
impl BootloaderLayout {
|
||||
fn process_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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BootloaderUI for UICaesar {
|
||||
fn screen_welcome() {
|
||||
fn screen_event(buf: &mut [u8], event: Option<Event>) -> u32 {
|
||||
let layout = get_layout::<BootloaderLayout>(buf);
|
||||
layout.process_event(event)
|
||||
}
|
||||
|
||||
fn screen_welcome(buf: &mut [u8]) {
|
||||
let mut frame = Welcome::new();
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Welcome(frame));
|
||||
}
|
||||
|
||||
fn screen_install_success(restart_seconds: u8, _initial_setup: bool, complete_draw: bool) {
|
||||
@ -221,8 +251,12 @@ impl BootloaderUI for UICaesar {
|
||||
show(&mut frame, false);
|
||||
}
|
||||
|
||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
fn screen_menu(_initial_setup: bool, firmware_present: secbool, buf: &mut [u8]) {
|
||||
let mut frame = Menu::new(firmware_present);
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Menu(frame));
|
||||
}
|
||||
|
||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||
@ -273,9 +307,12 @@ impl BootloaderUI for UICaesar {
|
||||
);
|
||||
}
|
||||
|
||||
fn screen_connect(_initial_setup: bool) {
|
||||
fn screen_connect(_initial_setup: bool, _auto_update: bool, buf: &mut [u8]) {
|
||||
let mut frame = Connect::new("Waiting for host...", fonts::FONT_NORMAL, BLD_FG, BLD_BG);
|
||||
show(&mut frame, false);
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Connect(frame));
|
||||
}
|
||||
|
||||
fn screen_wipe_success() {
|
||||
@ -381,4 +418,19 @@ impl BootloaderUI for UICaesar {
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_confirm_pairing(_code: u32, _initial_setup: bool) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_pairing_mode(_initial_setup: bool, _buf: &mut [u8]) {
|
||||
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,7 +3,7 @@ use heapless::String;
|
||||
use crate::{
|
||||
trezorhal::secbool::secbool,
|
||||
ui::{
|
||||
component::{connect::Connect, Label},
|
||||
component::Label,
|
||||
display::{self, Color, Icon},
|
||||
geometry::{Alignment, Offset, Point, Rect},
|
||||
layout::simplified::{run, show},
|
||||
@ -43,6 +43,11 @@ use crate::ui::{
|
||||
use ufmt::uwrite;
|
||||
|
||||
use super::theme::bootloader::BLD_WARN_COLOR;
|
||||
use crate::ui::{
|
||||
component::Event,
|
||||
layout::simplified::{get_layout, init_layout, process_frame_event},
|
||||
};
|
||||
use connect::Connect;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
|
||||
@ -50,6 +55,8 @@ pub mod intro;
|
||||
pub mod menu;
|
||||
pub mod welcome;
|
||||
|
||||
pub mod connect;
|
||||
|
||||
pub type BootloaderString = String<128>;
|
||||
|
||||
const RECONNECT_MESSAGE: &str = "PLEASE RECONNECT\nTHE DEVICE";
|
||||
@ -121,11 +128,32 @@ impl UIDelizia {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum BootloaderLayout {
|
||||
Welcome(Welcome),
|
||||
Menu(Menu),
|
||||
Connect(Connect),
|
||||
}
|
||||
|
||||
impl BootloaderLayout {
|
||||
fn process_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),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl BootloaderUI for UIDelizia {
|
||||
fn screen_welcome() {
|
||||
fn screen_event(buf: &mut [u8], event: Option<Event>) -> u32 {
|
||||
let layout = get_layout::<BootloaderLayout>(buf);
|
||||
layout.process_event(event)
|
||||
}
|
||||
|
||||
fn screen_welcome(buf: &mut [u8]) {
|
||||
let mut frame = Welcome::new();
|
||||
show(&mut frame, true);
|
||||
init_layout(buf, BootloaderLayout::Welcome(frame));
|
||||
}
|
||||
|
||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool) {
|
||||
@ -290,8 +318,12 @@ impl BootloaderUI for UIDelizia {
|
||||
show(&mut frame, true);
|
||||
}
|
||||
|
||||
fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
fn screen_menu(_initial_setup: bool, firmware_present: secbool, buf: &mut [u8]) {
|
||||
let mut frame = Menu::new(firmware_present);
|
||||
|
||||
show(&mut frame, true);
|
||||
|
||||
init_layout(buf, BootloaderLayout::Menu(frame));
|
||||
}
|
||||
|
||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32 {
|
||||
@ -357,7 +389,7 @@ impl BootloaderUI for UIDelizia {
|
||||
)
|
||||
}
|
||||
|
||||
fn screen_connect(initial_setup: bool) {
|
||||
fn screen_connect(initial_setup: bool, _auto_update: bool, buf: &mut [u8]) {
|
||||
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
|
||||
let mut frame = Connect::new(
|
||||
"Waiting for host...",
|
||||
@ -366,6 +398,7 @@ impl BootloaderUI for UIDelizia {
|
||||
bg,
|
||||
);
|
||||
show(&mut frame, true);
|
||||
init_layout(buf, BootloaderLayout::Connect(frame));
|
||||
}
|
||||
|
||||
fn screen_wipe_success() {
|
||||
@ -475,4 +508,19 @@ impl BootloaderUI for UIDelizia {
|
||||
|
||||
display::refresh();
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_confirm_pairing(_code: u32, _initial_setup: bool) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_pairing_mode(_initial_setup: bool, _buf: &mut [u8]) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_pairing_mode_finalizing(_initial_setup: bool) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
#[cfg(feature = "bootloader")]
|
||||
use crate::trezorhal::secbool::secbool;
|
||||
use crate::ui::component::Event;
|
||||
|
||||
#[cfg(feature = "bootloader")]
|
||||
pub trait BootloaderUI {
|
||||
fn screen_welcome();
|
||||
fn screen_event(buf: &mut [u8], event: Option<Event>) -> u32;
|
||||
|
||||
fn screen_welcome(buf: &mut [u8]);
|
||||
|
||||
fn screen_install_success(restart_seconds: u8, initial_setup: bool, complete_draw: bool);
|
||||
|
||||
@ -25,7 +28,7 @@ pub trait BootloaderUI {
|
||||
|
||||
fn screen_unlock_bootloader_success();
|
||||
|
||||
fn screen_menu(firmware_present: secbool) -> u32;
|
||||
fn screen_menu(initial_setup: bool, firmware_present: secbool, buf: &mut [u8]);
|
||||
|
||||
fn screen_intro(bld_version: &str, vendor: &str, version: &str, fw_ok: bool) -> u32;
|
||||
|
||||
@ -35,7 +38,7 @@ pub trait BootloaderUI {
|
||||
|
||||
fn screen_install_progress(progress: u16, initialize: bool, initial_setup: bool);
|
||||
|
||||
fn screen_connect(initial_setup: bool);
|
||||
fn screen_connect(initial_setup: bool, auto_update: bool, buf: &mut [u8]);
|
||||
|
||||
fn screen_wipe_success();
|
||||
|
||||
@ -48,4 +51,13 @@ pub trait BootloaderUI {
|
||||
vendor_img: &'static [u8],
|
||||
wait: i32,
|
||||
);
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_confirm_pairing(code: u32, initial_setup: bool) -> u32;
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_pairing_mode(initial_setup: bool, buf: &mut [u8]);
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
fn screen_pairing_mode_finalizing(initial_setup: bool) -> u32;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <io/usb.h>
|
||||
#include <rtl/secbool.h>
|
||||
#include <sec/entropy.h>
|
||||
#include <sys/sysevent.h>
|
||||
#include <sys/systick.h>
|
||||
#include <util/flash.h>
|
||||
#include <util/translations.h>
|
||||
|
@ -221,7 +221,7 @@ void sysevents_poll(const sysevents_t *awaited, sysevents_t *signalled,
|
||||
bool ready = (poller->signalled->read_ready != 0) ||
|
||||
(poller->signalled->write_ready != 0);
|
||||
if (ready || timed_out) {
|
||||
systask_t *task = poller->task;
|
||||
systask_t *task = poller->task; // USB interface
|
||||
remove_poller(dispatcher, prio);
|
||||
if (task == kernel_task) {
|
||||
return;
|
||||
|
@ -110,7 +110,7 @@ void rsod_terminal(const systask_postmortem_t* pminfo) {
|
||||
|
||||
#ifdef FANCY_FATAL_ERROR
|
||||
|
||||
#include "rust_ui.h"
|
||||
#include "rust_ui_common.h"
|
||||
|
||||
void rsod_gui(const systask_postmortem_t* pminfo) {
|
||||
const char* title = RSOD_DEFAULT_TITLE;
|
||||
|
Loading…
Reference in New Issue
Block a user