From 1290e6f3400c2920962e0afb9ca5761d6e76f54f Mon Sep 17 00:00:00 2001
From: tychovrahe <brunam@seznam.cz>
Date: Wed, 5 Mar 2025 14:04:40 +0100
Subject: [PATCH] fixup! feat(core): add BLE to bootloader

---
 core/embed/projects/bootloader/bootui.c       |  6 ++
 core/embed/projects/bootloader/bootui.h       |  4 +
 .../projects/bootloader/wire/wire_iface_ble.c | 17 ++++
 .../workflow/wf_ble_pairing_request.c         |  6 +-
 .../bootloader/workflow/wf_host_control.c     |  3 +-
 .../bootloader/workflow/wf_wipe_device.c      | 63 +++++++++++++--
 .../projects/bootloader/workflow/workflow.h   |  2 +-
 core/embed/rust/rust_ui_bootloader.h          |  2 +-
 core/embed/rust/src/ui/api/bootloader_c.rs    |  4 +-
 .../rust/src/ui/layout_bolt/bootloader/mod.rs | 20 +++--
 .../src/ui/layout_bolt/theme/bootloader.rs    | 79 +++++++++++++++++--
 core/embed/rust/src/ui/ui_bootloader.rs       |  2 +-
 12 files changed, 182 insertions(+), 26 deletions(-)

diff --git a/core/embed/projects/bootloader/bootui.c b/core/embed/projects/bootloader/bootui.c
index 51ac3da056..24c06318c7 100644
--- a/core/embed/projects/bootloader/bootui.c
+++ b/core/embed/projects/bootloader/bootui.c
@@ -182,3 +182,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(const char *code) {
+  return screen_confirm_pairing(code, initial_setup);
+}
+#endif
diff --git a/core/embed/projects/bootloader/bootui.h b/core/embed/projects/bootloader/bootui.h
index 8dd4c556d8..ef438cc37e 100644
--- a/core/embed/projects/bootloader/bootui.h
+++ b/core/embed/projects/bootloader/bootui.h
@@ -95,3 +95,7 @@ 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(const char* code);
+#endif
diff --git a/core/embed/projects/bootloader/wire/wire_iface_ble.c b/core/embed/projects/bootloader/wire/wire_iface_ble.c
index 79a16bfd1f..3c6e66bd8b 100644
--- a/core/embed/projects/bootloader/wire/wire_iface_ble.c
+++ b/core/embed/projects/bootloader/wire/wire_iface_ble.c
@@ -25,6 +25,12 @@
 #include <io/ble.h>
 #include <sys/systick.h>
 
