Skip to content

Commit 39c79ca

Browse files
committed
orientation-screen: Migrate to rust
create an async friendly hardware supported delayin C and wrap it in rust using Future.
1 parent 4ab3db2 commit 39c79ca

File tree

18 files changed

+433
-135
lines changed

18 files changed

+433
-135
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
set(DBB-FIRMWARE-SOURCES
1818
${CMAKE_SOURCE_DIR}/src/firmware_main_loop.c
19+
${CMAKE_SOURCE_DIR}/src/delay.c
1920
${CMAKE_SOURCE_DIR}/src/keystore.c
2021
${CMAKE_SOURCE_DIR}/src/random.c
2122
${CMAKE_SOURCE_DIR}/src/hardfault.c
@@ -36,7 +37,6 @@ set(DBB-FIRMWARE-SOURCES
3637
${CMAKE_SOURCE_DIR}/src/touch/gestures.c
3738
${CMAKE_SOURCE_DIR}/src/reset.c
3839
${CMAKE_SOURCE_DIR}/src/cipher/cipher.c
39-
${CMAKE_SOURCE_DIR}/src/workflow/orientation_screen.c
4040
${CMAKE_SOURCE_DIR}/src/queue.c
4141
${CMAKE_SOURCE_DIR}/src/usb/usb_processing.c
4242
)

src/delay.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include <delay.h>
16+
#include <hal_timer.h>
17+
#include <platform/driver_init.h>
18+
#include <stdbool.h>
19+
#include <stdint.h>
20+
#include <string.h>
21+
#include <utils_assert.h>
22+
23+
struct task {
24+
struct timer_task timer;
25+
volatile bool done;
26+
};
27+
28+
static struct task _tasks[10] = {0};
29+
static struct task _empty = {0};
30+
31+
static void _hal_timer_cb(const struct timer_task* const timer)
32+
{
33+
for (int i = 0; i < (int)(sizeof(_tasks) / sizeof(struct task)); i++) {
34+
if (&_tasks[i].timer == timer) {
35+
_tasks[i].done = true;
36+
}
37+
}
38+
}
39+
40+
bool delay_init_ms(delay_t* self, uint32_t ms)
41+
{
42+
// find an unused slot in tasks
43+
int i;
44+
CRITICAL_SECTION_ENTER();
45+
for (i = 0; i < (int)(sizeof(_tasks) / sizeof(struct task)); i++) {
46+
if (memcmp(&_tasks[i], &_empty, sizeof(struct task)) == 0) {
47+
break;
48+
}
49+
}
50+
CRITICAL_SECTION_LEAVE();
51+
if (i == sizeof(_tasks)) {
52+
return false;
53+
}
54+
_tasks[i].done = false;
55+
memset(&_tasks[i], 0, sizeof(struct task));
56+
_tasks[i].timer.interval = ms;
57+
_tasks[i].timer.cb = _hal_timer_cb;
58+
_tasks[i].timer.mode = TIMER_TASK_ONE_SHOT;
59+
self->id = i;
60+
return true;
61+
}
62+
63+
void delay_start(const delay_t* self)
64+
{
65+
ASSERT(self->id < (sizeof(_tasks) / sizeof(struct task)));
66+
ASSERT(!_tasks[self->id].done);
67+
timer_add_task(&TIMER_0, &_tasks[self->id].timer);
68+
}
69+
70+
bool delay_poll(const delay_t* self)
71+
{
72+
ASSERT(self->id < (sizeof(_tasks) / sizeof(struct task)));
73+
if (_tasks[self->id].done) {
74+
memset(&_tasks[self->id], 0, sizeof(struct task));
75+
return true;
76+
}
77+
return false;
78+
}

src/delay.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef DELAY_H
16+
#define DELAY_H
17+
#include <stdbool.h>
18+
#include <stddef.h>
19+
#include <stdint.h>
20+
21+
typedef struct {
22+
size_t id;
23+
} delay_t;
24+
25+
// Create a non-blocking delay instance. Poll with delay_poll to completion
26+
// Limited to 10 concurrent delays, will return false if it fails to allocate one
27+
bool delay_init_ms(delay_t* self, uint32_t ms);
28+
29+
// Start the delay
30+
void delay_start(const delay_t* self);
31+
32+
// returns true if time has passed. After it has returned true once it must not be called again
33+
bool delay_poll(const delay_t* self);
34+
#endif

src/firmware_main_loop.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@
3232
#include "usb/usb.h"
3333
#include "usb/usb_frame.h"
3434
#include "usb/usb_processing.h"
35-
#include "workflow/orientation_screen.h"
3635
#include <rust/rust.h>
3736
#include <ui/fonts/monogram_5X9.h>
3837
#include <utils_ringbuffer.h>
38+
#include <version.h>
3939
#if APP_U2F == 1
4040
#include "u2f.h"
4141
#include "u2f/u2f_packet.h"
@@ -45,6 +45,36 @@
4545
// Must be power of 2
4646
#define UART_OUT_BUF_LEN 2048
4747

48+
// Currently we have one firmware for both BB02 and BB02_PLUS, and only the
49+
// PRODUCT_BITBOX_MULTI/BTCONLY definitions apply. The PRODUCT_BITBOX_PLUS_MULTI/BTCONLY defs
50+
// currently only apply in the bootloader, which we don't need here.
51+
#if PRODUCT_BITBOX_MULTI == 1
52+
#define PRODUCT_STRING_SUFFIX "multi"
53+
#elif PRODUCT_BITBOX_BTCONLY == 1
54+
#define PRODUCT_STRING_SUFFIX "btconly"
55+
#elif PRODUCT_BITBOX02_FACTORYSETUP == 1
56+
// Dummy, not actually needed, but this file is currently needlessly compiled for factorysetup.
57+
#define PRODUCT_STRING_SUFFIX "factory"
58+
#else
59+
#error "unknown edition"
60+
#endif
61+
62+
#define DEVICE_MODE \
63+
"{\"p\":\"bb02p-" PRODUCT_STRING_SUFFIX "\",\"v\":\"" DIGITAL_BITBOX_VERSION "\"}"
64+
65+
static void _orientation_done_cb(void* user_data)
66+
{
67+
struct ringbuffer* uart_out_queue = (struct ringbuffer*)user_data;
68+
// hww handler in usb_process must be setup before we can allow ble connections
69+
if (memory_get_platform() == MEMORY_PLATFORM_BITBOX02_PLUS) {
70+
da14531_handler_current_product = (const uint8_t*)DEVICE_MODE;
71+
da14531_handler_current_product_len = sizeof(DEVICE_MODE) - 1;
72+
da14531_set_product(
73+
da14531_handler_current_product, da14531_handler_current_product_len, uart_out_queue);
74+
}
75+
usb_start();
76+
}
77+
4878
void firmware_main_loop(void)
4979
{
5080
// Set the size of uart_read_buf to the size of the ringbuffer in the UART driver so we can read
@@ -63,7 +93,8 @@ void firmware_main_loop(void)
6393
da14531_set_name(buf, strlen(buf), &uart_write_queue);
6494

6595
// This starts the async orientation screen workflow, which is processed by the loop below.
66-
orientation_screen(&uart_write_queue);
96+
97+
rust_workflow_spawn_orientation_screen(_orientation_done_cb, &uart_write_queue);
6798

6899
const uint8_t* hww_data = NULL;
69100
uint8_t hww_frame[USB_REPORT_SIZE] = {0};

src/rust/bitbox02-rust-c/src/workflow.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ extern crate alloc;
2424

2525
use alloc::boxed::Box;
2626
use alloc::string::String;
27-
use bitbox02_rust::workflow::confirm;
27+
use bitbox02_rust::workflow::{confirm, orientation_screen};
2828
use core::task::Poll;
2929
use util::bb02_async::{Task, spin};
3030

@@ -42,6 +42,8 @@ static mut CONFIRM_PARAMS: Option<confirm::Params> = None;
4242
static mut CONFIRM_STATE: TaskState<'static, Result<(), confirm::UserAbort>> = TaskState::Nothing;
4343
static mut BITBOX02_HAL: bitbox02_rust::hal::BitBox02Hal = bitbox02_rust::hal::BitBox02Hal::new();
4444

45+
static mut ORIENTATION_SCREEN_STATE: TaskState<'static, ()> = TaskState::Nothing;
46+
4547
#[unsafe(no_mangle)]
4648
pub unsafe extern "C" fn rust_workflow_spawn_unlock() {
4749
unsafe {
@@ -70,6 +72,22 @@ pub unsafe extern "C" fn rust_workflow_spawn_confirm(
7072
}
7173
}
7274

75+
#[unsafe(no_mangle)]
76+
pub unsafe extern "C" fn rust_workflow_spawn_orientation_screen(
77+
orientation_selected_cb: Option<extern "C" fn(*mut core::ffi::c_void)>,
78+
user_data: *mut core::ffi::c_void,
79+
) {
80+
let callback = move || {
81+
if let Some(cb) = orientation_selected_cb {
82+
cb(user_data)
83+
}
84+
};
85+
unsafe {
86+
ORIENTATION_SCREEN_STATE =
87+
TaskState::Running(Box::pin(orientation_screen::create(callback)));
88+
}
89+
}
90+
7391
#[unsafe(no_mangle)]
7492
pub unsafe extern "C" fn rust_workflow_spin() {
7593
unsafe {
@@ -91,6 +109,15 @@ pub unsafe extern "C" fn rust_workflow_spin() {
91109
}
92110
_ => (),
93111
}
112+
match ORIENTATION_SCREEN_STATE {
113+
TaskState::Running(ref mut task) => {
114+
let result = spin(task);
115+
if let Poll::Ready(result) = result {
116+
ORIENTATION_SCREEN_STATE = TaskState::ResultAvailable(result);
117+
}
118+
}
119+
_ => (),
120+
}
94121
}
95122
}
96123

@@ -130,6 +157,20 @@ pub unsafe extern "C" fn rust_workflow_confirm_poll(result_out: &mut bool) -> bo
130157
}
131158
}
132159

