From 6dd6f52509914af3beba4693299be31399e91b05 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Tue, 1 Oct 2024 10:27:36 +0200 Subject: [PATCH] feat(core): expose BLE functionality to micropython [no changelog] --- .../upymod/modtrezorio/modtrezorio-ble.h | 208 ++++++++++++++++++ .../upymod/modtrezorio/modtrezorio-poll.h | 59 ++++- core/embed/upymod/modtrezorio/modtrezorio.c | 15 +- .../upymod/modtrezorutils/modtrezorutils.c | 7 + core/mocks/generated/trezorio/__init__.pyi | 7 +- core/mocks/generated/trezorio/ble.pyi | 50 +++++ core/mocks/generated/trezorutils.pyi | 2 + core/src/trezor/utils.py | 1 + 8 files changed, 334 insertions(+), 15 deletions(-) create mode 100644 core/embed/upymod/modtrezorio/modtrezorio-ble.h create mode 100644 core/mocks/generated/trezorio/ble.pyi 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,