1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-05 13:26:57 +00:00

feat(core/bootloader): prepare wireless setup bootloader workflow

[no changelog]
This commit is contained in:
tychovrahe 2025-06-27 21:40:36 +02:00 committed by TychoVrahe
parent 6ba7fa24a2
commit 03fd6a5de8
14 changed files with 259 additions and 18 deletions

View File

@ -153,6 +153,9 @@ void ble_event_flush(void);
// Obtains the current operational state of the BLE module.
void ble_get_state(ble_state_t *state);
// Retrieves last set advertising name
void ble_get_advertising_name(char *name, size_t max_len);
// Check if write is possible
bool ble_can_write(void);

View File

@ -847,6 +847,22 @@ bool ble_get_mac(uint8_t *mac, size_t max_len) {
return false;
}
void ble_get_advertising_name(char *name, size_t max_len) {
ble_driver_t *drv = &g_ble_driver;
if (max_len < sizeof(drv->adv_cmd.name)) {
memset(name, 0, max_len);
return;
}
if (!drv->initialized) {
memset(name, 0, max_len);
return;
}
memcpy(name, drv->adv_cmd.name, sizeof(drv->adv_cmd.name));
}
static void on_ble_iface_event_poll(void *context, bool read_awaited,
bool write_awaited) {
UNUSED(context);

View File

@ -28,3 +28,7 @@ uint32_t ble_read(uint8_t *data, uint16_t max_len) { return 0; }
bool ble_get_mac(uint8_t *mac, size_t max_len) { return false; }
void ble_event_flush(void) {}
void ble_get_advertising_name(char *name, size_t max_len) {
memset(name, 0, max_len);
}

View File

@ -45,9 +45,13 @@ workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
if (!ble_iface_start_pairing()) {
return WF_OK_PAIRING_FAILED;
}
char name[BLE_ADV_NAME_LEN + 1] = {0};
ble_get_advertising_name(name, sizeof(name));
c_layout_t layout;
memset(&layout, 0, sizeof(layout));
screen_pairing_mode(ui_get_initial_setup(), &layout);
screen_pairing_mode(ui_get_initial_setup(), name, strlen(name), &layout);
uint32_t code = 0;
workflow_result_t res =
@ -117,4 +121,93 @@ workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
return WF_OK_PAIRING_COMPLETED;
}
workflow_result_t workflow_wireless_setup(const vendor_header *const vhdr,
const image_header *const hdr,
protob_ios_t *ios) {
ble_iface_start_pairing();
char name[BLE_ADV_NAME_LEN + 1] = {0};
ble_get_advertising_name(name, sizeof(name));
c_layout_t layout;
memset(&layout, 0, sizeof(layout));
screen_wireless_setup(name, strlen(name), &layout);
uint32_t code = 0;
workflow_result_t res = workflow_host_control(vhdr, hdr, &layout, &code, ios);
if (res != WF_OK_UI_ACTION) {
ble_iface_end_pairing();
return res;
}
if (code == WIRELESS_SETUP_CANCEL) {
ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
uint32_t result = ui_screen_confirm_pairing(code);
uint8_t pairing_code[BLE_PAIRING_CODE_LEN] = {0};
if (result != CONFIRM || !encode_pairing_code(code, pairing_code)) {
ble_command_t cmd = {
.cmd_type = BLE_REJECT_PAIRING,
};
ble_issue_command(&cmd);
return WF_OK_PAIRING_FAILED;
}
ble_command_t cmd = {
.cmd_type = BLE_ALLOW_PAIRING,
.data_len = sizeof(pairing_code),
};
memcpy(cmd.data.raw, pairing_code, sizeof(pairing_code));
ble_issue_command(&cmd);
bool skip_finalization = false;
sysevents_t awaited = {0};
sysevents_t signalled = {0};
awaited.read_ready |= 1 << SYSHANDLE_BLE;
sysevents_poll(&awaited, &signalled, ticks_timeout(500));
if (signalled.read_ready == 1 << SYSHANDLE_BLE) {
ble_event_t event = {0};
if (ble_get_event(&event)) {
if (event.type == BLE_PAIRING_COMPLETED) {
skip_finalization = true;
}
}
}
if (!skip_finalization) {
pairing_mode_finalization_result_t r =
screen_pairing_mode_finalizing(ui_get_initial_setup());
if (r == PAIRING_FINALIZATION_FAILED) {
ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
if (r == PAIRING_FINALIZATION_CANCEL) {
ble_command_t disconnect = {.cmd_type = BLE_DISCONNECT};
ble_issue_command(&disconnect);
ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
}
memset(&layout, 0, sizeof(layout));
screen_wireless_setup_final(&layout);
uint32_t ui_result = 0;
res = workflow_host_control(vhdr, hdr, &layout, &ui_result, ios);
if (ui_result == WIRELESS_SETUP_FINAL_CANCEL) {
return WF_OK_PAIRING_COMPLETED;
}
return res;
}
#endif

View File

@ -54,12 +54,13 @@ workflow_result_t workflow_empty_device(void) {
res = workflow_host_control(NULL, NULL, &layout, &ui_result, &ios);
#ifdef USE_BLE
if (res == WF_OK_UI_ACTION && ui_result == WELCOME_PAIRING_MODE) {
res = workflow_ble_pairing_request(NULL, NULL);
res = workflow_wireless_setup(NULL, NULL, &ios);
if (res == WF_OK_PAIRING_COMPLETED || res == WF_OK_PAIRING_FAILED) {
res = WF_CANCELLED;
ui_result = WELCOME_CANCEL;
continue;
}
return res;
}
#endif
if (res == WF_OK_UI_ACTION && ui_result == WELCOME_MENU) {

View File

@ -81,6 +81,10 @@ workflow_result_t workflow_auto_update(const vendor_header *const vhdr,
#ifdef USE_BLE
workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
const image_header *const hdr);
workflow_result_t workflow_wireless_setup(const vendor_header *const vhdr,
const image_header *const hdr,
protob_ios_t *ios);
#endif
void workflow_ifaces_init(secbool usb21_landing, protob_ios_t *ios);

View File

@ -88,4 +88,17 @@ typedef enum {
// 0 - 999999 - pairing code
PAIRING_MODE_CANCEL = 1000000,
} pairing_mode_result_t;
void screen_pairing_mode(bool initial_setup, c_layout_t* layout);
void screen_pairing_mode(bool initial_setup, const char* name, size_t name_len,
c_layout_t* layout);
typedef enum {
// 0 - 999999 - pairing code
WIRELESS_SETUP_CANCEL = 1000000,
} wireless_setup_result_t;
void screen_wireless_setup(const char* name, size_t name_len,
c_layout_t* layout);
typedef enum {
WIRELESS_SETUP_FINAL_CANCEL = 1,
} wireless_setup_final_result_t;
void screen_wireless_setup_final(c_layout_t* layout);

View File

@ -181,8 +181,39 @@ extern "C" fn screen_confirm_pairing(code: u32, initial_setup: bool) -> u32 {
#[cfg(feature = "ble")]
#[no_mangle]
extern "C" fn screen_pairing_mode(initial_setup: bool, layout: *mut c_layout_t) {
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_pairing_mode(initial_setup);
extern "C" fn screen_pairing_mode(
initial_setup: bool,
name: *const cty::c_char,
name_len: usize,
layout: *mut c_layout_t,
) {
let name = unsafe { from_c_array(name, name_len).unwrap_or("") };
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_pairing_mode(initial_setup, name);
screen.show();
// SAFETY: calling code is supposed to give us exclusive access to the layout
let mut layout = unsafe { LayoutBuffer::new(layout) };
layout.store(screen);
}
#[cfg(feature = "ble")]
#[no_mangle]
extern "C" fn screen_wireless_setup(
name: *const cty::c_char,
name_len: usize,
layout: *mut c_layout_t,
) {
let name = unsafe { from_c_array(name, name_len).unwrap_or("") };
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_wireless_setup(name);
screen.show();
// SAFETY: calling code is supposed to give us exclusive access to the layout
let mut layout = unsafe { LayoutBuffer::new(layout) };
layout.store(screen);
}
#[cfg(feature = "ble")]
#[no_mangle]
extern "C" fn screen_wireless_setup_final(layout: *mut c_layout_t) {
let mut screen = <ModelUI as BootloaderUI>::CLayoutType::init_wireless_setup_final();
screen.show();
// SAFETY: calling code is supposed to give us exclusive access to the layout
let mut layout = unsafe { LayoutBuffer::new(layout) };

View File

@ -190,7 +190,7 @@ impl BootloaderLayoutType for BootloaderLayout {
}
#[cfg(feature = "ble")]
fn init_pairing_mode(initial_setup: bool) -> Self {
fn init_pairing_mode(initial_setup: bool, _name: &'static str) -> Self {
let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
let btn = if initial_setup {
@ -208,6 +208,16 @@ impl BootloaderLayoutType for BootloaderLayout {
);
Self::PairingMode(frame)
}
#[cfg(feature = "ble")]
fn init_wireless_setup(_name: &'static str) -> Self {
unimplemented!()
}
#[cfg(feature = "ble")]
fn init_wireless_setup_final() -> Self {
unimplemented!()
}
}
impl BootloaderUI for UIBolt {

View File

@ -136,7 +136,17 @@ impl BootloaderLayoutType for BootloaderLayout {
}
#[cfg(feature = "ble")]
fn init_pairing_mode(_initial_setup: bool) -> Self {
fn init_pairing_mode(_initial_setup: bool, _name: &'static str) -> Self {
unimplemented!()
}
#[cfg(feature = "ble")]
fn init_wireless_setup(_name: &'static str) -> Self {
unimplemented!()
}
#[cfg(feature = "ble")]
fn init_wireless_setup_final() -> Self {
unimplemented!()
}
}

View File

@ -171,7 +171,17 @@ impl BootloaderLayoutType for BootloaderLayout {
}
#[cfg(feature = "ble")]
fn init_pairing_mode(_initial_setup: bool) -> Self {
fn init_pairing_mode(_initial_setup: bool, _name: &'static str) -> Self {
unimplemented!()
}
#[cfg(feature = "ble")]
fn init_wireless_setup(_name: &'static str) -> Self {
unimplemented!()
}
#[cfg(feature = "ble")]
fn init_wireless_setup_final() -> Self {
unimplemented!()
}
}

View File

@ -1,3 +1,7 @@
use super::{
super::{cshape::ScreenBorder, theme},
BldActionBar, BldActionBarMsg,
};
use crate::{
strutil::TString,
ui::{
@ -9,11 +13,6 @@ use crate::{
},
};
use super::{
super::{cshape::ScreenBorder, theme},
BldActionBar, BldActionBarMsg,
};
#[repr(u32)]
pub enum PairingMsg {
Cancel,
@ -31,14 +30,16 @@ impl ReturnToC for PairingMsg {
pub struct PairingModeScreen {
message: Label<'static>,
name: Label<'static>,
action_bar: Option<BldActionBar>,
screen_border: ScreenBorder,
}
impl PairingModeScreen {
pub fn new(message: TString<'static>) -> Self {
pub fn new(message: TString<'static>, name: TString<'static>) -> Self {
Self {
message: Label::new(message, Alignment::Center, theme::TEXT_NORMAL),
name: Label::new(name, Alignment::Center, theme::TEXT_NORMAL),
action_bar: None,
screen_border: ScreenBorder::new(theme::BLUE),
}
@ -56,8 +57,9 @@ impl Component for PairingModeScreen {
fn place(&mut self, bounds: Rect) -> Rect {
let (_header_area, rest) = bounds.split_top(theme::HEADER_HEIGHT);
let (rest, action_bar_area) = rest.split_bottom(theme::ACTION_BAR_HEIGHT);
let content_area = rest.inset(theme::SIDE_INSETS);
let (content_area, name_area) = rest.inset(theme::SIDE_INSETS).split_top(70);
self.message.place(content_area);
self.name.place(name_area);
self.action_bar.place(action_bar_area);
bounds
}
@ -83,6 +85,7 @@ impl Component for PairingModeScreen {
fn render<'s>(&'s self, target: &mut impl Renderer<'s>) {
self.message.render(target);
self.name.render(target);
self.action_bar.render(target);
self.screen_border.render(u8::MAX, target);
}
@ -93,5 +96,6 @@ impl crate::trace::Trace for PairingModeScreen {
fn trace(&self, t: &mut dyn crate::trace::Tracer) {
t.component("PairingMode");
t.string("message", *self.message.text());
t.string("name", *self.name.text());
}
}

View File

@ -93,6 +93,10 @@ pub enum BootloaderLayout {
Connect(ConnectScreen),
#[cfg(feature = "ble")]
PairingMode(PairingModeScreen),
#[cfg(feature = "ble")]
WirelessSetup(PairingModeScreen),
#[cfg(feature = "ble")]
WirelessSetupFinal(ConnectScreen),
}
impl BootloaderLayoutType for BootloaderLayout {
@ -103,6 +107,14 @@ impl BootloaderLayoutType for BootloaderLayout {
BootloaderLayout::Connect(f) => process_frame_event::<ConnectScreen>(f, event),
#[cfg(feature = "ble")]
BootloaderLayout::PairingMode(f) => process_frame_event::<PairingModeScreen>(f, event),
#[cfg(feature = "ble")]
BootloaderLayout::WirelessSetup(f) => {
process_frame_event::<PairingModeScreen>(f, event)
}
#[cfg(feature = "ble")]
BootloaderLayout::WirelessSetupFinal(f) => {
process_frame_event::<ConnectScreen>(f, event)
}
}
}
@ -113,10 +125,16 @@ impl BootloaderLayoutType for BootloaderLayout {
BootloaderLayout::Connect(f) => show(f, true),
#[cfg(feature = "ble")]
BootloaderLayout::PairingMode(f) => show(f, true),
#[cfg(feature = "ble")]
BootloaderLayout::WirelessSetup(f) => show(f, true),
#[cfg(feature = "ble")]
BootloaderLayout::WirelessSetupFinal(f) => show(f, true),
}
}
fn init_welcome() -> Self {
// TODO: different UI. needs to decide based on some host already paired:
// peer_count() > 0
let screen = BldWelcomeScreen::new();
Self::Welcome(screen)
}
@ -138,13 +156,32 @@ impl BootloaderLayoutType for BootloaderLayout {
}
#[cfg(feature = "ble")]
fn init_pairing_mode(_initial_setup: bool) -> Self {
fn init_pairing_mode(_initial_setup: bool, name: &'static str) -> Self {
// TODO: different style for initial setup
let btn = Button::with_text("Cancel".into()).styled(button_default());
let screen = PairingModeScreen::new("Waiting for pairing...".into())
let screen = PairingModeScreen::new("Waiting for pairing...".into(), name.into())
.with_action_bar(BldActionBar::new_single(btn));
Self::PairingMode(screen)
}
#[cfg(feature = "ble")]
fn init_wireless_setup(name: &'static str) -> Self {
// todo implement correct UI
let btn = Button::with_text("Cancel".into()).styled(button_default());
let screen = PairingModeScreen::new("QR_CODE".into(), name.into())
.with_action_bar(BldActionBar::new_single(btn));
Self::WirelessSetup(screen)
}
#[cfg(feature = "ble")]
fn init_wireless_setup_final() -> Self {
// todo implement correct UI
let btn = Button::with_text("Cancel".into()).styled(button_default());
let screen =
ConnectScreen::new("WAIT".into()).with_action_bar(BldActionBar::new_single(btn));
Self::WirelessSetupFinal(screen)
}
}
impl BootloaderUI for UIEckhart {

View File

@ -7,7 +7,12 @@ pub trait BootloaderLayoutType {
fn init_menu(initial_setup: bool) -> Self;
fn init_connect(initial_setup: bool, auto_update: bool) -> Self;
#[cfg(feature = "ble")]
fn init_pairing_mode(initial_setup: bool) -> Self;
fn init_pairing_mode(initial_setup: bool, name: &'static str) -> Self;
#[cfg(feature = "ble")]
fn init_wireless_setup(name: &'static str) -> Self;
#[cfg(feature = "ble")]
fn init_wireless_setup_final() -> Self;
}
pub trait BootloaderUI {