160+
/// Returns true if there was a result.
161+
#[unsafe(no_mangle)]
162+
pub unsafe extern "C" fn rust_workflow_orientation_screen_poll() -> bool {
163+
unsafe {
164+
match ORIENTATION_SCREEN_STATE {
165+
TaskState::ResultAvailable(_) => {
166+
ORIENTATION_SCREEN_STATE = TaskState::Nothing;
167+
true
168+
}
169+
_ => false,
170+
}
171+
}
172+
}
173+
133174
#[unsafe(no_mangle)]
134175
pub unsafe extern "C" fn rust_workflow_abort_current() {
135176
unsafe {
@@ -139,5 +180,7 @@ pub unsafe extern "C" fn rust_workflow_abort_current() {
139180
CONFIRM_BODY = None;
140181
CONFIRM_PARAMS = None;
141182
CONFIRM_STATE = TaskState::Nothing;
183+
184+
ORIENTATION_SCREEN_STATE = TaskState::Nothing;
142185
}
143186
}

src/rust/bitbox02-rust/src/workflow.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod confirm;
1717
pub mod menu;
1818
#[cfg_attr(feature = "c-unit-testing", path = "workflow/mnemonic_c_unit_tests.rs")]
1919
pub mod mnemonic;
20+
pub mod orientation_screen;
2021
pub mod pairing;
2122
pub mod password;
2223
pub mod sdcard;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use crate::bb02_async::option_no_screensaver;
16+
17+
pub async fn create<CB>(orientation_selected_cb: CB)
18+
where
19+
CB: FnOnce(),
20+
{
21+
let result = core::cell::RefCell::new(None as Option<()>);
22+
let mut orientation_arrows = bitbox02::ui::orientation_arrows(|upside_down| {
23+
if upside_down {
24+
bitbox02::screen_rotate()
25+
}
26+
*result.borrow_mut() = Some(());
27+
});
28+
orientation_arrows.screen_stack_push();
29+
30+
// Wait until orientation has been chosen
31+
option_no_screensaver(&result).await;
32+
drop(orientation_arrows);
33+
34+
// During this delay the bb02 logotype is shown
35+
if let Ok(delay) = bitbox02::delay::Delay::from_ms(1300) {
36+
delay.await;
37+
}
38+
39+
// Switch to lockscreen that shows "See the bitbox app" and device name
40+
bitbox02::ui::screen_process_waiting_switch_to_lockscreen();
41+
42+
orientation_selected_cb();
43+
}

src/rust/bitbox02-sys/build.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const ALLOWLIST_TYPES: &[&str] = &[
5050
"component_t",
5151
"confirm_params_t",
5252
"trinary_input_string_params_t",
53+
"delay_t",
5354
];
5455

5556
const ALLOWLIST_FNS: &[&str] = &[
@@ -64,12 +65,15 @@ const ALLOWLIST_FNS: &[&str] = &[
6465
"confirm_create",
6566
"confirm_transaction_address_create",
6667
"confirm_transaction_fee_create",
68+
"delay_init_ms",
6769
"delay_ms",
70+
"delay_poll",
71+
"delay_start",
6872
"delay_us",
6973
"empty_create",
70-
"unlock_animation_create",
7174
"keystore_bip39_mnemonic_to_seed",
7275
"keystore_copy_seed",
76+
"unlock_animation_create",
7377
"keystore_copy_bip39_seed",
7478
"keystore_create_and_store_seed",
7579
"keystore_encrypt_and_store_seed",
@@ -117,6 +121,7 @@ const ALLOWLIST_FNS: &[&str] = &[
117121
"menu_create",
118122
"fake_memory_factoryreset",
119123
"spi_mem_full_erase",
124+
"orientation_arrows_create",
120125
"printf",
121126
"progress_create",
122127
"progress_set",
@@ -129,8 +134,10 @@ const ALLOWLIST_FNS: &[&str] = &[
129134
"screen_print_debug",
130135
"screen_process",
131136
"screen_process_waiting_switch_to_logo",
137+
"screen_process_waiting_switch_to_lockscreen",
132138
"screen_saver_disable",
133139
"screen_saver_enable",
140+
"screen_rotate",
134141
"sd_card_inserted",
135142
"sd_erase_file_in_subdir",
136143
"sd_format",
@@ -248,7 +255,6 @@ const BITBOX02_SOURCES: &[&str] = &[
248255
"src/usb/usb_processing.c",
249256
"src/usb/usb.c",
250257
"src/util.c",
251-
"src/workflow/orientation_screen.c",
252258
"external/asf4-drivers/hal/utils/src/utils_ringbuffer.c",
253259
];
254260

@@ -410,6 +416,7 @@ pub fn main() -> Result<(), &'static str> {
410416
"test/hardware-fakes/src/fake_cipher.c",
411417
"test/hardware-fakes/src/fake_component.c",
412418
"test/hardware-fakes/src/fake_diskio.c",
419+
"test/hardware-fakes/src/fake_delay.c",
413420
"test/hardware-fakes/src/fake_memory.c",
414421
"test/hardware-fakes/src/fake_qtouch.c",
415422
"test/hardware-fakes/src/fake_screen.c",

0 commit comments

Comments
 (0)