diff --git a/core/embed/upymod/modtrezorio/modtrezorio-ble.h b/core/embed/upymod/modtrezorio/modtrezorio-ble.h
new file mode 100644
index 0000000000..b348f9973f
--- /dev/null
+++ b/core/embed/upymod/modtrezorio/modtrezorio-ble.h
@@ -0,0 +1,208 @@
+/*
+ * 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 .
+ */
+
+// #include "ble/dfu.h"
+// #include "ble/messages.h"
+
+/// package: trezorio.ble
+
+// /// def update_init(data: bytes, binsize: int) -> int:
+// /// """
+// /// Initializes the BLE firmware update
+// /// """
+// STATIC mp_obj_t mod_trezorio_BLE_update_init(mp_obj_t data, mp_obj_t binsize)
+// {
+// mp_buffer_info_t buffer = {0};
+// mp_int_t binsize_int = mp_obj_get_int(binsize);
+//
+// mp_get_buffer_raise(data, &buffer, MP_BUFFER_READ);
+//
+// ble_set_dfu_mode(true);
+//
+// dfu_result_t result = dfu_update_init(buffer.buf, buffer.len, binsize_int);
+// if (result == DFU_NEXT_CHUNK) {
+// return mp_obj_new_int(0);
+// } else if (result == DFU_SUCCESS) {
+// ble_set_dfu_mode(false);
+// return mp_obj_new_int(1);
+// } else {
+// ble_set_dfu_mode(false);
+// mp_raise_msg(&mp_type_RuntimeError, "Upload failed.");
+// }
+// }
+// STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_update_init_obj,
+// mod_trezorio_BLE_update_init);
+//
+// /// def update_chunk(chunk: bytes) -> int:
+// /// """
+// /// Writes next chunk of BLE firmware update
+// /// """
+// STATIC mp_obj_t mod_trezorio_BLE_update_chunk(mp_obj_t data) {
+// mp_buffer_info_t buffer = {0};
+//
+// mp_get_buffer_raise(data, &buffer, MP_BUFFER_READ);
+//
+// dfu_result_t result = dfu_update_chunk(buffer.buf, buffer.len);
+//
+// if (result == DFU_NEXT_CHUNK) {
+// return mp_obj_new_int(0);
+// } else if (result == DFU_SUCCESS) {
+// ble_set_dfu_mode(false);
+// return mp_obj_new_int(1);
+// } else {
+// ble_set_dfu_mode(false);
+// mp_raise_msg(&mp_type_RuntimeError, "Upload failed.");
+// }
+// }
+// STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_update_chunk_obj,
+// mod_trezorio_BLE_update_chunk);
+
+/// def write(msg: bytes) -> int:
+/// """
+/// Sends message over BLE
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_write(mp_obj_t msg) {
+ mp_buffer_info_t buf = {0};
+ mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
+ bool success = ble_write(buf.buf, buf.len);
+ if (success) {
+ return MP_OBJ_NEW_SMALL_INT(buf.len);
+ } else {
+ return MP_OBJ_NEW_SMALL_INT(-1);
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_write_obj,
+ mod_trezorio_BLE_write);
+
+/// def read(buf: bytes, offset: int = 0) -> int
+/// """
+/// Reads message using BLE (device).
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_read(size_t n_args, const mp_obj_t *args) {
+ mp_buffer_info_t buf = {0};
+ mp_get_buffer_raise(args[0], &buf, MP_BUFFER_WRITE);
+
+ int offset = 0;
+ if (n_args >= 1) {
+ offset = mp_obj_get_int(args[1]);
+ }
+
+ if (offset < 0) {
+ mp_raise_ValueError("Negative offset not allowed");
+ }
+
+ uint32_t buffer_space = buf.len - offset;
+
+ if (buffer_space < BLE_RX_PACKET_SIZE) {
+ mp_raise_ValueError("Buffer too small");
+ }
+
+ uint32_t r = ble_read(&((uint8_t *)buf.buf)[offset], BLE_RX_PACKET_SIZE);
+
+ if (r != BLE_RX_PACKET_SIZE) {
+ mp_raise_msg(&mp_type_RuntimeError, "Unexpected read length");
+ }
+
+ return MP_OBJ_NEW_SMALL_INT(r);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_BLE_read_obj, 1, 2,
+ mod_trezorio_BLE_read);
+
+/// def erase_bonds() -> None:
+/// """
+/// Erases all BLE bonds
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_erase_bonds(void) {
+ ble_issue_command(BLE_ERASE_BONDS);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_erase_bonds_obj,
+ mod_trezorio_BLE_erase_bonds);
+
+/// def start_comm() -> None:
+/// """
+/// Start communication with BLE chip
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_start_comm(void) {
+ ble_start();
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_start_comm_obj,
+ mod_trezorio_BLE_start_comm);
+
+/// def start_advertising(whitelist: bool) -> None:
+/// """
+/// Start advertising
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_start_advertising(mp_obj_t whitelist) {
+ bool whitelist_bool = mp_obj_is_true(whitelist);
+
+ ble_issue_command(whitelist_bool ? BLE_SWITCH_ON : BLE_PAIRING_MODE);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_start_advertising_obj,
+ mod_trezorio_BLE_start_advertising);
+
+/// def stop_advertising(whitelist: bool) -> None:
+/// """
+/// Stop advertising
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_stop_advertising(void) {
+ ble_issue_command(BLE_SWITCH_OFF);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_stop_advertising_obj,
+ mod_trezorio_BLE_stop_advertising);
+
+/// def disconnect() -> None:
+/// """
+/// Disconnect BLE
+/// """
+STATIC mp_obj_t mod_trezorio_BLE_disconnect(void) {
+ ble_issue_command(BLE_DISCONNECT);
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_disconnect_obj,
+ mod_trezorio_BLE_disconnect);
+
+STATIC const mp_rom_map_elem_t mod_trezorio_BLE_globals_table[] = {
+ {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ble)},
+ // {MP_ROM_QSTR(MP_QSTR_update_init),
+ // MP_ROM_PTR(&mod_trezorio_BLE_update_init_obj)},
+ // {MP_ROM_QSTR(MP_QSTR_update_chunk),
+ // MP_ROM_PTR(&mod_trezorio_BLE_update_chunk_obj)},
+ {MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mod_trezorio_BLE_write_obj)},
+ {MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mod_trezorio_BLE_read_obj)},
+ {MP_ROM_QSTR(MP_QSTR_erase_bonds),
+ MP_ROM_PTR(&mod_trezorio_BLE_erase_bonds_obj)},
+ {MP_ROM_QSTR(MP_QSTR_start_comm),
+ MP_ROM_PTR(&mod_trezorio_BLE_start_comm_obj)},
+ {MP_ROM_QSTR(MP_QSTR_start_advertising),
+ MP_ROM_PTR(&mod_trezorio_BLE_start_advertising_obj)},
+ {MP_ROM_QSTR(MP_QSTR_stop_advertising),
+ MP_ROM_PTR(&mod_trezorio_BLE_stop_advertising_obj)},
+ {MP_ROM_QSTR(MP_QSTR_disconnect),
+ MP_ROM_PTR(&mod_trezorio_BLE_disconnect_obj)},
+};
+STATIC MP_DEFINE_CONST_DICT(mod_trezorio_BLE_globals,
+ mod_trezorio_BLE_globals_table);
+
+STATIC const mp_obj_module_t mod_trezorio_BLE_module = {
+ .base = {&mp_type_module},
+ .globals = (mp_obj_dict_t *)&mod_trezorio_BLE_globals};
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-poll.h b/core/embed/upymod/modtrezorio/modtrezorio-poll.h
index 6467cd43a4..33e6342b11 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio-poll.h
+++ b/core/embed/upymod/modtrezorio/modtrezorio-poll.h
@@ -23,6 +23,10 @@
#include
#include
+#ifdef USE_BLE
+#include
+#endif
+
#ifdef USE_BUTTON
#include
#endif
@@ -33,9 +37,12 @@
#include "SDL.h"
#endif
+#define BLE_EVENT_IFACE (252)
#define USB_EVENT_IFACE (253)
#define BUTTON_IFACE (254)
#define TOUCH_IFACE (255)
+#define USB_RW_IFACE_MAX (15) // 0-15 reserved for USB
+#define BLE_IFACE (16)
#define POLL_READ (0x0000)
#define POLL_WRITE (0x0100)
@@ -164,23 +171,53 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
}
}
#endif
- else if (mode == POLL_READ) {
- if ((sectrue == usb_hid_can_read(iface)) ||
- (sectrue == usb_webusb_can_read(iface))) {
- ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
- ret->items[1] = MP_OBJ_NEW_SMALL_INT(USB_PACKET_LEN);
- return mp_const_true;
+ else if (iface <= USB_RW_IFACE_MAX) {
+ if (mode == POLL_READ) {
+ if ((sectrue == usb_hid_can_read(iface)) ||
+ (sectrue == usb_webusb_can_read(iface))) {
+ ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
+ ret->items[1] = MP_OBJ_NEW_SMALL_INT(USB_PACKET_LEN);
+ return mp_const_true;
+ }
+ } else if (mode == POLL_WRITE) {
+ if ((sectrue == usb_hid_can_write(iface)) ||
+ (sectrue == usb_webusb_can_write(iface))) {
+ ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
+ ret->items[1] = mp_const_none;
+ return mp_const_true;
+ }
}
- } else if (mode == POLL_WRITE) {
- if ((sectrue == usb_hid_can_write(iface)) ||
- (sectrue == usb_webusb_can_write(iface))) {
+ }
+#ifdef USE_BLE
+ else if (iface == BLE_IFACE) {
+ if (mode == POLL_READ) {
+ int len = ble_can_read();
+ if (len > 0) {
+ ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
+ ret->items[1] = MP_OBJ_NEW_SMALL_INT(BLE_RX_PACKET_SIZE);
+ return mp_const_true;
+ }
+ } else if (mode == POLL_WRITE) {
+ if (ble_can_write()) {
+ ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
+ ret->items[1] = mp_const_none;
+ return mp_const_true;
+ }
+ }
+ } else if (iface == BLE_EVENT_IFACE) {
+ ble_event_t event = {0};
+ bool read = ble_get_event(&event);
+ if (read) {
+ mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
+ tuple->items[0] = MP_OBJ_NEW_SMALL_INT(event.type);
+ tuple->items[1] = mp_obj_new_bytes(event.data, event.data_len);
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
- ret->items[1] = mp_const_none;
+ ret->items[1] = MP_OBJ_FROM_PTR(tuple);
return mp_const_true;
}
}
+#endif
}
-
if (mp_hal_ticks_ms() >= deadline) {
break;
} else {
diff --git a/core/embed/upymod/modtrezorio/modtrezorio.c b/core/embed/upymod/modtrezorio/modtrezorio.c
index d03168d8f3..a8ddcfbf34 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio.c
+++ b/core/embed/upymod/modtrezorio/modtrezorio.c
@@ -51,6 +51,9 @@ uint32_t last_touch_sample_time = 0;
#include "modtrezorio-webusb.h"
#include "modtrezorio-usb.h"
// clang-format on
+#ifdef USE_BLE
+#include "modtrezorio-ble.h"
+#endif
#ifdef USE_SD_CARD
#include "modtrezorio-fatfs.h"
#include "modtrezorio-sdcard.h"
@@ -60,11 +63,14 @@ uint32_t last_touch_sample_time = 0;
#endif
/// package: trezorio.__init__
-/// from . import fatfs, haptic, sdcard
+/// from . import fatfs, haptic, sdcard, ble
/// POLL_READ: int # wait until interface is readable and return read data
/// POLL_WRITE: int # wait until interface is writable
///
+/// BLE: int # interface id of the BLE events
+/// BLE_EVENT: int # interface id for BLE events
+///
/// TOUCH: int # interface id of the touch events
/// TOUCH_START: int # event id of touch start event
/// TOUCH_MOVE: int # event id of touch move event
@@ -78,7 +84,7 @@ uint32_t last_touch_sample_time = 0;
/// USB_EVENT: int # interface id for USB events
-/// WireInterface = Union[HID, WebUSB]
+/// WireInterface = Union[HID, WebUSB, BleInterface]
STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio)},
@@ -92,6 +98,11 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_haptic), MP_ROM_PTR(&mod_trezorio_haptic_module)},
#endif
+#ifdef USE_BLE
+ {MP_ROM_QSTR(MP_QSTR_ble), MP_ROM_PTR(&mod_trezorio_BLE_module)},
+ {MP_ROM_QSTR(MP_QSTR_BLE), MP_ROM_INT(BLE_IFACE)},
+ {MP_ROM_QSTR(MP_QSTR_BLE_EVENT), MP_ROM_INT(BLE_EVENT_IFACE)},
+#endif
#ifdef USE_TOUCH
{MP_ROM_QSTR(MP_QSTR_TOUCH), MP_ROM_INT(TOUCH_IFACE)},
{MP_ROM_QSTR(MP_QSTR_TOUCH_START), MP_ROM_INT((TOUCH_START >> 24) & 0xFFU)},
diff --git a/core/embed/upymod/modtrezorutils/modtrezorutils.c b/core/embed/upymod/modtrezorutils/modtrezorutils.c
index e60bd425dc..6bc7493949 100644
--- a/core/embed/upymod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -380,6 +380,8 @@ STATIC mp_obj_tuple_t mod_trezorutils_version_obj = {
/// """Git commit hash of the firmware."""
/// VERSION: VersionTuple
/// """Firmware version as a tuple (major, minor, patch, build)."""
+/// USE_BLE: bool
+/// """Whether the hardware supports BLE."""
/// USE_SD_CARD: bool
/// """Whether the hardware supports SD card."""
/// USE_BACKLIGHT: bool
@@ -443,6 +445,11 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = {
#else
{MP_ROM_QSTR(MP_QSTR_USE_SD_CARD), mp_const_false},
#endif
+#ifdef USE_BLE
+ {MP_ROM_QSTR(MP_QSTR_USE_BLE), mp_const_true},
+#else
+ {MP_ROM_QSTR(MP_QSTR_USE_BLE), mp_const_false},
+#endif
#ifdef USE_BACKLIGHT
{MP_ROM_QSTR(MP_QSTR_USE_BACKLIGHT), mp_const_true},
#else
diff --git a/core/mocks/generated/trezorio/__init__.pyi b/core/mocks/generated/trezorio/__init__.pyi
index 764e5f440a..61de734dad 100644
--- a/core/mocks/generated/trezorio/__init__.pyi
+++ b/core/mocks/generated/trezorio/__init__.pyi
@@ -158,10 +158,13 @@ class WebUSB:
"""
Reads message using USB WebUSB (device) or UDP (emulator).
"""
-from . import fatfs, haptic, sdcard
+from . import fatfs, haptic, sdcard, ble
POLL_READ: int # wait until interface is readable and return read data
POLL_WRITE: int # wait until interface is writable
+BLE: int # interface id of the BLE events
+BLE_EVENT: int # interface id for BLE events
+
TOUCH: int # interface id of the touch events
TOUCH_START: int # event id of touch start event
TOUCH_MOVE: int # event id of touch move event
@@ -172,4 +175,4 @@ BUTTON_RELEASED: int # button up event
BUTTON_LEFT: int # button number of left button
BUTTON_RIGHT: int # button number of right button
USB_EVENT: int # interface id for USB events
-WireInterface = Union[HID, WebUSB]
+WireInterface = Union[HID, WebUSB, BleInterface]
diff --git a/core/mocks/generated/trezorio/ble.pyi b/core/mocks/generated/trezorio/ble.pyi
new file mode 100644
index 0000000000..5a5288b685
--- /dev/null
+++ b/core/mocks/generated/trezorio/ble.pyi
@@ -0,0 +1,50 @@
+from typing import *
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def write(msg: bytes) -> int:
+ """
+ Sends message over BLE
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def read(buf: bytes, offset: int = 0) -> int
+ """
+ Reads message using BLE (device).
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def erase_bonds() -> None:
+ """
+ Erases all BLE bonds
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def start_comm() -> None:
+ """
+ Start communication with BLE chip
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def start_advertising(whitelist: bool) -> None:
+ """
+ Start advertising
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def stop_advertising(whitelist: bool) -> None:
+ """
+ Stop advertising
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ble.h
+def disconnect() -> None:
+ """
+ Disconnect BLE
+ """
diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi
index c8b55c2b9b..53b9748643 100644
--- a/core/mocks/generated/trezorutils.pyi
+++ b/core/mocks/generated/trezorutils.pyi
@@ -122,6 +122,8 @@ SCM_REVISION: bytes
"""Git commit hash of the firmware."""
VERSION: VersionTuple
"""Firmware version as a tuple (major, minor, patch, build)."""
+USE_BLE: bool
+"""Whether the hardware supports BLE."""
USE_SD_CARD: bool
"""Whether the hardware supports SD card."""
USE_BACKLIGHT: bool
diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py
index 0162d4b8d5..d37800c4a0 100644
--- a/core/src/trezor/utils.py
+++ b/core/src/trezor/utils.py
@@ -11,6 +11,7 @@ from trezorutils import ( # noqa: F401
SCM_REVISION,
UI_LAYOUT,
USE_BACKLIGHT,
+ USE_BLE,
USE_BUTTON,
USE_HAPTIC,
USE_OPTIGA,