Skip to content

Commit f2dd8dd

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 5a00420 commit f2dd8dd

File tree

20 files changed

+450
-149
lines changed

20 files changed

+450
-149
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: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 <util.h>
22+
#include <utils_assert.h>
23+
24+
struct task {
25+
struct timer_task timer;
26+
volatile bool done;
27+
};
28+
29+
static struct task _tasks[10] = {0};
30+
static struct task _empty = {0};
31+
32+
static void _hal_timer_cb(const struct timer_task* const timer)
33+
{
34+
for (size_t i = 0; i < COUNT_OF(_tasks); i++) {
35+
if (&_tasks[i].timer == timer) {
36+
_tasks[i].done = true;
37+
}
38+
}
39+
}
40+
41+
bool delay_init_ms(delay_t* self, uint32_t ms)
42+
{
43+
// find an unused slot in tasks
44+
size_t i;
45+
CRITICAL_SECTION_ENTER();
46+
for (i = 0; i < COUNT_OF(_tasks); i++) {
47+
if (memcmp(&_tasks[i], &_empty, sizeof(struct task)) == 0) {
48+
break;
49+
}
50+
}
51+
CRITICAL_SECTION_LEAVE();
52+
if (i == COUNT_OF(_tasks)) {
53+
return false;
54+
}
55+
_tasks[i].done = false;
56+
memset(&_tasks[i], 0, sizeof(struct task));
57+
_tasks[i].timer.interval = ms;
58+
_tasks[i].timer.cb = _hal_timer_cb;
59+
_tasks[i].timer.mode = TIMER_TASK_ONE_SHOT;
60+
self->id = i;
61+
return true;
62+
}
63+
64+
void delay_start(const delay_t* self)
65+
{
66+
ASSERT(self->id < COUNT_OF(_tasks));
67+
ASSERT(!_tasks[self->id].done);
68+
timer_add_task(&TIMER_0, &_tasks[self->id].timer);
69+
}
70+
71+
bool delay_poll(const delay_t* self)
72+
{
73+
ASSERT(self->id < COUNT_OF(_tasks));
74+
if (_tasks[self->id].done) {
75+
memset(&_tasks[self->id], 0, sizeof(struct task));
76+
return true;
77+
}
78+
return false;
79+
}
Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2019 Shift Cryptosecurity AG
1+
// Copyright 2025 Shift Crypto AG
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -12,15 +12,23 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#ifndef DELAY_H
16+
#define DELAY_H
17+
#include <stdbool.h>
18+
#include <stddef.h>
1519
#include <stdint.h>
16-
#include <unistd.h>
1720

18-
void delay_ms(const uint16_t ms)
19-
{
20-
usleep(1000 * ms);
21-
}
21+
typedef struct {
22+
size_t id;
23+
} delay_t;
2224

23-
void delay_us(const uint16_t us)
24-
{
25-
usleep(us);
26-
}
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/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ extern crate util;
3939
// handler will print the available information on the screen and over RTT. If we compile with
4040
// `panic=abort` this code will never get executed.
4141
#[cfg_attr(feature = "bootloader", allow(unused_variables))]
42-
#[cfg(not(any(test, feature = "testing", feature = "c-unit-testing")))]
42+
#[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
4343
#[panic_handler]
4444
fn panic(info: &core::panic::PanicInfo) -> ! {
4545
#[cfg(feature = "firmware")]

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 {
@@ -71,6 +73,22 @@ pub unsafe extern "C" fn rust_workflow_spawn_confirm(
7173
}
7274
}
7375

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

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

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

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 util::bb02_async::option;
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(&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: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,15 @@ const ALLOWLIST_FNS: &[&str] = &[
6868
"confirm_create",
6969
"confirm_transaction_address_create",
7070
"confirm_transaction_fee_create",
71+
"delay_init_ms",
7172
"delay_ms",
73+
"delay_poll",
74+
"delay_start",
7275
"delay_us",
7376
"empty_create",
74-
"unlock_animation_create",
7577
"keystore_bip39_mnemonic_to_seed",
7678
"keystore_copy_seed",
79+
"unlock_animation_create",
7780
"keystore_copy_bip39_seed",
7881
"keystore_encrypt_and_store_seed",
7982
"keystore_get_bip39_word",
@@ -121,6 +124,7 @@ const ALLOWLIST_FNS: &[&str] = &[
121124
"menu_create",
122125
"fake_memory_factoryreset",
123126
"spi_mem_full_erase",
127+
"orientation_arrows_create",
124128
"printf",
125129
"progress_create",
126130
"progress_set",
@@ -133,8 +137,10 @@ const ALLOWLIST_FNS: &[&str] = &[
133137
"screen_print_debug",
134138
"screen_process",
135139
"screen_process_waiting_switch_to_logo",
140+
"screen_process_waiting_switch_to_lockscreen",
136141
"screen_saver_disable",
137142
"screen_saver_enable",
143+
"screen_rotate",
138144
"sd_card_inserted",
139145
"sd_erase_file_in_subdir",
140146
"sd_format",
@@ -256,7 +262,6 @@ const BITBOX02_SOURCES: &[&str] = &[
256262
"src/usb/usb_processing.c",
257263
"src/usb/usb.c",
258264
"src/util.c",
259-
"src/workflow/orientation_screen.c",
260265
"external/asf4-drivers/hal/utils/src/utils_ringbuffer.c",
261266
];
262267

0 commit comments

Comments
 (0)