diff --git a/core/embed/io/button/button_fsm.c b/core/embed/io/button/button_fsm.c
new file mode 100644
index 0000000000..f28ae43970
--- /dev/null
+++ b/core/embed/io/button/button_fsm.c
@@ -0,0 +1,86 @@
+/*
+ * 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 .
+ */
+
+#ifdef KERNEL_MODE
+
+#include
+
+#include
+
+#include "button_fsm.h"
+
+void button_fsm_init(button_fsm_t* fsm) {
+ memset(fsm, 0, sizeof(button_fsm_t));
+}
+
+bool button_fsm_event_ready(button_fsm_t* fsm, uint32_t new_state) {
+ // Remember state changes
+ fsm->pressed |= new_state & ~fsm->state;
+ fsm->released |= ~new_state & fsm->state;
+ fsm->time = systick_us();
+ // Return true if there are any state changes
+ return (fsm->pressed | fsm->released) != 0;
+}
+
+bool button_fsm_get_event(button_fsm_t* fsm, uint32_t new_state,
+ button_event_t* event) {
+ uint64_t now = systick_us();
+
+ if ((now - fsm->time) > 100000) {
+ // Reset the history if the button was not read for 100ms
+ fsm->pressed = 0;
+ fsm->released = 0;
+ }
+
+ // Remember state changes and the time of the last read
+ fsm->pressed |= new_state & ~fsm->state;
+ fsm->released |= ~new_state & fsm->state;
+
+ // Bring the automaton out of invalid states,
+ // in case it somehow ends up in one.
+ fsm->released &= fsm->pressed | fsm->state;
+ fsm->pressed &= fsm->released | ~fsm->state;
+
+ uint8_t button_idx = 0;
+ while (fsm->pressed | fsm->released) {
+ uint32_t mask = 1 << button_idx;
+
+ if ((fsm->pressed & mask) != 0 && (fsm->state & mask) == 0) {
+ // Button press was not signalled yet
+ fsm->pressed &= ~mask;
+ fsm->state |= mask;
+ event->button = (button_t)button_idx;
+ event->event_type = BTN_EVENT_DOWN;
+ return true;
+ } else if ((fsm->released & mask) != 0 && (fsm->state & mask) != 0) {
+ // Button release was not signalled yet
+ fsm->released &= ~mask;
+ fsm->state &= ~mask;
+ event->button = (button_t)button_idx;
+ event->event_type = BTN_EVENT_UP;
+ return true;
+ }
+
+ ++button_idx;
+ }
+
+ return false;
+}
+
+#endif // KERNEL_MODE
diff --git a/core/embed/io/button/button_fsm.h b/core/embed/io/button/button_fsm.h
new file mode 100644
index 0000000000..bfb66ffe50
--- /dev/null
+++ b/core/embed/io/button/button_fsm.h
@@ -0,0 +1,59 @@
+/*
+ * 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 .
+ */
+
+#pragma once
+
+#include
+
+// This module is a simple finite state machine for buttons.
+//
+// It is designed to be used in a polling loop, where the state of the buttons
+// is read periodically. The module keeps track of the state changes and
+// provides a simple interface to get the events that happened since the last
+// call to button_fsm_get_event().
+//
+// The structure is designed to be used in a multi-threaded environment, where
+// each thread has its own state machine. The state machines are stored in an
+// array indexed by the task ID.
+
+typedef struct {
+ // Time of last update of pressed/released data
+ uint64_t time;
+ // Button presses that were detected since last get_event call
+ uint32_t pressed;
+ // Button releases that were detected since last get_event call
+ uint32_t released;
+ // State of buttons signalled to the poller
+ uint32_t state;
+} button_fsm_t;
+
+// Initializes button finite state machine
+void button_fsm_init(button_fsm_t* fsm);
+
+// Checks if button_fsm_get_event() would return `true` on the next call
+bool button_fsm_event_ready(button_fsm_t* fsm, uint32_t new_state);
+
+// Processes the new_state of the button and fills the event structure.
+//
+// `new_state` is the current state of the buttons - each bit represents
+// the state of one button (up to 32 buttons can be handled simultaneously).
+//
+// Returns `true` if the event structure was filled.
+bool button_fsm_get_event(button_fsm_t* fsm, uint32_t new_state,
+ button_event_t* event);
diff --git a/core/embed/io/button/inc/io/button.h b/core/embed/io/button/inc/io/button.h
index 7db72ff656..4335bb278b 100644
--- a/core/embed/io/button/inc/io/button.h
+++ b/core/embed/io/button/inc/io/button.h
@@ -68,8 +68,4 @@ void button_deinit(void);
bool button_get_event(button_event_t* event);
// Checks if the specified button is currently pressed
-//
-// The current implementation returns the state of the button at the time
-// `button_get_event()` was called. In the future, we may fix this limitation.
-// For now, `button_get_event()` must be called before `button_is_down()`.
bool button_is_down(button_t button);
diff --git a/core/embed/io/button/stm32/button.c b/core/embed/io/button/stm32/button.c
index a426ae1b9c..4437505af8 100644
--- a/core/embed/io/button/stm32/button.c
+++ b/core/embed/io/button/stm32/button.c
@@ -23,6 +23,9 @@
#include
#include
#include
+#include
+
+#include "../button_fsm.h"
#ifdef USE_POWERCTL
#include
@@ -34,15 +37,8 @@
typedef struct {
bool initialized;
-#ifdef BTN_LEFT_PIN
- bool left_down;
-#endif
-#ifdef BTN_RIGHT_PIN
- bool right_down;
-#endif
-#ifdef BTN_POWER_PIN
- bool power_down;
-#endif
+ // Each task has its own state machine
+ button_fsm_t tls[SYSTASK_MAX_TASKS];
} button_driver_t;
@@ -51,7 +47,10 @@ static button_driver_t g_button_driver = {
.initialized = false,
};
-static void button_setup_pin(GPIO_TypeDef *port, uint16_t pin) {
+// Forward declarations
+static const syshandle_vmt_t g_button_handle_vmt;
+
+static void button_setup_pin(GPIO_TypeDef* port, uint16_t pin) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
@@ -62,7 +61,7 @@ static void button_setup_pin(GPIO_TypeDef *port, uint16_t pin) {
}
bool button_init(void) {
- button_driver_t *drv = &g_button_driver;
+ button_driver_t* drv = &g_button_driver;
if (drv->initialized) {
return true;
@@ -99,106 +98,76 @@ bool button_init(void) {
NVIC_EnableIRQ(BTN_EXTI_INTERRUPT_NUM);
#endif // BTN_EXTI_INTERRUPT_HANDLER
- drv->initialized = true;
+ if (!syshandle_register(SYSHANDLE_BUTTON, &g_button_handle_vmt, drv)) {
+ goto cleanup;
+ }
+ drv->initialized = true;
return true;
+
+cleanup:
+ button_deinit();
+ return false;
}
void button_deinit(void) {
+ button_driver_t* drv = &g_button_driver;
+
+ syshandle_unregister(SYSHANDLE_BUTTON);
+
#ifdef BTN_EXIT_INTERRUPT_HANDLER
NVIC_DisableIRQ(BTN_EXTI_INTERRUPT_NUM);
#endif
+
+ memset(drv, 0, sizeof(button_driver_t));
}
-bool button_get_event(button_event_t *event) {
- button_driver_t *drv = &g_button_driver;
+static uint32_t button_read_state(button_driver_t* drv) {
+ UNUSED(drv);
+ uint32_t state = 0;
+#ifdef BTN_LEFT_PIN
+ if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_LEFT_PORT, BTN_LEFT_PIN)) {
+ state |= (1U << BTN_LEFT);
+ }
+#endif
+
+#ifdef BTN_RIGHT_PIN
+ if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_RIGHT_PORT, BTN_RIGHT_PIN)) {
+ state |= (1U << BTN_RIGHT);
+ }
+#endif
+
+#ifdef BTN_POWER_PIN
+ if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_POWER_PORT, BTN_POWER_PIN)) {
+ state |= (1U << BTN_POWER);
+ }
+#endif
+ return state;
+}
+
+bool button_get_event(button_event_t* event) {
+ button_driver_t* drv = &g_button_driver;
memset(event, 0, sizeof(*event));
if (!drv->initialized) {
return false;
}
-#ifdef BTN_LEFT_PIN
- bool left_down =
- (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_LEFT_PORT, BTN_LEFT_PIN));
+ uint32_t new_state = button_read_state(drv);
- if (drv->left_down != left_down) {
- drv->left_down = left_down;
- if (left_down) {
- event->button = BTN_LEFT;
- event->event_type = BTN_EVENT_DOWN;
- return true;
- } else {
- event->button = BTN_LEFT;
- event->event_type = BTN_EVENT_UP;
- return true;
- }
- }
-#endif
-
-#ifdef BTN_RIGHT_PIN
- bool right_down =
- (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_RIGHT_PORT, BTN_RIGHT_PIN));
-
- if (drv->right_down != right_down) {
- drv->right_down = right_down;
- if (right_down) {
- event->button = BTN_RIGHT;
- event->event_type = BTN_EVENT_DOWN;
- return true;
- } else {
- event->button = BTN_RIGHT;
- event->event_type = BTN_EVENT_UP;
- return true;
- }
- }
-#endif
-
-#ifdef BTN_POWER_PIN
- bool power_down =
- (GPIO_PIN_RESET == HAL_GPIO_ReadPin(BTN_POWER_PORT, BTN_POWER_PIN));
-
- if (drv->power_down != power_down) {
- drv->power_down = power_down;
- if (power_down) {
- event->button = BTN_POWER;
- event->event_type = BTN_EVENT_DOWN;
- return true;
- } else {
- event->button = BTN_POWER;
- event->event_type = BTN_EVENT_UP;
- return true;
- }
- }
-#endif
-
- return 0;
+ button_fsm_t* fsm = &drv->tls[systask_id(systask_active())];
+ return button_fsm_get_event(fsm, new_state, event);
}
bool button_is_down(button_t button) {
- button_driver_t *drv = &g_button_driver;
+ button_driver_t* drv = &g_button_driver;
if (!drv->initialized) {
return false;
}
- switch (button) {
-#ifdef BTN_LEFT_PIN
- case BTN_LEFT:
- return drv->left_down;
-#endif
-#ifdef BTN_RIGHT_PIN
- case BTN_RIGHT:
- return drv->right_down;
-#endif
-#ifdef BTN_POWER_PIN
- case BTN_POWER:
- return drv->power_down;
-#endif
- default:
- return false;
- }
+ return (button_read_state(drv) & (1 << button)) != 0;
}
#ifdef BTN_EXTI_INTERRUPT_HANDLER
@@ -221,4 +190,40 @@ void BTN_EXTI_INTERRUPT_HANDLER(void) {
}
#endif
+static void on_task_created(void* context, systask_id_t task_id) {
+ button_driver_t* drv = (button_driver_t*)context;
+ button_fsm_t* fsm = &drv->tls[task_id];
+ button_fsm_init(fsm);
+}
+
+static void on_event_poll(void* context, bool read_awaited,
+ bool write_awaited) {
+ button_driver_t* drv = (button_driver_t*)context;
+
+ UNUSED(write_awaited);
+
+ if (read_awaited) {
+ uint32_t state = button_read_state(drv);
+ syshandle_signal_read_ready(SYSHANDLE_BUTTON, &state);
+ }
+}
+
+static bool on_check_read_ready(void* context, systask_id_t task_id,
+ void* param) {
+ button_driver_t* drv = (button_driver_t*)context;
+ button_fsm_t* fsm = &drv->tls[task_id];
+
+ uint32_t new_state = *(uint32_t*)param;
+
+ return button_fsm_event_ready(fsm, new_state);
+}
+
+static const syshandle_vmt_t g_button_handle_vmt = {
+ .task_created = on_task_created,
+ .task_killed = NULL,
+ .check_read_ready = on_check_read_ready,
+ .check_write_ready = NULL,
+ .poll = on_event_poll,
+};
+
#endif // KERNEL_MODE
diff --git a/core/embed/io/button/unix/button.c b/core/embed/io/button/unix/button.c
index e66c1cfd0e..f4647b7f49 100644
--- a/core/embed/io/button/unix/button.c
+++ b/core/embed/io/button/unix/button.c
@@ -20,23 +20,19 @@
#include
#include
-#include
-
#include
+#include
+#include
+
+#include "../button_fsm.h"
// Button driver state
typedef struct {
bool initialized;
-
-#ifdef BTN_LEFT_KEY
- bool left_down;
-#endif
-#ifdef BTN_RIGHT_KEY
- bool right_down;
-#endif
-#ifdef BTN_POWER_KEY
- bool power_down;
-#endif
+ // Global state of buttons
+ uint32_t state;
+ // Each task has its own state machine
+ button_fsm_t tls[SYSTASK_MAX_TASKS];
} button_driver_t;
// Button driver instance
@@ -44,8 +40,12 @@ static button_driver_t g_button_driver = {
.initialized = false,
};
+// Forward declarations
+static const syshandle_vmt_t g_button_handle_vmt;
+static void button_sdl_event_filter(void* context, SDL_Event* sdl_event);
+
bool button_init(void) {
- button_driver_t *drv = &g_button_driver;
+ button_driver_t* drv = &g_button_driver;
if (drv->initialized) {
return true;
@@ -53,79 +53,128 @@ bool button_init(void) {
memset(drv, 0, sizeof(button_driver_t));
+ if (!syshandle_register(SYSHANDLE_BUTTON, &g_button_handle_vmt, drv)) {
+ goto cleanup;
+ }
+
+ if (!sdl_events_register(button_sdl_event_filter, drv)) {
+ goto cleanup;
+ }
+
drv->initialized = true;
-
return true;
-}
-
-bool button_get_event(button_event_t *event) {
- button_driver_t *drv = &g_button_driver;
-
- memset(event, 0, sizeof(button_event_t));
-
- if (!drv->initialized) {
- return 0;
- }
-
- SDL_Event sdl_event;
-
- if (SDL_PollEvent(&sdl_event) > 0 &&
- (sdl_event.type == SDL_KEYDOWN || sdl_event.type == SDL_KEYUP) &&
- !sdl_event.key.repeat) {
- bool down = (sdl_event.type == SDL_KEYDOWN);
- uint32_t evt_type = down ? BTN_EVENT_DOWN : BTN_EVENT_UP;
-
- switch (sdl_event.key.keysym.sym) {
-#ifdef BTN_LEFT_KEY
- case BTN_LEFT_KEY:
- drv->left_down = down;
- event->event_type = evt_type;
- event->button = BTN_LEFT;
- return true;
-#endif
-#ifdef BTN_RIGHT_KEY
- case BTN_RIGHT_KEY:
- drv->right_down = down;
- event->event_type = evt_type;
- event->button = BTN_RIGHT;
- return true;
-#endif
-#ifdef BTN_POWER_KEY
- case BTN_POWER_KEY:
- drv->power_down = down;
- event->event_type = evt_type;
- event->button = BTN_POWER;
- return true;
-#endif
- default:
- break;
- }
- }
+cleanup:
+ button_deinit();
return false;
}
-bool button_is_down(button_t button) {
- button_driver_t *drv = &g_button_driver;
+void button_deinit(void) {
+ button_driver_t* drv = &g_button_driver;
+
+ syshandle_unregister(SYSHANDLE_BUTTON);
+
+ sdl_events_unregister(button_sdl_event_filter, drv);
+
+ memset(drv, 0, sizeof(button_driver_t));
+}
+
+// Called from global event loop to filter and process SDL events
+static void button_sdl_event_filter(void* context, SDL_Event* sdl_event) {
+ button_driver_t* drv = &g_button_driver;
+
+ if (sdl_event->type != SDL_KEYDOWN && sdl_event->type != SDL_KEYUP) {
+ return;
+ }
+
+ if (sdl_event->key.repeat) {
+ return;
+ }
+
+ button_t button;
+
+ switch (sdl_event->key.keysym.sym) {
+#ifdef BTN_LEFT_KEY
+ case BTN_LEFT_KEY:
+ button = BTN_LEFT;
+ break;
+#endif
+#ifdef BTN_RIGHT_KEY
+ case BTN_RIGHT_KEY:
+ button = BTN_RIGHT;
+ break;
+#endif
+#ifdef BTN_POWER_KEY
+ case BTN_POWER_KEY:
+ button = BTN_POWER;
+ break;
+#endif
+ default:
+ return;
+ }
+
+ if (sdl_event->type == SDL_KEYDOWN) {
+ drv->state |= (1 << button);
+ } else {
+ drv->state &= ~(1 << button);
+ }
+}
+
+static uint32_t button_read_state(button_driver_t* drv) {
+ sdl_events_poll();
+ return drv->state;
+}
+
+bool button_get_event(button_event_t* event) {
+ button_driver_t* drv = &g_button_driver;
+ memset(event, 0, sizeof(*event));
if (!drv->initialized) {
return false;
}
- switch (button) {
-#ifdef BTN_LEFT_KEY
- case BTN_LEFT:
- return drv->left_down;
-#endif
-#ifdef BTN_RIGHT_KEY
- case BTN_RIGHT:
- return drv->right_down;
-#endif
-#ifdef BTN_POWER_KEY
- case BTN_POWER:
- return drv->power_down;
-#endif
- default:
- return false;
+ uint32_t new_state = button_read_state(drv);
+
+ button_fsm_t* fsm = &drv->tls[systask_id(systask_active())];
+ return button_fsm_get_event(fsm, new_state, event);
+}
+
+bool button_is_down(button_t button) {
+ button_driver_t* drv = &g_button_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ return (button_read_state(drv) & (1 << button)) != 0;
+}
+
+static void on_event_poll(void* context, bool read_awaited,
+ bool write_awaited) {
+ button_driver_t* drv = (button_driver_t*)context;
+
+ UNUSED(write_awaited);
+
+ if (read_awaited) {
+ uint32_t state = button_read_state(drv);
+ syshandle_signal_read_ready(SYSHANDLE_BUTTON, &state);
}
}
+
+static bool on_check_read_ready(void* context, systask_id_t task_id,
+ void* param) {
+ button_driver_t* drv = (button_driver_t*)context;
+ button_fsm_t* fsm = &drv->tls[task_id];
+
+ uint32_t new_state = *(uint32_t*)param;
+
+ return button_fsm_event_ready(fsm, new_state);
+}
+
+static const syshandle_vmt_t g_button_handle_vmt = {
+ .task_created = NULL,
+ .task_killed = NULL,
+ .check_read_ready = on_check_read_ready,
+ .check_write_ready = NULL,
+ .poll = on_event_poll,
+};
diff --git a/core/embed/projects/bootloader/bootui.c b/core/embed/projects/bootloader/bootui.c
index 800161ba8d..eb4d94f51b 100644
--- a/core/embed/projects/bootloader/bootui.c
+++ b/core/embed/projects/bootloader/bootui.c
@@ -70,15 +70,11 @@ void ui_click(void) {
void ui_click(void) {
for (;;) {
- button_event_t event = {0};
- button_get_event(&event);
if (button_is_down(BTN_LEFT) && button_is_down(BTN_RIGHT)) {
break;
}
}
for (;;) {
- button_event_t event = {0};
- button_get_event(&event);
if (!button_is_down(BTN_LEFT) && !button_is_down(BTN_RIGHT)) {
break;
}
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index 5b6f9f6b5b..65a4934146 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -362,8 +362,6 @@ int bootloader_main(void) {
}
}
#elif defined USE_BUTTON
- button_event_t btn_evt = {0};
- button_get_event(&btn_evt);
if (button_is_down(BTN_LEFT)) {
touched = 1;
}
diff --git a/core/embed/projects/prodtest/cmd/prodtest_button.c b/core/embed/projects/prodtest/cmd/prodtest_button.c
index d899119741..c8ac570022 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_button.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_button.c
@@ -67,10 +67,6 @@ static void test_button_combination(cli_t* cli, uint32_t timeout, button_t btn1,
cli_trace(cli, "Waiting for button combination to be pressed...");
while (true) {
- // Event must be read before calling `button_is_down()`
- button_event_t e = {0};
- button_get_event(&e);
-
if (button_is_down(btn1) && button_is_down(btn2)) {
break;
} else if (ticks_expired(expire_time)) {
@@ -84,10 +80,6 @@ static void test_button_combination(cli_t* cli, uint32_t timeout, button_t btn1,
cli_trace(cli, "Waiting for buttons to be released...");
while (true) {
- // Event must be read before calling `button_is_down()`
- button_event_t e = {0};
- button_get_event(&e);
-
if (!button_is_down(btn1) && !button_is_down(btn2)) {
break;
} else if (ticks_expired(expire_time)) {
diff --git a/core/site_scons/models/T2B1/emulator.py b/core/site_scons/models/T2B1/emulator.py
index 6f20161db2..172fd65ddf 100644
--- a/core/site_scons/models/T2B1/emulator.py
+++ b/core/site_scons/models/T2B1/emulator.py
@@ -50,6 +50,7 @@ def configure(
if "input" in features_wanted:
sources += ["embed/io/button/unix/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [("USE_BUTTON", "1")]
diff --git a/core/site_scons/models/T2B1/trezor_r_v10.py b/core/site_scons/models/T2B1/trezor_r_v10.py
index 98eb84a501..3781ff0985 100644
--- a/core/site_scons/models/T2B1/trezor_r_v10.py
+++ b/core/site_scons/models/T2B1/trezor_r_v10.py
@@ -50,6 +50,7 @@ def configure(
if "input" in features_wanted:
sources += ["embed/io/button/stm32/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [("USE_BUTTON", "1")]
diff --git a/core/site_scons/models/T3B1/emulator.py b/core/site_scons/models/T3B1/emulator.py
index 4a4654b516..a24f57c2df 100644
--- a/core/site_scons/models/T3B1/emulator.py
+++ b/core/site_scons/models/T3B1/emulator.py
@@ -50,6 +50,7 @@ def configure(
if "input" in features_wanted:
sources += ["embed/io/button/unix/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [("USE_BUTTON", "1")]
diff --git a/core/site_scons/models/T3B1/trezor_t3b1_revB.py b/core/site_scons/models/T3B1/trezor_t3b1_revB.py
index fd8bc66bb8..b448448fa0 100644
--- a/core/site_scons/models/T3B1/trezor_t3b1_revB.py
+++ b/core/site_scons/models/T3B1/trezor_t3b1_revB.py
@@ -49,6 +49,7 @@ def configure(
if "input" in features_wanted:
sources += ["embed/io/button/stm32/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [("USE_BUTTON", "1")]
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
index 05bfcc8194..04b7e47fa7 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
@@ -63,6 +63,7 @@ def configure(
paths += ["embed/io/touch/inc"]
features_available.append("touch")
sources += ["embed/io/button/stm32/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
index 79f1d98a03..e36da80dab 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
@@ -63,6 +63,7 @@ def configure(
paths += ["embed/io/touch/inc"]
features_available.append("touch")
sources += ["embed/io/button/stm32/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py
index b737b8e59e..611e1d5eb0 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py
@@ -63,6 +63,7 @@ def configure(
paths += ["embed/io/touch/inc"]
features_available.append("touch")
sources += ["embed/io/button/stm32/button.c"]
+ sources += ["embed/io/button/button_fsm.c"]
paths += ["embed/io/button/inc"]
features_available.append("button")
defines += [