+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;
@@ -36,6 +42,9 @@ static bool ble_write_(uint8_t* data, size_t size) {
     if (ticks_expired(deadline)) {
       return false;
     }
+    if (!is_connected()) {
+      return false;
+    }
     if (ble_can_write()) {
       break;
     }
@@ -55,6 +64,9 @@ static int ble_read_(uint8_t* buffer, size_t buffer_size) {
     if (ticks_expired(deadline)) {
       return false;
     }
+    if (!is_connected()) {
+      return false;
+    }
     if (ble_can_read()) {
       break;
     }
@@ -92,6 +104,11 @@ void ble_iface_init(wire_iface_t* iface) {
     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);
     } else {
diff --git a/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c b/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
index b6d214c70b..c57c46b6e4 100644
--- a/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
+++ b/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
@@ -24,11 +24,10 @@
 #include <io/ble.h>
 
 #include "bootui.h"
-#include "rust_ui_bootloader.h"
 #include "workflow.h"
 
-workflow_result_t workflow_ble_pairing_request(const uint8_t *data) {
-  ui_result_t result = screen_confirm_pairing(data);
+workflow_result_t workflow_ble_pairing_request(const char *code) {
+  ui_result_t result = ui_screen_confirm_pairing(code);
 
   if (result == UI_RESULT_CONFIRM) {
     ble_command_t cmd = {
@@ -42,7 +41,6 @@ workflow_result_t workflow_ble_pairing_request(const uint8_t *data) {
     ble_issue_command(&cmd);
   }
 
-  screen_connect(false);
   return WF_OK;
 }
 
diff --git a/core/embed/projects/bootloader/workflow/wf_host_control.c b/core/embed/projects/bootloader/workflow/wf_host_control.c
index 2356f1bbba..f815b7e14b 100644
--- a/core/embed/projects/bootloader/workflow/wf_host_control.c
+++ b/core/embed/projects/bootloader/workflow/wf_host_control.c
@@ -115,7 +115,8 @@ workflow_result_t workflow_host_control(const vendor_header *const vhdr,
     if (i == IFACE_BLE_EVENT) {
       switch (e.event.ble_event.type) {
         case BLE_PAIRING_REQUEST:
-          workflow_ble_pairing_request(e.event.ble_event.data);
+          workflow_ble_pairing_request((char *)e.event.ble_event.data);
+          redraw_wait_screen();
           continue;
         default:
           break;
diff --git a/core/embed/projects/bootloader/workflow/wf_wipe_device.c b/core/embed/projects/bootloader/workflow/wf_wipe_device.c
index 51b483d06e..e1e13cd4b0 100644
--- a/core/embed/projects/bootloader/workflow/wf_wipe_device.c
+++ b/core/embed/projects/bootloader/workflow/wf_wipe_device.c
@@ -22,12 +22,62 @@
 
 #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 "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) {
   ui_result_t response = ui_screen_wipe_confirm();
   if (UI_RESULT_CONFIRM != response) {
     if (iface != NULL) {
@@ -38,11 +88,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;
   }
diff --git a/core/embed/projects/bootloader/workflow/workflow.h b/core/embed/projects/bootloader/workflow/workflow.h
index d2a130c5f7..36c678fca9 100644
--- a/core/embed/projects/bootloader/workflow/workflow.h
+++ b/core/embed/projects/bootloader/workflow/workflow.h
@@ -68,5 +68,5 @@ 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 uint8_t *data);
+workflow_result_t workflow_ble_pairing_request(const char *code);
 #endif
diff --git a/core/embed/rust/rust_ui_bootloader.h b/core/embed/rust/rust_ui_bootloader.h
index 6472e63742..17cbff1252 100644
--- a/core/embed/rust/rust_ui_bootloader.h
+++ b/core/embed/rust/rust_ui_bootloader.h
@@ -28,4 +28,4 @@ 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_confirm_pairing(const uint8_t* code);
+uint32_t screen_confirm_pairing(const char* code, bool initial_setup);
diff --git a/core/embed/rust/src/ui/api/bootloader_c.rs b/core/embed/rust/src/ui/api/bootloader_c.rs
index 2724c53f70..0dcf61a100 100644
--- a/core/embed/rust/src/ui/api/bootloader_c.rs
+++ b/core/embed/rust/src/ui/api/bootloader_c.rs
@@ -146,8 +146,8 @@ extern "C" fn screen_wipe_fail() {
 }
 
 #[no_mangle]
-extern "C" fn screen_confirm_pairing(code: *const cty::c_char) -> u32 {
+extern "C" fn screen_confirm_pairing(code: *const cty::c_char, initial_setup: bool) -> u32 {
     let code = unwrap!(unsafe { from_c_array(code, 6) });
 
-    ModelUI::screen_confirm_pairing(code)
+    ModelUI::screen_confirm_pairing(code, initial_setup)
 }
diff --git a/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs b/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs
index 14abccaa7f..92324ab0af 100644
--- a/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs
+++ b/core/embed/rust/src/ui/layout_bolt/bootloader/mod.rs
@@ -41,7 +41,7 @@ use crate::ui::{
 
 use ufmt::uwrite;
 
-use super::theme::bootloader::BLD_WARN_COLOR;
+use super::theme::bootloader::{button_confirm_initial, button_initial, BLD_WARN_COLOR};
 
 use intro::Intro;
 use menu::Menu;
@@ -441,15 +441,25 @@ impl BootloaderUI for UIBolt {
     }
 
     #[cfg(feature = "ble")]
-    fn screen_confirm_pairing(code: &str) -> u32 {
+    fn screen_confirm_pairing(code: &str, initial_setup: bool) -> u32 {
+        let bg = if initial_setup { WELCOME_COLOR } else { BLD_BG };
         let title = Label::centered("Pair device".into(), TEXT_NORMAL);
 
         let msg = Label::centered(code.into(), TEXT_NORMAL);
 
-        let right = Button::with_text("CONFIRM".into()).styled(button_confirm());
-        let left = Button::with_text("REJECT".into()).styled(button_bld());
+        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 = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg);
+        let mut frame = Confirm::new(bg, left, right, ConfirmTitle::Text(title), msg);
 
         run(&mut frame)
     }
diff --git a/core/embed/rust/src/ui/layout_bolt/theme/bootloader.rs b/core/embed/rust/src/ui/layout_bolt/theme/bootloader.rs
index 56c948a132..231719a42e 100644
--- a/core/embed/rust/src/ui/layout_bolt/theme/bootloader.rs
+++ b/core/embed/rust/src/ui/layout_bolt/theme/bootloader.rs
@@ -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,
diff --git a/core/embed/rust/src/ui/ui_bootloader.rs b/core/embed/rust/src/ui/ui_bootloader.rs
index 031348521c..a07dca89b6 100644
--- a/core/embed/rust/src/ui/ui_bootloader.rs
+++ b/core/embed/rust/src/ui/ui_bootloader.rs
@@ -50,5 +50,5 @@ pub trait BootloaderUI {
     );
 
     #[cfg(feature = "ble")]
-    fn screen_confirm_pairing(code: &str) -> u32;
+    fn screen_confirm_pairing(code: &str, initial_setup: bool) -> u32;
 }