diff --git a/core/embed/io/ble/inc/io/ble.h b/core/embed/io/ble/inc/io/ble.h index 26d9006a043..d7247061aa6 100644 --- a/core/embed/io/ble/inc/io/ble.h +++ b/core/embed/io/ble/inc/io/ble.h @@ -29,6 +29,8 @@ #define BLE_RX_PACKET_SIZE 244 #define BLE_TX_PACKET_SIZE 64 +#define BLE_ADV_NAME_LEN 20 + typedef enum { BLE_SWITCH_OFF = 0, // Turn off BLE advertising, disconnect BLE_SWITCH_ON = 1, // Turn on BLE advertising @@ -37,6 +39,18 @@ typedef enum { BLE_ERASE_BONDS = 4, // Erase all bonding information BLE_ALLOW_PAIRING = 5, // Accept pairing request BLE_REJECT_PAIRING = 6, // Reject pairing request +} ble_command_type_t; + +typedef union { + uint8_t raw[32]; + uint8_t name[BLE_ADV_NAME_LEN]; + +} ble_command_data_t; + +typedef struct { + ble_command_type_t cmd_type; + uint8_t data_len; + ble_command_data_t data; } ble_command_t; typedef enum { @@ -91,7 +105,7 @@ void ble_stop(void); // Sends a specific command to the BLE module for execution. // // Returns `true` if the command was successfully issued. -bool ble_issue_command(ble_command_t command); +bool ble_issue_command(ble_command_t *command); // Reads an event from the BLE module // diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c index 68c2f57c03a..81d0a709162 100644 --- a/core/embed/io/ble/stm32/ble.c +++ b/core/embed/io/ble/stm32/ble.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "ble_comm_defs.h" @@ -65,6 +66,7 @@ typedef struct { tsqueue_entry_t ts_queue_entries[TX_QUEUE_LEN]; tsqueue_t tx_queue; + char adv_name[BLE_ADV_NAME_LEN]; systimer_t *timer; uint16_t ping_cntr; } ble_driver_t; @@ -80,11 +82,19 @@ static bool ble_send_state_request(ble_driver_t *drv) { static bool ble_send_advertising_on(ble_driver_t *drv, bool whitelist) { (void)drv; - uint8_t data[2]; - data[0] = INTERNAL_CMD_ADVERTISING_ON; - data[1] = whitelist ? 1 : 0; - return nrf_send_msg(NRF_SERVICE_BLE_MANAGER, data, sizeof(data), NULL, - NULL) >= 0; + + unit_properties_t props; + unit_properties_get(&props); + + cmd_advertising_on_t data = { + .cmd_id = INTERNAL_CMD_ADVERTISING_ON, + .whitelist = whitelist ? 1 : 0, + .color = props.color, + }; + memcpy(data.name, drv->adv_name, BLE_ADV_NAME_LEN); + + return nrf_send_msg(NRF_SERVICE_BLE_MANAGER, (uint8_t *)&data, sizeof(data), + NULL, NULL) >= 0; } static bool ble_send_advertising_off(ble_driver_t *drv) { @@ -146,14 +156,6 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) { event_status_msg_t msg; memcpy(&msg, data, sizeof(event_status_msg_t)); - if (!drv->status_valid) { - if (msg.peer_count > 0) { - drv->mode_requested = BLE_MODE_CONNECTABLE; - } else { - drv->mode_requested = BLE_MODE_OFF; - } - } - if (drv->connected != msg.connected) { if (msg.connected) { // new connection @@ -507,7 +509,7 @@ uint32_t ble_read(uint8_t *data, uint16_t max_len) { return read_len; } -bool ble_issue_command(ble_command_t command) { +bool ble_issue_command(ble_command_t *command) { ble_driver_t *drv = &g_ble_driver; if (!drv->initialized) { @@ -518,14 +520,16 @@ bool ble_issue_command(ble_command_t command) { bool result = false; - switch (command) { + switch (command->cmd_type) { case BLE_SWITCH_OFF: drv->mode_requested = BLE_MODE_OFF; break; case BLE_SWITCH_ON: + memcpy(drv->adv_name, command->data.name, sizeof(drv->adv_name)); drv->mode_requested = BLE_MODE_CONNECTABLE; break; case BLE_PAIRING_MODE: + memcpy(drv->adv_name, command->data.name, sizeof(drv->adv_name)); drv->mode_requested = BLE_MODE_PAIRING; break; case BLE_DISCONNECT: diff --git a/core/embed/io/ble/stm32/ble_comm_defs.h b/core/embed/io/ble/stm32/ble_comm_defs.h index b32c7b4497b..126d567ecea 100644 --- a/core/embed/io/ble/stm32/ble_comm_defs.h +++ b/core/embed/io/ble/stm32/ble_comm_defs.h @@ -17,11 +17,12 @@ * along with this program. If not, see . */ -#ifndef TREZORHAL_BLE_COMM_DEFS_H_ -#define TREZORHAL_BLE_COMM_DEFS_H_ +#pragma once #include +#include + typedef struct { uint8_t msg_id; uint8_t connected; @@ -56,4 +57,10 @@ typedef enum { INTERNAL_CMD_ALLOW_PAIRING = 0x06, INTERNAL_CMD_REJECT_PAIRING = 0x07, } internal_cmd_t; -#endif + +typedef struct { + uint8_t cmd_id; + uint8_t whitelist; + uint8_t color; + uint8_t name[BLE_ADV_NAME_LEN]; +} cmd_advertising_on_t; diff --git a/core/embed/rust/src/trezorhal/ble.rs b/core/embed/rust/src/trezorhal/ble.rs index 3bef99eb252..c566d32ff51 100644 --- a/core/embed/rust/src/trezorhal/ble.rs +++ b/core/embed/rust/src/trezorhal/ble.rs @@ -15,20 +15,43 @@ pub fn connected() -> bool { } } -pub fn pairing_mode() { +pub fn pairing_mode(name: &str) { unsafe { - ffi::ble_issue_command(ffi::ble_command_t_BLE_PAIRING_MODE); + let mut cmd = ffi::ble_command_t { + cmd_type: ffi::ble_command_type_t_BLE_PAIRING_MODE, + data_len: 0, + data: ffi::ble_command_data_t { raw: [0; 32] }, + }; + + let bytes = name.as_bytes(); + + // Determine how many bytes we can copy (min of buffer size and string length). + let len = bytes.len().min(cmd.data.name.len()); + + cmd.data.name[..len].copy_from_slice(&bytes[..len]); + + ffi::ble_issue_command(&mut cmd as _); } } pub fn allow_pairing() { unsafe { - ffi::ble_issue_command(ffi::ble_command_t_BLE_ALLOW_PAIRING); + let mut cmd = ffi::ble_command_t { + cmd_type: ffi::ble_command_type_t_BLE_ALLOW_PAIRING, + data_len: 0, + data: ffi::ble_command_data_t { raw: [0; 32] }, + }; + ffi::ble_issue_command(&mut cmd as _); } } pub fn reject_pairing() { unsafe { - ffi::ble_issue_command(ffi::ble_command_t_BLE_REJECT_PAIRING); + let mut cmd = ffi::ble_command_t { + cmd_type: ffi::ble_command_type_t_BLE_REJECT_PAIRING, + data_len: 0, + data: ffi::ble_command_data_t { raw: [0; 32] }, + }; + ffi::ble_issue_command(&mut cmd as _); } } diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c index 085bacb72fd..58b9f62806e 100644 --- a/core/embed/sys/syscall/stm32/syscall_dispatch.c +++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c @@ -687,8 +687,8 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args, } break; case SYSCALL_BLE_ISSUE_COMMAND: { - ble_command_t command = args[0]; - ble_issue_command(command); + ble_command_t *command = (ble_command_t *)args[0]; + args[0] = ble_issue_command__verified(command); } break; case SYSCALL_BLE_GET_STATE: { diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c index 2ffa6abf727..4bfe2f11615 100644 --- a/core/embed/sys/syscall/stm32/syscall_stubs.c +++ b/core/embed/sys/syscall/stm32/syscall_stubs.c @@ -655,7 +655,7 @@ secbool firmware_calc_hash(const uint8_t *challenge, size_t challenge_len, void ble_start(void) { syscall_invoke0(SYSCALL_BLE_START); } -bool ble_issue_command(ble_command_t command) { +bool ble_issue_command(ble_command_t *command) { return (bool)syscall_invoke1((uint32_t)command, SYSCALL_BLE_ISSUE_COMMAND); } diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c index fd82ca4b128..925acde30bc 100644 --- a/core/embed/sys/syscall/stm32/syscall_verifiers.c +++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c @@ -718,6 +718,18 @@ secbool firmware_get_vendor__verified(char *buff, size_t buff_size) { // --------------------------------------------------------------------- #ifdef USE_BLE +bool ble_issue_command__verified(ble_command_t *command) { + if (!probe_read_access(command, sizeof(*command))) { + goto access_violation; + } + + return ble_issue_command(command); + +access_violation: + apptask_access_violation(); + return false; +} + void ble_get_state__verified(ble_state_t *state) { if (!probe_write_access(state, sizeof(*state))) { goto access_violation; diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h index bd63f6200ba..b1b2d9287ab 100644 --- a/core/embed/sys/syscall/stm32/syscall_verifiers.h +++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h @@ -191,6 +191,8 @@ secbool firmware_get_vendor__verified(char *buff, size_t buff_size); #include +bool ble_issue_command__verified(ble_command_t *state); + void ble_get_state__verified(ble_state_t *state); bool ble_get_event__verified(ble_event_t *event); diff --git a/core/embed/upymod/modtrezorio/modtrezorio-ble.h b/core/embed/upymod/modtrezorio/modtrezorio-ble.h index b348f9973fe..80a82992f17 100644 --- a/core/embed/upymod/modtrezorio/modtrezorio-ble.h +++ b/core/embed/upymod/modtrezorio/modtrezorio-ble.h @@ -124,18 +124,18 @@ STATIC mp_obj_t mod_trezorio_BLE_read(size_t n_args, const mp_obj_t *args) { STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorio_BLE_read_obj, 1, 2, mod_trezorio_BLE_read); -/// def erase_bonds() -> None: +/// def erase_bonds() -> bool: /// """ /// Erases all BLE bonds /// """ STATIC mp_obj_t mod_trezorio_BLE_erase_bonds(void) { - ble_issue_command(BLE_ERASE_BONDS); - return mp_const_none; + ble_command_t cmd = {.cmd_type = BLE_ERASE_BONDS, .data_len = 0}; + return mp_obj_new_bool(ble_issue_command(&cmd)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_erase_bonds_obj, mod_trezorio_BLE_erase_bonds); -/// def start_comm() -> None: +/// def start_comm() -> bool: /// """ /// Start communication with BLE chip /// """ @@ -146,41 +146,76 @@ STATIC mp_obj_t mod_trezorio_BLE_start_comm(void) { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_start_comm_obj, mod_trezorio_BLE_start_comm); -/// def start_advertising(whitelist: bool) -> None: +/// def start_advertising(whitelist: bool, name: str | None) -> bool: /// """ /// Start advertising /// """ -STATIC mp_obj_t mod_trezorio_BLE_start_advertising(mp_obj_t whitelist) { - bool whitelist_bool = mp_obj_is_true(whitelist); +STATIC mp_obj_t mod_trezorio_BLE_start_advertising(size_t n_args, + const mp_obj_t *args) { + bool whitelist_bool = mp_obj_is_true(args[0]); - ble_issue_command(whitelist_bool ? BLE_SWITCH_ON : BLE_PAIRING_MODE); - return mp_const_none; + mp_buffer_info_t name = {0}; + + char *name_buf = NULL; + int name_len = 0; + + if (n_args == 1 || !mp_get_buffer(args[1], &name, MP_BUFFER_READ)) { + name_buf = MODEL_FULL_NAME; + name_len = strlen(MODEL_FULL_NAME); + } else { + name_buf = name.buf; + name_len = name.len; + } + + ble_command_t cmd = { + .cmd_type = whitelist_bool ? BLE_SWITCH_ON : BLE_PAIRING_MODE, + .data_len = name.len}; + + // get a minimum of the two lengths + int len = name_len < BLE_ADV_NAME_LEN ? name_len : BLE_ADV_NAME_LEN; + + memcpy(cmd.data.name, name_buf, len); + + return mp_obj_new_bool(ble_issue_command(&cmd)); } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_start_advertising_obj, - mod_trezorio_BLE_start_advertising); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN( + mod_trezorio_BLE_start_advertising_obj, 1, 2, + mod_trezorio_BLE_start_advertising); -/// def stop_advertising(whitelist: bool) -> None: +/// def stop_advertising(whitelist: bool) -> bool: /// """ /// Stop advertising /// """ STATIC mp_obj_t mod_trezorio_BLE_stop_advertising(void) { - ble_issue_command(BLE_SWITCH_OFF); - return mp_const_none; + ble_command_t cmd = {.cmd_type = BLE_SWITCH_OFF, .data_len = 0}; + return mp_obj_new_bool(ble_issue_command(&cmd)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_stop_advertising_obj, mod_trezorio_BLE_stop_advertising); -/// def disconnect() -> None: +/// def disconnect() -> bool: /// """ /// Disconnect BLE /// """ STATIC mp_obj_t mod_trezorio_BLE_disconnect(void) { - ble_issue_command(BLE_DISCONNECT); - return mp_const_none; + ble_command_t cmd = {.cmd_type = BLE_DISCONNECT, .data_len = 0}; + return mp_obj_new_bool(ble_issue_command(&cmd)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_disconnect_obj, mod_trezorio_BLE_disconnect); +/// def peer_count() -> int: +/// """ +/// Get peer count (number of bonded devices) +/// """ +STATIC mp_obj_t mod_trezorio_BLE_peer_count(void) { + ble_state_t state; + ble_get_state(&state); + return MP_OBJ_NEW_SMALL_INT(state.peer_count); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_peer_count_obj, + mod_trezorio_BLE_peer_count); + 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), @@ -199,6 +234,8 @@ STATIC const mp_rom_map_elem_t mod_trezorio_BLE_globals_table[] = { MP_ROM_PTR(&mod_trezorio_BLE_stop_advertising_obj)}, {MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&mod_trezorio_BLE_disconnect_obj)}, + {MP_ROM_QSTR(MP_QSTR_peer_count), + MP_ROM_PTR(&mod_trezorio_BLE_peer_count_obj)}, }; STATIC MP_DEFINE_CONST_DICT(mod_trezorio_BLE_globals, mod_trezorio_BLE_globals_table); diff --git a/core/mocks/generated/trezorio/ble.pyi b/core/mocks/generated/trezorio/ble.pyi index 5a5288b6850..0bdea87c103 100644 --- a/core/mocks/generated/trezorio/ble.pyi +++ b/core/mocks/generated/trezorio/ble.pyi @@ -16,35 +16,42 @@ def read(buf: bytes, offset: int = 0) -> int # upymod/modtrezorio/modtrezorio-ble.h -def erase_bonds() -> None: +def erase_bonds() -> bool: """ Erases all BLE bonds """ # upymod/modtrezorio/modtrezorio-ble.h -def start_comm() -> None: +def start_comm() -> bool: """ Start communication with BLE chip """ # upymod/modtrezorio/modtrezorio-ble.h -def start_advertising(whitelist: bool) -> None: +def start_advertising(whitelist: bool, name: str | None) -> bool: """ Start advertising """ # upymod/modtrezorio/modtrezorio-ble.h -def stop_advertising(whitelist: bool) -> None: +def stop_advertising(whitelist: bool) -> bool: """ Stop advertising """ # upymod/modtrezorio/modtrezorio-ble.h -def disconnect() -> None: +def disconnect() -> bool: """ Disconnect BLE """ + + +# upymod/modtrezorio/modtrezorio-ble.h +def peer_count() -> int: + """ + Get peer count (number of bonded devices) + """