1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-20 09:09:02 +00:00

refactor(core): convert modtrezorio.ble to rust

[no changelog]
This commit is contained in:
Martin Milata 2025-04-11 02:09:22 +02:00
parent b35d2f4000
commit 2a40d91790
14 changed files with 599 additions and 517 deletions

View File

@ -402,8 +402,15 @@ fn generate_trezorhal_bindings() {
.allowlist_type("usb_event_t")
.allowlist_function("usb_get_state")
// ble
.allowlist_var("BLE_PAIRING_CODE_LEN")
.allowlist_var("BLE_RX_PACKET_SIZE")
.allowlist_var("BLE_TX_PACKET_SIZE")
.allowlist_var("BLE_ADV_NAME_LEN")
.allowlist_function("ble_get_state")
.allowlist_function("ble_issue_command")
.allowlist_function("ble_start")
.allowlist_function("ble_write")
.allowlist_function("ble_read")
.allowlist_type("ble_command_t")
.allowlist_type("ble_state_t")
// touch

View File

@ -10,6 +10,7 @@ mp_obj_t protobuf_debug_msg_def_type();
extern mp_obj_module_t mp_module_trezorproto;
extern mp_obj_module_t mp_module_trezorui_api;
extern mp_obj_module_t mp_module_trezortranslate;
extern mp_obj_module_t mp_module_trezorble;
#if !PYOPT
mp_obj_t ui_debug_layout_type();

View File

@ -19,6 +19,7 @@ static void _librust_qstrs(void) {
MP_QSTR_ATTACHED;
MP_QSTR_AttachType;
MP_QSTR_BacklightLevels;
MP_QSTR_BleInterface;
MP_QSTR_CANCELLED;
MP_QSTR_CONFIRMED;
MP_QSTR_DIM;
@ -36,12 +37,14 @@ static void _librust_qstrs(void) {
MP_QSTR_NONE;
MP_QSTR_NORMAL;
MP_QSTR_RESUME;
MP_QSTR_RX_PACKET_LEN;
MP_QSTR_SWIPE_DOWN;
MP_QSTR_SWIPE_LEFT;
MP_QSTR_SWIPE_RIGHT;
MP_QSTR_SWIPE_UP;
MP_QSTR_TR;
MP_QSTR_TRANSITIONING;
MP_QSTR_TX_PACKET_LEN;
MP_QSTR_TranslationsHeader;
MP_QSTR___del__;
MP_QSTR___dict__;
@ -78,6 +81,7 @@ static void _librust_qstrs(void) {
MP_QSTR_address_label;
MP_QSTR_address_qr;
MP_QSTR_allow_cancel;
MP_QSTR_allow_pairing;
MP_QSTR_amount;
MP_QSTR_amount_change;
MP_QSTR_amount_label;
@ -222,6 +226,7 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_value;
MP_QSTR_confirm_value_intro;
MP_QSTR_confirm_with_info;
MP_QSTR_connection_flags;
MP_QSTR_continue_recovery_homepage;
MP_QSTR_count;
MP_QSTR_current;
@ -237,11 +242,13 @@ static void _librust_qstrs(void) {
MP_QSTR_device_name__change_template;
MP_QSTR_device_name__title;
MP_QSTR_disable_animation;
MP_QSTR_disconnect;
MP_QSTR_encode;
MP_QSTR_encoded_length;
MP_QSTR_entropy__send;
MP_QSTR_entropy__title_confirm;
MP_QSTR_erase;
MP_QSTR_erase_bonds;
MP_QSTR_experimental_mode__enable;
MP_QSTR_experimental_mode__only_for_dev;
MP_QSTR_experimental_mode__title;
@ -282,6 +289,7 @@ static void _librust_qstrs(void) {
MP_QSTR_homescreen__title_set;
MP_QSTR_horizontal;
MP_QSTR_icon_name;
MP_QSTR_iface_num;
MP_QSTR_image;
MP_QSTR_indeterminate;
MP_QSTR_info;
@ -312,6 +320,8 @@ static void _librust_qstrs(void) {
MP_QSTR_instructions__tap_to_continue;
MP_QSTR_instructions__tap_to_start;
MP_QSTR_instructions__view_all_data;
MP_QSTR_interface;
MP_QSTR_is_connected;
MP_QSTR_is_data;
MP_QSTR_is_type_of;
MP_QSTR_items;
@ -378,6 +388,7 @@ static void _librust_qstrs(void) {
MP_QSTR_passphrase__turn_on;
MP_QSTR_passphrase__wallet;
MP_QSTR_path;
MP_QSTR_peer_count;
MP_QSTR_pin__cancel_description;
MP_QSTR_pin__cancel_info;
MP_QSTR_pin__cancel_setup;
@ -432,6 +443,7 @@ static void _librust_qstrs(void) {
MP_QSTR_prompt_screen;
MP_QSTR_prompt_title;
MP_QSTR_qr_title;
MP_QSTR_read;
MP_QSTR_reboot_to_bootloader__just_a_moment;
MP_QSTR_reboot_to_bootloader__restart;
MP_QSTR_reboot_to_bootloader__title;
@ -487,6 +499,7 @@ static void _librust_qstrs(void) {
MP_QSTR_recovery__x_of_y_entered_template;
MP_QSTR_recovery__you_have_entered;
MP_QSTR_recovery_type;
MP_QSTR_reject_pairing;
MP_QSTR_remaining_shares;
MP_QSTR_request_bip39;
MP_QSTR_request_complete_repaint;
@ -674,6 +687,9 @@ static void _librust_qstrs(void) {
MP_QSTR_sign_message__message_size;
MP_QSTR_sign_message__verify_address;
MP_QSTR_skip_first_paint;
MP_QSTR_start_advertising;
MP_QSTR_start_comm;
MP_QSTR_stop_advertising;
MP_QSTR_storage_msg__processing;
MP_QSTR_storage_msg__starting;
MP_QSTR_storage_msg__verifying_pin;
@ -697,6 +713,7 @@ static void _librust_qstrs(void) {
MP_QSTR_total_len;
MP_QSTR_touch_event;
MP_QSTR_trace;
MP_QSTR_trezorble;
MP_QSTR_trezorproto;
MP_QSTR_trezorui_api;
MP_QSTR_tutorial;
@ -729,6 +746,7 @@ static void _librust_qstrs(void) {
MP_QSTR_tutorial__welcome_safe5;
MP_QSTR_type_for_name;
MP_QSTR_type_for_wire;
MP_QSTR_unpair;
MP_QSTR_usb_event;
MP_QSTR_user_fee_change;
MP_QSTR_value;

View File

@ -1,64 +0,0 @@
use super::ffi;
pub fn connected() -> bool {
unsafe {
let mut state = ffi::ble_state_t {
connected: false,
peer_count: 0,
connectable: false,
pairing: false,
pairing_requested: false,
state_known: false,
};
ffi::ble_get_state(&mut state as _);
state.connected
}
}
pub fn pairing_mode(name: &str) {
unsafe {
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.adv_start.name.len());
cmd.data.adv_start.name[..len].copy_from_slice(&bytes[..len]);
ffi::ble_issue_command(&mut cmd as _);
}
}
pub fn allow_pairing(mut code: u32) {
const CODE_LEN: u8 = 6;
let mut cmd = ffi::ble_command_t {
cmd_type: ffi::ble_command_type_t_BLE_ALLOW_PAIRING,
data_len: CODE_LEN,
data: ffi::ble_command_data_t { raw: [0; 32] },
};
unsafe {
for i in (0..CODE_LEN).rev() {
let digit = b'0' + ((code % 10) as u8);
code /= 10;
cmd.data.raw[i as usize] = digit;
}
ffi::ble_issue_command(&mut cmd as _);
}
}
pub fn reject_pairing() {
unsafe {
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 _);
}
}

View File

@ -0,0 +1,287 @@
use super::*;
use crate::{
error::Error,
micropython::{
buffer::{get_buffer, get_buffer_mut, StrBuffer},
list::List,
macros::*,
map::Map,
module::Module,
obj::Obj,
qstr::Qstr,
simple_type::SimpleTypeObj,
typ::Type,
util,
},
strutil::ShortString,
};
extern "C" fn py_erase_bonds() -> Obj {
let block = || {
erase_bonds()?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_unpair() -> Obj {
let block = || {
unpair()?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_start_comm() -> Obj {
start_comm();
Obj::const_none()
}
extern "C" fn py_start_advertising(whitelist: Obj, name: Obj) -> Obj {
let block = || {
let whitelist: bool = whitelist.try_into()?;
let name = name.try_into_option::<StrBuffer>()?;
let name = name.as_deref().unwrap_or(model::FULL_NAME);
if whitelist {
connectable_mode(name)?;
} else {
pairing_mode(name)?;
};
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_stop_advertising() -> Obj {
let block = || {
stop_advertising()?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_disconnect() -> Obj {
let block = || {
disconnect()?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_allow_pairing(code: Obj) -> Obj {
let block = || {
let code: u32 = code.try_into()?;
allow_pairing(code)?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_reject_pairing() -> Obj {
let block = || {
reject_pairing()?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_peer_count() -> Obj {
peer_count().into()
}
extern "C" fn py_is_connected() -> Obj {
is_connected().into()
}
extern "C" fn py_connection_flags() -> Obj {
let block = || {
let mut result = List::with_capacity(4)?;
let s = state();
if !s.state_known {
result.append(c"unknown".try_into()?)?;
return Ok(result.leak().into());
}
if s.connectable {
result.append(c"connectable".try_into()?)?;
}
if s.connected {
result.append(c"connected".try_into()?)?;
}
if s.pairing {
result.append(c"pairing".try_into()?)?;
}
if s.pairing_requested {
result.append(c"pairing_requested".try_into()?)?;
}
Ok(result.leak().into())
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_iface_num(_self: Obj) -> Obj {
Obj::small_int(8) // FIXME SYSHANDLE_BLE_IFACE_0
}
extern "C" fn py_iface_write(_self: Obj, msg: Obj) -> Obj {
let block = || {
// SAFETY: reference is discarded at the end of the block
let buf = unsafe { get_buffer(msg)? };
if write(buf).is_ok() {
Ok(buf.len().try_into()?)
} else {
Ok((-1).try_into()?)
}
};
unsafe { util::try_or_raise(block) }
}
extern "C" fn py_iface_read(n_args: usize, args: *const Obj) -> Obj {
let block = |args: &[Obj], _kwargs: &Map| {
// SAFETY: reference is discarded at the end of the block
let mut buf = unsafe { get_buffer_mut(args[1]) }?;
if args.len() > 2 {
let offset: usize = args[2].try_into()?;
buf = buf
.get_mut(offset..)
.ok_or(Error::ValueError(c"Offset out of bounds"))?;
}
if buf.len() < RX_PACKET_SIZE {
return Err(Error::ValueError(c"Buffer too small"));
}
let read_len = read(buf, RX_PACKET_SIZE)?;
if read_len != RX_PACKET_SIZE {
return Err(Error::ValueError(c"Unexpected read length"));
}
read_len.try_into()
};
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
}
static BLE_INTERFACE_TYPE: Type = obj_type! {
name: Qstr::MP_QSTR_BleInterface,
locals: &obj_dict!(obj_map! {
Qstr::MP_QSTR_iface_num => obj_fn_1!(py_iface_num).as_obj(),
Qstr::MP_QSTR_write => obj_fn_2!(py_iface_write).as_obj(),
Qstr::MP_QSTR_read => obj_fn_var!(2, 3, py_iface_read).as_obj(),
Qstr::MP_QSTR_RX_PACKET_LEN => Obj::small_int(RX_PACKET_SIZE as u16),
Qstr::MP_QSTR_TX_PACKET_LEN => Obj::small_int(TX_PACKET_SIZE as u16),
}),
};
static BLE_INTERFACE_OBJ: SimpleTypeObj = SimpleTypeObj::new(&BLE_INTERFACE_TYPE);
#[no_mangle]
#[rustfmt::skip]
pub static mp_module_trezorble: Module = obj_module! {
Qstr::MP_QSTR___name__ => Qstr::MP_QSTR_trezorble.to_obj(),
/// class BleInterface:
/// """
/// BLE interface wrapper.
/// """
///
/// RX_PACKET_LEN: int
/// """Length of one BLE RX packet."""
///
/// TX_PACKET_LEN: int
/// """Length of one BLE TX packet."""
///
/// def iface_num(self) -> int:
/// """
/// Returns the configured number of this interface.
/// """
///
/// def write(self, msg: bytes) -> int:
/// """
/// Sends message over BLE
/// """
///
/// def read(self, buf: bytearray, offset: int = 0) -> int:
/// """
/// Reads message using BLE (device).
/// """
///
/// mock:global
///
/// interface: BleInterface
/// """BLE interface instance."""
Qstr::MP_QSTR_interface => BLE_INTERFACE_OBJ.as_obj(),
/// mock:global
/// def erase_bonds():
/// """
/// Erases all BLE bonds.
/// Raises exception if BLE reports an error.
/// """
Qstr::MP_QSTR_erase_bonds => obj_fn_0!(py_erase_bonds).as_obj(),
/// def unpair():
/// """
/// Erases bond for current connection, if any.
/// Raises exception if BLE driver reports an error.
/// """
Qstr::MP_QSTR_unpair => obj_fn_0!(py_unpair).as_obj(),
/// def start_comm():
/// """
/// Start communication with BLE chip.
/// """
Qstr::MP_QSTR_start_comm => obj_fn_0!(py_start_comm).as_obj(),
/// def start_advertising(whitelist: bool, name: str | None):
/// """
/// Start advertising.
/// Raises exception if BLE driver reports an error.
/// """
Qstr::MP_QSTR_start_advertising => obj_fn_2!(py_start_advertising).as_obj(),
/// def stop_advertising():
/// """
/// Stop advertising.
/// Raises exception if BLE driver reports an error.
/// """
Qstr::MP_QSTR_stop_advertising => obj_fn_0!(py_stop_advertising).as_obj(),
/// def disconnect():
/// """
/// Disconnect BLE.
/// Raises exception if BLE driver reports an error.
/// """
Qstr::MP_QSTR_disconnect => obj_fn_0!(py_disconnect).as_obj(),
/// def peer_count() -> int:
/// """
/// Get peer count (number of bonded devices).
/// """
Qstr::MP_QSTR_peer_count => obj_fn_0!(py_peer_count).as_obj(),
/// def is_connected() -> bool:
/// """
/// True if a host is connected to us. May or may not be paired.
/// """
Qstr::MP_QSTR_is_connected => obj_fn_0!(py_is_connected).as_obj(),
/// def connection_flags() -> list[str]:
/// """
/// Returns current connection state as a list of string flags.
/// """
Qstr::MP_QSTR_connection_flags => obj_fn_0!(py_connection_flags).as_obj(),
/// def allow_pairing(code: int):
/// """
/// Accept BLE pairing request. Code must match the one received with
/// BLE_PAIRING_REQUEST event.
/// Raises exception if BLE driver reports an error.
/// """
Qstr::MP_QSTR_allow_pairing => obj_fn_1!(py_allow_pairing).as_obj(),
/// def reject_pairing():
/// """
/// Reject BLE pairing request.
/// Raises exception if BLE driver reports an error.
/// """
Qstr::MP_QSTR_reject_pairing => obj_fn_0!(py_reject_pairing).as_obj(),
};

View File

@ -0,0 +1,164 @@
#[cfg(feature = "micropython")]
mod micropython;
use crate::error::Error;
use core::mem::size_of;
use super::{ffi, model};
pub const ADV_NAME_LEN: usize = ffi::BLE_ADV_NAME_LEN as usize;
pub const PAIRING_CODE_LEN: usize = ffi::BLE_PAIRING_CODE_LEN as usize;
pub const RX_PACKET_SIZE: usize = ffi::BLE_RX_PACKET_SIZE as usize;
pub const TX_PACKET_SIZE: usize = ffi::BLE_TX_PACKET_SIZE as usize;
const COMMAND_FAILED: Error = Error::RuntimeError(c"BLE command failed");
const WRITE_FAILED: Error = Error::RuntimeError(c"BLE write failed");
// NOTE: replace with floor_char_boundary when stable
fn prefix_utf8_bytes(text: &str, max_len: usize) -> &[u8] {
let mut i = text.len().min(max_len);
while !text.is_char_boundary(i) {
i -= 1;
}
&text.as_bytes()[..i]
}
fn state() -> ffi::ble_state_t {
let mut state = ffi::ble_state_t {
connected: false,
peer_count: 0,
connectable: false,
pairing: false,
pairing_requested: false,
state_known: false,
};
unsafe { ffi::ble_get_state(&mut state as _) };
state
}
fn issue_command(
cmd_type: ffi::ble_command_type_t,
cmd_data: ffi::ble_command_data_t,
) -> Result<(), Error> {
let data_len = match cmd_type {
ffi::ble_command_type_t_BLE_ALLOW_PAIRING => PAIRING_CODE_LEN,
ffi::ble_command_type_t_BLE_PAIRING_MODE | ffi::ble_command_type_t_BLE_SWITCH_ON => {
size_of::<ffi::ble_adv_start_cmd_data_t>()
}
_ => 0,
};
let mut cmd = ffi::ble_command_t {
cmd_type,
data_len: unwrap!(data_len.try_into()),
data: cmd_data,
};
if unsafe { ffi::ble_issue_command(&mut cmd as _) } {
Ok(())
} else {
Err(COMMAND_FAILED)
}
}
fn data_advname(name: &str) -> ffi::ble_command_data_t {
let mut data = ffi::ble_command_data_t {
adv_start: ffi::ble_adv_start_cmd_data_t {
name: [0u8; ADV_NAME_LEN],
static_mac: false,
},
};
let bytes = prefix_utf8_bytes(name, ADV_NAME_LEN);
unsafe {
data.adv_start.name[..bytes.len()].copy_from_slice(bytes);
}
data
}
fn data_code(mut code: u32) -> ffi::ble_command_data_t {
let mut pairing_code: [u8; PAIRING_CODE_LEN] = [0; PAIRING_CODE_LEN];
for i in (0..PAIRING_CODE_LEN).rev() {
let digit = b'0' + ((code % 10) as u8);
code /= 10;
pairing_code[i] = digit;
}
ffi::ble_command_data_t { pairing_code }
}
const fn data_none() -> ffi::ble_command_data_t {
ffi::ble_command_data_t { raw: [0; 32] }
}
pub fn pairing_mode(name: &str) -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_PAIRING_MODE, data_advname(name))
}
pub fn connectable_mode(name: &str) -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_SWITCH_ON, data_advname(name))
}
pub fn stop_advertising() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_SWITCH_OFF, data_none())
}
pub fn allow_pairing(code: u32) -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_ALLOW_PAIRING, data_code(code))
}
pub fn reject_pairing() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_REJECT_PAIRING, data_none())
}
pub fn erase_bonds() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_ERASE_BONDS, data_none())
}
pub fn unpair() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_UNPAIR, data_none())
}
pub fn disconnect() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_DISCONNECT, data_none())
}
pub fn start_comm() {
unsafe { ffi::ble_start() }
}
pub fn peer_count() -> u8 {
state().peer_count
}
pub fn is_started() -> bool {
state().state_known
}
pub fn is_connectable() -> bool {
state().connectable
}
pub fn is_connected() -> bool {
state().connected
}
pub fn is_pairing() -> bool {
state().pairing
}
pub fn is_pairing_requested() -> bool {
state().pairing_requested
}
pub fn write(bytes: &[u8]) -> Result<(), Error> {
let len = bytes.len() as u16;
let success = unsafe { ffi::ble_write(bytes.as_ptr(), len) };
if success {
Ok(())
} else {
Err(WRITE_FAILED)
}
}
pub fn read(buf: &mut [u8], max_len: usize) -> Result<usize, Error> {
let len: u16 = max_len.try_into()?;
let read_len = unsafe { super::ffi::ble_read(buf.as_mut_ptr(), len) };
Ok(read_len.try_into()?)
}

View File

@ -1,333 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <sys/sysevent.h>
/// package: trezorio.ble
///
/// def erase_bonds() -> bool:
/// """
/// Erases all BLE bonds
/// """
STATIC mp_obj_t mod_trezorio_BLE_erase_bonds(void) {
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 unpair() -> bool:
/// """
/// Erases bond for current connection, if any
/// """
STATIC mp_obj_t mod_trezorio_BLE_unpair(void) {
ble_command_t cmd = {.cmd_type = BLE_UNPAIR, .data_len = 0};
return mp_obj_new_bool(ble_issue_command(&cmd));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_unpair_obj,
mod_trezorio_BLE_unpair);
/// def start_comm() -> bool:
/// """
/// 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, name: str | None) -> bool:
/// """
/// Start advertising
/// """
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]);
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 = sizeof(ble_adv_start_cmd_data_t)};
// get a minimum of the two lengths
int len = name_len < BLE_ADV_NAME_LEN ? name_len : BLE_ADV_NAME_LEN;
cmd.data.adv_start.static_mac = false;
memcpy(cmd.data.adv_start.name, name_buf, len);
return mp_obj_new_bool(ble_issue_command(&cmd));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
mod_trezorio_BLE_start_advertising_obj, 1, 2,
mod_trezorio_BLE_start_advertising);
/// def stop_advertising() -> bool:
/// """
/// Stop advertising
/// """
STATIC mp_obj_t mod_trezorio_BLE_stop_advertising(void) {
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() -> bool:
/// """
/// Disconnect BLE
/// """
STATIC mp_obj_t mod_trezorio_BLE_disconnect(void) {
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);
/// def connection_state() -> int:
/// """
/// Returns current connection state as flags:
///
/// 0x01 state known
/// 0x02 connectable
/// 0x04 connected
/// 0x08 pairing
/// 0x10 pairing request
/// """
STATIC mp_obj_t mod_trezorio_BLE_connection_state(void) {
ble_state_t state;
ble_get_state(&state);
mp_int_t flags = (state.state_known << 0) | (state.connectable << 1) |
(state.connected << 2) | (state.pairing << 3) |
(state.pairing_requested << 4);
return MP_OBJ_NEW_SMALL_INT(flags);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_connection_state_obj,
mod_trezorio_BLE_connection_state);
const size_t CODE_LEN = 6;
static bool encode_pairing_code(mp_obj_t obj, uint8_t *outbuf) {
mp_int_t code = mp_obj_get_int(obj);
if (code < 0 || code > 999999) {
return false;
}
for (size_t i = 0; i < CODE_LEN; i++) {
outbuf[CODE_LEN - i - 1] = '0' + (code % 10);
code /= 10;
}
return true;
}
/// def allow_pairing(code: int) -> bool:
/// """
/// Accept BLE pairing request. Code must match the one received with
/// BLE_PAIRING_REQUEST event.
/// """
STATIC mp_obj_t mod_trezorio_BLE_allow_pairing(mp_obj_t code) {
ble_command_t cmd = {.cmd_type = BLE_ALLOW_PAIRING, .data_len = CODE_LEN};
if (!encode_pairing_code(code, cmd.data.raw)) {
return mp_const_false;
}
return mp_obj_new_bool(ble_issue_command(&cmd));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_allow_pairing_obj,
mod_trezorio_BLE_allow_pairing);
/// def reject_pairing() -> bool:
/// """
/// Reject BLE pairing request
/// """
STATIC mp_obj_t mod_trezorio_BLE_reject_pairing(void) {
ble_command_t cmd = {.cmd_type = BLE_REJECT_PAIRING, .data_len = 0};
return mp_obj_new_bool(ble_issue_command(&cmd));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_reject_pairing_obj,
mod_trezorio_BLE_reject_pairing);
/// class BleInterface:
/// """
/// BLE interface wrapper.
/// """
typedef struct _mp_obj_BleInterface_t {
mp_obj_base_t base;
} mp_obj_BleInterface_t;
/// def __init__(
/// self,
/// ) -> None:
/// """
/// Initialize BLE interface.
/// """
STATIC mp_obj_t mod_trezorio_BleInterface_make_new(const mp_obj_type_t *type,
size_t n_args, size_t n_kw,
const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 0, 0, false);
mp_obj_BleInterface_t *o = mp_obj_malloc(mp_obj_BleInterface_t, type);
return MP_OBJ_FROM_PTR(o);
}
/// def iface_num(self) -> int:
/// """
/// Returns the configured number of this interface.
/// """
STATIC mp_obj_t mod_trezorio_BleInterface_iface_num(mp_obj_t self) {
return MP_OBJ_NEW_SMALL_INT(SYSHANDLE_BLE_IFACE_0);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BleInterface_iface_num_obj,
mod_trezorio_BleInterface_iface_num);
/// def write(self, msg: bytes) -> int:
/// """
/// Sends message over BLE
/// """
STATIC mp_obj_t mod_trezorio_BleInterface_write(mp_obj_t self, 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_2(mod_trezorio_BleInterface_write_obj,
mod_trezorio_BleInterface_write);
/// def read(self, buf: bytearray, offset: int = 0) -> int:
/// """
/// Reads message using BLE (device).
/// """
STATIC mp_obj_t mod_trezorio_BleInterface_read(size_t n_args,
const mp_obj_t *args) {
mp_buffer_info_t buf = {0};
mp_get_buffer_raise(args[1], &buf, MP_BUFFER_WRITE);
int offset = 0;
if (n_args >= 2) {
offset = mp_obj_get_int(args[2]);
}
if (offset < 0) {
mp_raise_ValueError("Negative offset not allowed");
}
if (offset > buf.len) {
mp_raise_ValueError("Offset out of bounds");
}
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_BleInterface_read_obj,
2, 3,
mod_trezorio_BleInterface_read);
/// RX_PACKET_LEN: int
/// """Length of one BLE RX packet."""
/// TX_PACKET_LEN: int
/// """Length of one BLE TX packet."""
STATIC const mp_rom_map_elem_t mod_trezorio_BleInterface_locals_dict_table[] = {
{MP_ROM_QSTR(MP_QSTR_iface_num),
MP_ROM_PTR(&mod_trezorio_BleInterface_iface_num_obj)},
{MP_ROM_QSTR(MP_QSTR_write),
MP_ROM_PTR(&mod_trezorio_BleInterface_write_obj)},
{MP_ROM_QSTR(MP_QSTR_read),
MP_ROM_PTR(&mod_trezorio_BleInterface_read_obj)},
{MP_ROM_QSTR(MP_QSTR_RX_PACKET_LEN), MP_ROM_INT(BLE_RX_PACKET_SIZE)},
{MP_ROM_QSTR(MP_QSTR_TX_PACKET_LEN), MP_ROM_INT(BLE_TX_PACKET_SIZE)},
};
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_BleInterface_locals_dict,
mod_trezorio_BleInterface_locals_dict_table);
STATIC const mp_obj_type_t mod_trezorio_BleInterface_type = {
{&mp_type_type},
.name = MP_QSTR_BleInterface,
.make_new = mod_trezorio_BleInterface_make_new,
.locals_dict = (void *)&mod_trezorio_BleInterface_locals_dict,
};
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_erase_bonds),
MP_ROM_PTR(&mod_trezorio_BLE_erase_bonds_obj)},
{MP_ROM_QSTR(MP_QSTR_unpair), MP_ROM_PTR(&mod_trezorio_BLE_unpair_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)},
{MP_ROM_QSTR(MP_QSTR_peer_count),
MP_ROM_PTR(&mod_trezorio_BLE_peer_count_obj)},
{MP_ROM_QSTR(MP_QSTR_connection_state),
MP_ROM_PTR(&mod_trezorio_BLE_connection_state_obj)},
{MP_ROM_QSTR(MP_QSTR_allow_pairing),
MP_ROM_PTR(&mod_trezorio_BLE_allow_pairing_obj)},
{MP_ROM_QSTR(MP_QSTR_reject_pairing),
MP_ROM_PTR(&mod_trezorio_BLE_reject_pairing_obj)},
{MP_ROM_QSTR(MP_QSTR_BleInterface),
MP_ROM_PTR(&mod_trezorio_BleInterface_type)},
};
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};

View File

@ -51,9 +51,6 @@ 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"
@ -99,7 +96,6 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
#endif
#ifdef USE_BLE
{MP_ROM_QSTR(MP_QSTR_ble), MP_ROM_PTR(&mod_trezorio_BLE_module)},
{MP_ROM_QSTR(MP_QSTR_BLE_EVENT), MP_ROM_INT(SYSHANDLE_BLE)},
#endif
#ifdef USE_TOUCH

View File

@ -31,3 +31,7 @@ MP_REGISTER_MODULE(MP_QSTR_trezorproto, mp_module_trezorproto);
#if MICROPY_PY_TREZORTRANSLATE
MP_REGISTER_MODULE(MP_QSTR_trezortranslate, mp_module_trezortranslate);
#endif
#ifdef USE_BLE
MP_REGISTER_MODULE(MP_QSTR_trezorble, mp_module_trezorble);
#endif

View File

@ -0,0 +1,114 @@
from typing import *
# rust/src/trezorhal/ble/micropython.rs
class BleInterface:
"""
BLE interface wrapper.
"""
RX_PACKET_LEN: int
"""Length of one BLE RX packet."""
TX_PACKET_LEN: int
"""Length of one BLE TX packet."""
def iface_num(self) -> int:
"""
Returns the configured number of this interface.
"""
def write(self, msg: bytes) -> int:
"""
Sends message over BLE
"""
def read(self, buf: bytearray, offset: int = 0) -> int:
"""
Reads message using BLE (device).
"""
interface: BleInterface
"""BLE interface instance."""
# rust/src/trezorhal/ble/micropython.rs
def erase_bonds():
"""
Erases all BLE bonds.
Raises exception if BLE reports an error.
"""
# rust/src/trezorhal/ble/micropython.rs
def unpair():
"""
Erases bond for current connection, if any.
Raises exception if BLE driver reports an error.
"""
# rust/src/trezorhal/ble/micropython.rs
def start_comm():
"""
Start communication with BLE chip.
"""
# rust/src/trezorhal/ble/micropython.rs
def start_advertising(whitelist: bool, name: str | None):
"""
Start advertising.
Raises exception if BLE driver reports an error.
"""
# rust/src/trezorhal/ble/micropython.rs
def stop_advertising():
"""
Stop advertising.
Raises exception if BLE driver reports an error.
"""
# rust/src/trezorhal/ble/micropython.rs
def disconnect():
"""
Disconnect BLE.
Raises exception if BLE driver reports an error.
"""
# rust/src/trezorhal/ble/micropython.rs
def peer_count() -> int:
"""
Get peer count (number of bonded devices).
"""
# rust/src/trezorhal/ble/micropython.rs
def is_connected() -> bool:
"""
True if a host is connected to us. May or may not be paired.
"""
# rust/src/trezorhal/ble/micropython.rs
def connection_flags() -> list[str]:
"""
Returns current connection state as a list of string flags.
"""
# rust/src/trezorhal/ble/micropython.rs
def allow_pairing(code: int):
"""
Accept BLE pairing request. Code must match the one received with
BLE_PAIRING_REQUEST event.
Raises exception if BLE driver reports an error.
"""
# rust/src/trezorhal/ble/micropython.rs
def reject_pairing():
"""
Reject BLE pairing request.
Raises exception if BLE driver reports an error.
"""

View File

@ -1,112 +0,0 @@
from typing import *
# upymod/modtrezorio/modtrezorio-ble.h
def erase_bonds() -> bool:
"""
Erases all BLE bonds
"""
# upymod/modtrezorio/modtrezorio-ble.h
def unpair() -> bool:
"""
Erases bond for current connection, if any
"""
# upymod/modtrezorio/modtrezorio-ble.h
def start_comm() -> bool:
"""
Start communication with BLE chip
"""
# upymod/modtrezorio/modtrezorio-ble.h
def start_advertising(whitelist: bool, name: str | None) -> bool:
"""
Start advertising
"""
# upymod/modtrezorio/modtrezorio-ble.h
def stop_advertising() -> bool:
"""
Stop advertising
"""
# upymod/modtrezorio/modtrezorio-ble.h
def disconnect() -> bool:
"""
Disconnect BLE
"""
# upymod/modtrezorio/modtrezorio-ble.h
def peer_count() -> int:
"""
Get peer count (number of bonded devices)
"""
# upymod/modtrezorio/modtrezorio-ble.h
def connection_state() -> int:
"""
Returns current connection state as flags:
0x01 state known
0x02 connectable
0x04 connected
0x08 pairing
0x10 pairing request
"""
# upymod/modtrezorio/modtrezorio-ble.h
def allow_pairing(code: int) -> bool:
"""
Accept BLE pairing request. Code must match the one received with
BLE_PAIRING_REQUEST event.
"""
# upymod/modtrezorio/modtrezorio-ble.h
def reject_pairing() -> bool:
"""
Reject BLE pairing request
"""
# upymod/modtrezorio/modtrezorio-ble.h
class BleInterface:
"""
BLE interface wrapper.
"""
def __init__(
self,
) -> None:
"""
Initialize BLE interface.
"""
def iface_num(self) -> int:
"""
Returns the configured number of this interface.
"""
def write(self, msg: bytes) -> int:
"""
Sends message over BLE
"""
def read(self, buf: bytearray, offset: int = 0) -> int:
"""
Reads message using BLE (device).
"""
RX_PACKET_LEN: int
"""Length of one BLE RX packet."""
TX_PACKET_LEN: int
"""Length of one BLE TX packet."""

View File

@ -1,6 +1,6 @@
from trezorio import ble
from typing import TYPE_CHECKING
import trezorble as ble
from trezor import TR
if TYPE_CHECKING:

View File

@ -52,7 +52,7 @@ usb.bus.open(storage.device.get_device_id())
# enable BLE, allow connections
if utils.USE_BLE:
from trezorio import ble
import trezorble as ble
ble.start_comm()
# allow connections from bonded peers if any

View File

@ -24,10 +24,10 @@ workflow.start_default()
wire.setup(usb.iface_wire)
if utils.USE_BLE:
from trezorio import ble
import trezorble as ble
# initialize the wire codec over BLE
wire.setup(ble.BleInterface())
wire.setup(ble.interface)
# start the event loop
loop.run()