mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-24 15:38:22 +00:00
STM
This commit is contained in:
parent
ce54e90aec
commit
e36b8fda3b
101
common/protob/messages-ble.proto
Normal file
101
common/protob/messages-ble.proto
Normal file
@ -0,0 +1,101 @@
|
||||
syntax = "proto2";
|
||||
package hw.trezor.messages.ble;
|
||||
|
||||
// Sugar for easier handling in Java
|
||||
option java_package = "com.satoshilabs.trezor.lib.protobuf";
|
||||
option java_outer_classname = "TrezorMessageBLE";
|
||||
|
||||
option (include_in_bitcoin_only) = true;
|
||||
|
||||
import "messages.proto";
|
||||
|
||||
|
||||
/**
|
||||
* Request: initializes upload of a new ble firmware im
|
||||
* @start
|
||||
* @next UploadBLEFirmwareNextChunk
|
||||
* @next Failure
|
||||
*/
|
||||
message UploadBLEFirmwareInit {
|
||||
required bytes init_data = 1;
|
||||
required uint32 binsize = 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Response: Requests next chunk of a new ble firmware im
|
||||
* @next UploadBLEFirmwareChunk
|
||||
*/
|
||||
message UploadBLEFirmwareNextChunk {
|
||||
required uint32 offset = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: sends next chunk of a new ble firmware im
|
||||
* @next UploadBLEFirmwareNextChunk
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message UploadBLEFirmwareChunk {
|
||||
required bytes data = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request: erases all BLE bonds
|
||||
* @start
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message EraseBonds {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request: disconnect
|
||||
* @start
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message Disconnect {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Request: initiates new pairing request
|
||||
* @start
|
||||
* @next AuthKey
|
||||
* @next Failure
|
||||
*/
|
||||
message PairingRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: confirms pairing request with auth key entered on the device
|
||||
* @end
|
||||
*/
|
||||
message AuthKey {
|
||||
required bytes key = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request: initiates repairing request
|
||||
* @start
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message RepairRequest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request: initiates new pairing request with numeric comparison
|
||||
* @start
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message ComparisonRequest {
|
||||
required bytes key = 1;
|
||||
}
|
@ -39,6 +39,7 @@ message Failure {
|
||||
Failure_PinMismatch = 12;
|
||||
Failure_WipeCodeMismatch = 13;
|
||||
Failure_InvalidSession = 14;
|
||||
Failure_DeviceIsBusy = 15;
|
||||
Failure_FirmwareError = 99;
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +140,17 @@ enum MessageType {
|
||||
MessageType_FirmwareRequest = 8 [(bitcoin_only) = true, (wire_out) = true, (wire_bootloader) = true];
|
||||
MessageType_SelfTest = 32 [(bitcoin_only) = true, (wire_in) = true, (wire_bootloader) = true];
|
||||
|
||||
// BLE
|
||||
MessageType_UploadBLEFirmwareInit = 8000 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_UploadBLEFirmwareNextChunk = 8001 [(bitcoin_only) = true, (wire_out) = true];
|
||||
MessageType_UploadBLEFirmwareChunk = 8002 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_PairingRequest = 8003 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_AuthKey = 8004 [(bitcoin_only) = true, (wire_out) = true];
|
||||
MessageType_RepairRequest = 8005 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_EraseBonds = 8006 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_Disconnect = 8007 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_ComparisonRequest = 8008 [(bitcoin_only) = true, (wire_in) = true];
|
||||
|
||||
// Bitcoin
|
||||
MessageType_GetPublicKey = 11 [(bitcoin_only) = true, (wire_in) = true];
|
||||
MessageType_PublicKey = 12 [(bitcoin_only) = true, (wire_out) = true];
|
||||
|
@ -395,6 +395,9 @@ upload: ## upload firmware using trezorctl
|
||||
upload_prodtest: ## upload prodtest using trezorctl
|
||||
trezorctl firmware_update -s -f $(PRODTEST_BUILD_DIR)/prodtest.bin
|
||||
|
||||
upload_ble: ## upload ble firmware using trezorctl
|
||||
trezorctl ble update $(BLE_FIRMWARE_BUILD_DIR)/ble_firmware.zip
|
||||
|
||||
coverage: ## generate coverage report
|
||||
./tools/coverage-report
|
||||
|
||||
|
@ -20,7 +20,7 @@ if TREZOR_MODEL in ('1', ):
|
||||
)
|
||||
Return()
|
||||
|
||||
FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga"]
|
||||
FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga", "ble"]
|
||||
|
||||
CCFLAGS_MOD = ''
|
||||
CPPPATH_MOD = []
|
||||
@ -38,9 +38,9 @@ if TREZOR_MODEL in ('R', ):
|
||||
FONT_BIG=None
|
||||
elif TREZOR_MODEL in ('T', 'T3W1', 'DISC1'):
|
||||
FONT_NORMAL='Font_TTHoves_Regular_21'
|
||||
FONT_DEMIBOLD=None
|
||||
FONT_DEMIBOLD='Font_TTHoves_Regular_21'
|
||||
FONT_BOLD='Font_TTHoves_Bold_17'
|
||||
FONT_MONO=None
|
||||
FONT_MONO='Font_TTHoves_Regular_21'
|
||||
FONT_BIG=None
|
||||
|
||||
# modtrezorcrypto
|
||||
@ -87,6 +87,7 @@ SOURCE_MOD += [
|
||||
'embed/lib/fonts/fonts.c',
|
||||
'embed/lib/fonts/font_bitmap.c',
|
||||
'embed/lib/image.c',
|
||||
'embed/lib/protob_helpers.c',
|
||||
'embed/extmod/modtrezorcrypto/rand.c',
|
||||
'vendor/micropython/lib/uzlib/adler32.c',
|
||||
'vendor/micropython/lib/uzlib/crc32.c',
|
||||
@ -150,7 +151,6 @@ env.Replace(
|
||||
CPPPATH=[
|
||||
'embed/rust',
|
||||
'embed/bootloader',
|
||||
'embed/bootloader/nanopb',
|
||||
'embed/bootloader/protob',
|
||||
'embed/lib',
|
||||
'embed/models',
|
||||
|
@ -19,7 +19,7 @@ FEATURE_FLAGS = {
|
||||
"SYSTEM_VIEW": False,
|
||||
}
|
||||
|
||||
FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga"]
|
||||
FEATURES_WANTED = ["input", "sbu", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb", "optiga", "ble"]
|
||||
|
||||
CCFLAGS_MOD = ''
|
||||
CPPPATH_MOD = []
|
||||
@ -436,6 +436,7 @@ env.Replace(
|
||||
'embed/firmware',
|
||||
'embed/lib',
|
||||
'embed/models',
|
||||
'embed/firmware/ble',
|
||||
'embed/trezorhal',
|
||||
'embed/extmod/modtrezorui',
|
||||
'vendor/micropython',
|
||||
|
@ -372,6 +372,7 @@ SOURCE_MICROPYTHON = [
|
||||
]
|
||||
|
||||
SOURCE_UNIX = [
|
||||
'embed/trezorhal/unix/ble.c',
|
||||
'embed/trezorhal/unix/common.c',
|
||||
'embed/trezorhal/unix/display-unix.c',
|
||||
'embed/trezorhal/unix/flash.c',
|
||||
@ -382,6 +383,10 @@ SOURCE_UNIX = [
|
||||
'embed/unix/main.c',
|
||||
'embed/unix/main_main.c',
|
||||
'embed/unix/profile.c',
|
||||
'embed/lib/ble/state.c',
|
||||
'embed/lib/ble/fwu.c',
|
||||
'embed/lib/ble/messages.c',
|
||||
'embed/lib/ble/dfu.c',
|
||||
'vendor/micropython/shared/runtime/gchelper_generic.c',
|
||||
'vendor/micropython/ports/unix/alloc.c',
|
||||
'vendor/micropython/ports/unix/gccollect.c',
|
||||
@ -517,6 +522,7 @@ env.Replace(
|
||||
'embed/unix',
|
||||
'embed/trezorhal',
|
||||
'embed/trezorhal/unix',
|
||||
'embed/trezorhal/unix/ble',
|
||||
'embed/extmod/modtrezorui',
|
||||
'vendor/micropython',
|
||||
'vendor/micropython/ports/unix',
|
||||
@ -803,6 +809,7 @@ def cargo_build():
|
||||
features.append('touch')
|
||||
features.append('button')
|
||||
features.append('sd_card')
|
||||
features.append('ble')
|
||||
if TREZOR_MODEL in ('R', '1'):
|
||||
features.append('button')
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "ble_nus.h"
|
||||
#include "connection.h"
|
||||
#include "messages.pb.h"
|
||||
#include "nrf_dfu_types.h"
|
||||
#include "nrf_drv_spi.h"
|
||||
#include "nrf_log.h"
|
||||
#include "pm.h"
|
||||
@ -18,7 +19,7 @@
|
||||
|
||||
static uint8_t m_uart_rx_data[BLE_NUS_MAX_DATA_LEN];
|
||||
static uint8_t m_spi_tx_data[BLE_PACKET_SIZE];
|
||||
static bool m_uart_rx_data_ready_internal = false;
|
||||
static volatile bool m_uart_rx_data_ready_internal = false;
|
||||
|
||||
BLE_NUS_DEF(m_nus,
|
||||
NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< BLE NUS service instance. */
|
||||
@ -28,6 +29,11 @@ static const nrf_drv_spi_t spi =
|
||||
static volatile bool spi_xfer_done = true; /**< Flag used to indicate that SPI
|
||||
instance completed the transfer. */
|
||||
|
||||
#define CODE_PAGE_SIZE (MBR_PAGE_SIZE_IN_WORDS * sizeof(uint32_t))
|
||||
#define BOOTLOADER_SETTINGS_PAGE_SIZE (CODE_PAGE_SIZE)
|
||||
uint8_t m_dfu_settings_buffer[BOOTLOADER_SETTINGS_PAGE_SIZE]
|
||||
__attribute__((section(".bootloader_settings_page"))) __attribute__((used));
|
||||
|
||||
/**
|
||||
* @brief SPI user event handler.
|
||||
* @param event
|
||||
@ -353,15 +359,24 @@ void nus_data_handler(ble_nus_evt_t *p_evt) {
|
||||
/**@snippet [Handling the data received over BLE] */
|
||||
|
||||
void send_status_event(void) {
|
||||
uint8_t tx_data[] = {
|
||||
INTERNAL_EVENT_STATUS,
|
||||
(get_connection_handle() != BLE_CONN_HANDLE_INVALID) ? 1
|
||||
: 0, // connected
|
||||
is_advertising() ? 1 : 0, // advertising
|
||||
is_advertising_wl() ? 1 : 0, // advertising whitelist
|
||||
pm_peer_count(), // peer count
|
||||
};
|
||||
send_packet(INTERNAL_EVENT, tx_data, sizeof(tx_data));
|
||||
ble_version_t version = {0};
|
||||
nrf_dfu_settings_t *settins = (nrf_dfu_settings_t *)m_dfu_settings_buffer;
|
||||
|
||||
sd_ble_version_get(&version);
|
||||
|
||||
event_status_msg_t msg = {0};
|
||||
msg.msg_id = INTERNAL_EVENT_STATUS;
|
||||
msg.connected = (get_connection_handle() != BLE_CONN_HANDLE_INVALID) ? 1 : 0;
|
||||
msg.advertising = is_advertising() ? 1 : 0;
|
||||
msg.advertising_whitelist = is_advertising_wl() ? 1 : 0;
|
||||
msg.peer_count = pm_peer_count();
|
||||
msg.sd_version_number = version.version_number;
|
||||
msg.sd_company_id = version.company_id;
|
||||
msg.sd_subversion_number = version.subversion_number;
|
||||
msg.app_version = settins->app_version;
|
||||
msg.bld_version = settins->bootloader_version;
|
||||
|
||||
send_packet(INTERNAL_EVENT, (uint8_t *)&msg, sizeof(msg));
|
||||
}
|
||||
|
||||
void send_success_event(void) {
|
||||
|
@ -50,12 +50,19 @@
|
||||
#ifdef USE_RGB_LED
|
||||
#include "rgb_led.h"
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
#include "ble.h"
|
||||
#include "ble/state.h"
|
||||
#endif
|
||||
|
||||
#include "model.h"
|
||||
#include "usb.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "bootui.h"
|
||||
#include "messages.h"
|
||||
#include "messages.pb.h"
|
||||
#include "protob_helpers.h"
|
||||
#include "rust_ui.h"
|
||||
#include "unit_variant.h"
|
||||
|
||||
@ -81,13 +88,12 @@ static const uint8_t * const BOOTLOADER_KEYS[] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#define USB_IFACE_NUM 0
|
||||
|
||||
typedef enum {
|
||||
SHUTDOWN = 0,
|
||||
CONTINUE_TO_FIRMWARE = 0xAABBCCDD,
|
||||
RETURN_TO_MENU = 0x55667788,
|
||||
} usb_result_t;
|
||||
NO_RESULT = -1,
|
||||
} comm_result_t;
|
||||
|
||||
volatile secbool dont_optimize_out_true = sectrue;
|
||||
void failed_jump_to_firmware(void);
|
||||
@ -133,12 +139,112 @@ static void usb_init_all(secbool usb21_landing) {
|
||||
usb_start();
|
||||
}
|
||||
|
||||
static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
// if both are NULL, we don't have a firmware installed
|
||||
// let's show a webusb landing page in this case
|
||||
usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
|
||||
void stop_comm(void) {
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
#ifdef USE_BLE
|
||||
stop_advertising();
|
||||
ble_stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
static comm_result_t process_common_messages(uint8_t iface, uint8_t *buf,
|
||||
const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
uint16_t msg_id;
|
||||
uint32_t msg_size;
|
||||
uint32_t response;
|
||||
int32_t upload_response;
|
||||
if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) {
|
||||
// invalid header -> discard
|
||||
return NO_RESULT;
|
||||
}
|
||||
switch (msg_id) {
|
||||
case MessageType_MessageType_Initialize:
|
||||
process_msg_Initialize(iface, msg_size, buf, vhdr, hdr);
|
||||
break;
|
||||
case MessageType_MessageType_Ping:
|
||||
process_msg_Ping(iface, msg_size, buf);
|
||||
break;
|
||||
case MessageType_MessageType_WipeDevice:
|
||||
response = ui_screen_wipe_confirm();
|
||||
if (INPUT_CANCEL == response) {
|
||||
send_user_abort(iface, "Wipe cancelled");
|
||||
stop_comm();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
ui_screen_wipe();
|
||||
upload_response = process_msg_WipeDevice(iface, msg_size, buf);
|
||||
if (upload_response < 0) { // error
|
||||
screen_wipe_fail();
|
||||
stop_comm();
|
||||
return SHUTDOWN;
|
||||
} else { // success
|
||||
screen_wipe_success();
|
||||
stop_comm();
|
||||
return SHUTDOWN;
|
||||
}
|
||||
break;
|
||||
case MessageType_MessageType_FirmwareErase:
|
||||
process_msg_FirmwareErase(iface, msg_size, buf);
|
||||
break;
|
||||
case MessageType_MessageType_FirmwareUpload:
|
||||
upload_response = process_msg_FirmwareUpload(iface, msg_size, buf);
|
||||
if (upload_response < 0 &&
|
||||
upload_response !=
|
||||
UPLOAD_ERR_USER_ABORT) { // error, but not user abort
|
||||
if (upload_response == UPLOAD_ERR_BOOTLOADER_LOCKED) {
|
||||
ui_screen_install_restricted();
|
||||
} else {
|
||||
ui_screen_fail();
|
||||
}
|
||||
stop_comm();
|
||||
return SHUTDOWN;
|
||||
} else if (upload_response == UPLOAD_ERR_USER_ABORT) {
|
||||
stop_comm();
|
||||
return RETURN_TO_MENU;
|
||||
} else if (upload_response == 0) { // last chunk received
|
||||
ui_screen_install_progress_upload(1000);
|
||||
ui_screen_done(4, sectrue);
|
||||
ui_screen_done(3, secfalse);
|
||||
hal_delay(1000);
|
||||
ui_screen_done(2, secfalse);
|
||||
hal_delay(1000);
|
||||
ui_screen_done(1, secfalse);
|
||||
hal_delay(1000);
|
||||
stop_comm();
|
||||
ui_screen_boot_empty(true);
|
||||
return CONTINUE_TO_FIRMWARE;
|
||||
}
|
||||
break;
|
||||
case MessageType_MessageType_GetFeatures:
|
||||
process_msg_GetFeatures(iface, msg_size, buf, vhdr, hdr);
|
||||
break;
|
||||
#ifdef USE_OPTIGA
|
||||
case MessageType_MessageType_UnlockBootloader:
|
||||
response = ui_screen_unlock_bootloader_confirm();
|
||||
if (INPUT_CANCEL == response) {
|
||||
send_user_abort(USB_IFACE_NUM, "Bootloader unlock cancelled");
|
||||
stop_comm();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf);
|
||||
screen_unlock_bootloader_success();
|
||||
stop_comm();
|
||||
return SHUTDOWN;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
process_msg_unknown(iface, msg_size, buf);
|
||||
break;
|
||||
}
|
||||
|
||||
return NO_RESULT;
|
||||
}
|
||||
|
||||
static comm_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
uint8_t buf[USB_PACKET_SIZE];
|
||||
|
||||
for (;;) {
|
||||
@ -150,107 +256,155 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr,
|
||||
if (r != USB_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
uint16_t msg_id;
|
||||
uint32_t msg_size;
|
||||
uint32_t response;
|
||||
if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) {
|
||||
// invalid header -> discard
|
||||
continue;
|
||||
}
|
||||
switch (msg_id) {
|
||||
case MessageType_MessageType_Initialize:
|
||||
process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr);
|
||||
break;
|
||||
case MessageType_MessageType_Ping:
|
||||
process_msg_Ping(USB_IFACE_NUM, msg_size, buf);
|
||||
break;
|
||||
case MessageType_MessageType_WipeDevice:
|
||||
response = ui_screen_wipe_confirm();
|
||||
if (INPUT_CANCEL == response) {
|
||||
send_user_abort(USB_IFACE_NUM, "Wipe cancelled");
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
ui_screen_wipe();
|
||||
r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf);
|
||||
if (r < 0) { // error
|
||||
screen_wipe_fail();
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return SHUTDOWN;
|
||||
} else { // success
|
||||
screen_wipe_success();
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return SHUTDOWN;
|
||||
}
|
||||
break;
|
||||
case MessageType_MessageType_FirmwareErase:
|
||||
process_msg_FirmwareErase(USB_IFACE_NUM, msg_size, buf);
|
||||
break;
|
||||
case MessageType_MessageType_FirmwareUpload:
|
||||
r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf);
|
||||
if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort
|
||||
if (r == UPLOAD_ERR_BOOTLOADER_LOCKED) {
|
||||
ui_screen_install_restricted();
|
||||
} else {
|
||||
ui_screen_fail();
|
||||
}
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return SHUTDOWN;
|
||||
} else if (r == UPLOAD_ERR_USER_ABORT) {
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return RETURN_TO_MENU;
|
||||
} else if (r == 0) { // last chunk received
|
||||
ui_screen_install_progress_upload(1000);
|
||||
ui_screen_done(4, sectrue);
|
||||
ui_screen_done(3, secfalse);
|
||||
hal_delay(1000);
|
||||
ui_screen_done(2, secfalse);
|
||||
hal_delay(1000);
|
||||
ui_screen_done(1, secfalse);
|
||||
hal_delay(1000);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
ui_screen_boot_empty(true);
|
||||
return CONTINUE_TO_FIRMWARE;
|
||||
}
|
||||
break;
|
||||
case MessageType_MessageType_GetFeatures:
|
||||
process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr);
|
||||
break;
|
||||
#ifdef USE_OPTIGA
|
||||
case MessageType_MessageType_UnlockBootloader:
|
||||
response = ui_screen_unlock_bootloader_confirm();
|
||||
if (INPUT_CANCEL == response) {
|
||||
send_user_abort(USB_IFACE_NUM, "Bootloader unlock cancelled");
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf);
|
||||
screen_unlock_bootloader_success();
|
||||
hal_delay(100);
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
return SHUTDOWN;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
process_msg_unknown(USB_IFACE_NUM, msg_size, buf);
|
||||
break;
|
||||
|
||||
comm_result_t res = process_common_messages(USB_IFACE_NUM, buf, vhdr, hdr);
|
||||
|
||||
if (res != NO_RESULT) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_BLE
|
||||
static comm_result_t bootloader_ble_loop(const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
start_advertising(true);
|
||||
|
||||
uint8_t buf[BLE_PACKET_SIZE];
|
||||
|
||||
uint8_t active_iface = 0;
|
||||
int r = 0;
|
||||
|
||||
for (;;) {
|
||||
for (;;) {
|
||||
r = ble_ext_comm_receive(buf, sizeof(buf));
|
||||
|
||||
if (r == BLE_PACKET_SIZE) {
|
||||
active_iface = BLE_EXT_IFACE_NUM;
|
||||
break;
|
||||
}
|
||||
|
||||
r = ble_int_comm_receive(buf, sizeof(buf));
|
||||
if (r != 0) {
|
||||
active_iface = BLE_INT_IFACE_NUM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (active_iface == BLE_INT_IFACE_NUM) {
|
||||
for (;;) {
|
||||
bool next = false;
|
||||
if (r == 0) {
|
||||
r = ble_int_comm_receive(buf, sizeof(buf));
|
||||
|
||||
if (r == 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
uint16_t msg_id;
|
||||
uint32_t msg_size;
|
||||
uint32_t response;
|
||||
if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) {
|
||||
// invalid header -> discard
|
||||
continue;
|
||||
}
|
||||
switch (msg_id) {
|
||||
case MessageType_MessageType_ComparisonRequest: // pairing request
|
||||
response =
|
||||
process_msg_ComparisonRequest(active_iface, msg_size, buf);
|
||||
if (response != INPUT_CONFIRM) {
|
||||
stop_comm();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
screen_connect();
|
||||
next = true;
|
||||
|
||||
break;
|
||||
case MessageType_MessageType_PairingRequest: // pairing request
|
||||
response = process_msg_Pairing(active_iface, msg_size, buf);
|
||||
if (response != INPUT_CONFIRM) {
|
||||
stop_comm();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
screen_connect();
|
||||
next = true;
|
||||
|
||||
break;
|
||||
case MessageType_MessageType_RepairRequest: // repairing request
|
||||
response = process_msg_Repair(active_iface, msg_size, buf);
|
||||
if (response != INPUT_CONFIRM) {
|
||||
stop_comm();
|
||||
return RETURN_TO_MENU;
|
||||
}
|
||||
// screen_connect();
|
||||
// todo - screen connect or timeout?
|
||||
break;
|
||||
default:
|
||||
process_msg_unknown(active_iface, msg_size, buf);
|
||||
break;
|
||||
}
|
||||
if (next) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active_iface == BLE_EXT_IFACE_NUM) {
|
||||
for (;;) {
|
||||
if (r == 0) {
|
||||
r = ble_ext_comm_receive(buf, sizeof(buf));
|
||||
|
||||
if (r != BLE_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
r = 0;
|
||||
|
||||
comm_result_t res =
|
||||
process_common_messages(active_iface, buf, vhdr, hdr);
|
||||
|
||||
if (res != NO_RESULT) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static comm_result_t bootloader_comm_select(const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
// if both are NULL, we don't have a firmware installed
|
||||
// let's show a webusb landing page in this case
|
||||
usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse);
|
||||
|
||||
#ifdef USE_BLE
|
||||
bool usb = false;
|
||||
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
hal_delay(1);
|
||||
if (usb_configured_now() == sectrue) {
|
||||
usb = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (usb) {
|
||||
return bootloader_usb_loop(vhdr, hdr);
|
||||
} else {
|
||||
usb_stop();
|
||||
usb_deinit();
|
||||
ble_start();
|
||||
return bootloader_ble_loop(vhdr, hdr);
|
||||
}
|
||||
#else
|
||||
return bootloader_usb_loop(vhdr, hdr);
|
||||
#endif
|
||||
}
|
||||
|
||||
secbool check_vendor_header_keys(const vendor_header *const vhdr) {
|
||||
return check_vendor_header_sig(vhdr, BOOTLOADER_KEY_M, BOOTLOADER_KEY_N,
|
||||
BOOTLOADER_KEYS);
|
||||
@ -475,6 +629,9 @@ int bootloader_main(void) {
|
||||
#ifdef USE_RGB_LED
|
||||
rgb_led_init();
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
ble_comm_init();
|
||||
#endif
|
||||
|
||||
unit_variant_init();
|
||||
|
||||
@ -557,7 +714,7 @@ int bootloader_main(void) {
|
||||
ui_screen_welcome();
|
||||
|
||||
// and start the usb loop
|
||||
switch (bootloader_usb_loop(NULL, NULL)) {
|
||||
switch (bootloader_comm_select(NULL, NULL)) {
|
||||
case CONTINUE_TO_FIRMWARE:
|
||||
continue_to_firmware = sectrue;
|
||||
continue_to_firmware_backup = sectrue;
|
||||
@ -614,7 +771,7 @@ int bootloader_main(void) {
|
||||
break;
|
||||
case SCREEN_WAIT_FOR_HOST:
|
||||
screen_connect();
|
||||
switch (bootloader_usb_loop(&vhdr, hdr)) {
|
||||
switch (bootloader_comm_select(&vhdr, hdr)) {
|
||||
case CONTINUE_TO_FIRMWARE:
|
||||
continue_to_firmware = sectrue;
|
||||
continue_to_firmware_backup = sectrue;
|
||||
|
@ -24,6 +24,11 @@
|
||||
#include <pb_encode.h>
|
||||
#include "messages.pb.h"
|
||||
|
||||
#include TREZOR_BOARD
|
||||
|
||||
#ifdef USE_BLE
|
||||
#include "ble.h"
|
||||
#endif
|
||||
#include "common.h"
|
||||
#include "flash.h"
|
||||
#include "image.h"
|
||||
@ -35,6 +40,7 @@
|
||||
|
||||
#include "bootui.h"
|
||||
#include "messages.h"
|
||||
#include "protob_helpers.h"
|
||||
#include "rust_ui.h"
|
||||
|
||||
#include "memzero.h"
|
||||
@ -48,30 +54,9 @@
|
||||
#include "secret.h"
|
||||
#endif
|
||||
|
||||
#define MSG_HEADER1_LEN 9
|
||||
#define MSG_HEADER2_LEN 1
|
||||
|
||||
secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id,
|
||||
uint32_t *msg_size) {
|
||||
if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
|
||||
return secfalse;
|
||||
}
|
||||
*msg_id = (buf[3] << 8) + buf[4];
|
||||
*msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8];
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint8_t iface_num;
|
||||
uint8_t packet_index;
|
||||
uint8_t packet_pos;
|
||||
uint8_t buf[USB_PACKET_SIZE];
|
||||
} usb_write_state;
|
||||
|
||||
/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */
|
||||
static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf,
|
||||
size_t count) {
|
||||
usb_write_state *state = (usb_write_state *)(stream->state);
|
||||
static bool _write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) {
|
||||
write_state *state = (write_state *)(stream->state);
|
||||
|
||||
size_t written = 0;
|
||||
// while we have data left
|
||||
@ -91,9 +76,20 @@ static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf,
|
||||
USB_PACKET_SIZE - state->packet_pos);
|
||||
written += USB_PACKET_SIZE - state->packet_pos;
|
||||
// send packet
|
||||
int r = usb_webusb_write_blocking(state->iface_num, state->buf,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
ensure(sectrue * (r == USB_PACKET_SIZE), NULL);
|
||||
|
||||
if (state->iface_num == USB_IFACE_NUM) {
|
||||
int r = usb_webusb_write_blocking(state->iface_num, state->buf,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
ensure(sectrue * (r == USB_PACKET_SIZE), NULL);
|
||||
}
|
||||
#ifdef USE_BLE
|
||||
else if (state->iface_num == BLE_INT_IFACE_NUM) {
|
||||
ble_int_comm_send(state->buf, USB_PACKET_SIZE, INTERNAL_MESSAGE);
|
||||
} else if (state->iface_num == BLE_EXT_IFACE_NUM) {
|
||||
ble_int_comm_send(state->buf, USB_PACKET_SIZE, EXTERNAL_MESSAGE);
|
||||
}
|
||||
#endif
|
||||
|
||||
// prepare new packet
|
||||
state->packet_index++;
|
||||
memzero(state->buf, USB_PACKET_SIZE);
|
||||
@ -105,7 +101,7 @@ static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _usb_write_flush(usb_write_state *state) {
|
||||
static void _write_flush(write_state *state) {
|
||||
// if packet is not filled up completely
|
||||
if (state->packet_pos < USB_PACKET_SIZE) {
|
||||
// pad it with zeroes
|
||||
@ -113,94 +109,53 @@ static void _usb_write_flush(usb_write_state *state) {
|
||||
USB_PACKET_SIZE - state->packet_pos);
|
||||
}
|
||||
// send packet
|
||||
int r = usb_webusb_write_blocking(state->iface_num, state->buf,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
ensure(sectrue * (r == USB_PACKET_SIZE), NULL);
|
||||
if (state->iface_num == USB_IFACE_NUM) {
|
||||
int r = usb_webusb_write_blocking(state->iface_num, state->buf,
|
||||
USB_PACKET_SIZE, USB_TIMEOUT);
|
||||
ensure(sectrue * (r == USB_PACKET_SIZE), NULL);
|
||||
}
|
||||
#ifdef USE_BLE
|
||||
else if (state->iface_num == BLE_INT_IFACE_NUM) {
|
||||
ble_int_comm_send(state->buf, USB_PACKET_SIZE, INTERNAL_MESSAGE);
|
||||
} else if (state->iface_num == BLE_EXT_IFACE_NUM) {
|
||||
ble_int_comm_send(state->buf, USB_PACKET_SIZE, EXTERNAL_MESSAGE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static secbool _send_msg(uint8_t iface_num, uint16_t msg_id,
|
||||
const pb_msgdesc_t *fields, const void *msg) {
|
||||
// determine message size by serializing it into a dummy stream
|
||||
pb_ostream_t sizestream = {.callback = NULL,
|
||||
.state = NULL,
|
||||
.max_size = SIZE_MAX,
|
||||
.bytes_written = 0,
|
||||
.errmsg = NULL};
|
||||
if (false == pb_encode(&sizestream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
const uint32_t msg_size = sizestream.bytes_written;
|
||||
#define BLE_GAP_PASSKEY_LEN 6
|
||||
|
||||
usb_write_state state = {
|
||||
.iface_num = iface_num,
|
||||
.packet_index = 0,
|
||||
.packet_pos = MSG_HEADER1_LEN,
|
||||
.buf =
|
||||
{
|
||||
'?',
|
||||
'#',
|
||||
'#',
|
||||
(msg_id >> 8) & 0xFF,
|
||||
msg_id & 0xFF,
|
||||
(msg_size >> 24) & 0xFF,
|
||||
(msg_size >> 16) & 0xFF,
|
||||
(msg_size >> 8) & 0xFF,
|
||||
msg_size & 0xFF,
|
||||
},
|
||||
};
|
||||
/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */
|
||||
static bool _write_authkey(pb_ostream_t *stream, const pb_field_iter_t *field,
|
||||
void *const *arg) {
|
||||
uint8_t *key = (uint8_t *)(*arg);
|
||||
if (!pb_encode_tag_for_field(stream, field)) return false;
|
||||
|
||||
pb_ostream_t stream = {.callback = &_usb_write,
|
||||
.state = &state,
|
||||
.max_size = SIZE_MAX,
|
||||
.bytes_written = 0,
|
||||
.errmsg = NULL};
|
||||
|
||||
if (false == pb_encode(&stream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
_usb_write_flush(&state);
|
||||
|
||||
return sectrue;
|
||||
return pb_encode_string(stream, (uint8_t *)key, BLE_GAP_PASSKEY_LEN);
|
||||
}
|
||||
|
||||
#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default
|
||||
#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \
|
||||
{ msg_send.FIELD = VALUE; }
|
||||
#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
msg_send.FIELD = VALUE; \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \
|
||||
strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \
|
||||
strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \
|
||||
memcpy(msg_send.FIELD.bytes, VALUE, \
|
||||
MIN(LEN, sizeof(msg_send.FIELD.bytes))); \
|
||||
msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \
|
||||
}
|
||||
#define MSG_SEND(TYPE) \
|
||||
_send_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, &msg_send)
|
||||
static bool _read_authkey(pb_istream_t *stream, const pb_field_t *field,
|
||||
void **arg) {
|
||||
uint8_t *key_buffer = (uint8_t *)(*arg);
|
||||
|
||||
typedef struct {
|
||||
uint8_t iface_num;
|
||||
uint8_t packet_index;
|
||||
uint8_t packet_pos;
|
||||
uint8_t *buf;
|
||||
} usb_read_state;
|
||||
if (stream->bytes_left > BLE_GAP_PASSKEY_LEN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(key_buffer, 0, BLE_GAP_PASSKEY_LEN);
|
||||
|
||||
while (stream->bytes_left) {
|
||||
// read data
|
||||
if (!pb_read(stream, (pb_byte_t *)(key_buffer),
|
||||
(stream->bytes_left > BLE_GAP_PASSKEY_LEN)
|
||||
? BLE_GAP_PASSKEY_LEN
|
||||
: stream->bytes_left)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) {
|
||||
for (int retry = 0;; retry++) {
|
||||
@ -220,16 +175,54 @@ static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_BLE
|
||||
static void _ble_read_retry(uint8_t iface_num, uint8_t *buf) {
|
||||
for (int retry = 0;; retry++) {
|
||||
int r = ble_ext_comm_receive(buf, BLE_PACKET_SIZE);
|
||||
if (r != BLE_PACKET_SIZE) { // reading failed
|
||||
if (r == 0 && retry < 500) {
|
||||
// only timeout => let's try again
|
||||
HAL_Delay(10);
|
||||
continue;
|
||||
} else {
|
||||
// error
|
||||
error_shutdown("BLE ERROR",
|
||||
"Error reading from BLE. Try different BLE cable.");
|
||||
}
|
||||
}
|
||||
return; // success
|
||||
}
|
||||
}
|
||||
|
||||
static void _ble_read_retry_int(uint8_t iface_num, uint8_t *buf) {
|
||||
for (int retry = 0;; retry++) {
|
||||
int r = ble_int_comm_receive(buf, USB_PACKET_SIZE);
|
||||
if (r == 0) { // reading failed
|
||||
if (retry < 500) {
|
||||
// only timeout => let's try again
|
||||
HAL_Delay(10);
|
||||
continue;
|
||||
} else {
|
||||
// error
|
||||
error_shutdown("BLE ERROR",
|
||||
"Error reading from BLE. Try different BLE cable.");
|
||||
}
|
||||
}
|
||||
return; // success
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */
|
||||
static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) {
|
||||
usb_read_state *state = (usb_read_state *)(stream->state);
|
||||
static bool _read(pb_istream_t *stream, uint8_t *buf, size_t count) {
|
||||
read_state *state = (read_state *)(stream->state);
|
||||
|
||||
size_t read = 0;
|
||||
// while we have data left
|
||||
while (read < count) {
|
||||
size_t remaining = count - read;
|
||||
// if all remaining data fit into our packet
|
||||
if (state->packet_pos + remaining <= USB_PACKET_SIZE) {
|
||||
if (state->packet_pos + remaining <= state->packet_size) {
|
||||
// append data from buf to state->buf
|
||||
memcpy(buf + read, state->buf + state->packet_pos, remaining);
|
||||
// advance position
|
||||
@ -239,10 +232,19 @@ static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) {
|
||||
} else {
|
||||
// append data that fits
|
||||
memcpy(buf + read, state->buf + state->packet_pos,
|
||||
USB_PACKET_SIZE - state->packet_pos);
|
||||
read += USB_PACKET_SIZE - state->packet_pos;
|
||||
state->packet_size - state->packet_pos);
|
||||
read += state->packet_size - state->packet_pos;
|
||||
// read next packet (with retry)
|
||||
_usb_webusb_read_retry(state->iface_num, state->buf);
|
||||
#ifdef USE_BLE
|
||||
if (state->iface_num == BLE_EXT_IFACE_NUM) {
|
||||
_ble_read_retry(state->iface_num, state->buf);
|
||||
} else if (state->iface_num == BLE_INT_IFACE_NUM) {
|
||||
_ble_read_retry_int(state->iface_num, state->buf);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
_usb_webusb_read_retry(state->iface_num, state->buf);
|
||||
}
|
||||
// prepare next packet
|
||||
state->packet_index++;
|
||||
state->packet_pos = MSG_HEADER2_LEN;
|
||||
@ -252,43 +254,24 @@ static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _usb_read_flush(usb_read_state *state) { (void)state; }
|
||||
static void _read_flush(read_state *state) { (void)state; }
|
||||
|
||||
static secbool _recv_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const pb_msgdesc_t *fields, void *msg) {
|
||||
usb_read_state state = {.iface_num = iface_num,
|
||||
.packet_index = 0,
|
||||
.packet_pos = MSG_HEADER1_LEN,
|
||||
.buf = buf};
|
||||
|
||||
pb_istream_t stream = {.callback = &_usb_read,
|
||||
.state = &state,
|
||||
.bytes_left = msg_size,
|
||||
.errmsg = NULL};
|
||||
|
||||
if (false == pb_decode_noinit(&stream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
_usb_read_flush(&state);
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default
|
||||
#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \
|
||||
{ \
|
||||
msg_recv.FIELD.funcs.decode = &CALLBACK; \
|
||||
msg_recv.FIELD.arg = (void *)ARGUMENT; \
|
||||
}
|
||||
#define MSG_RECV(TYPE) \
|
||||
_recv_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv)
|
||||
#define MSG_SEND_BLD(msg) (MSG_SEND(msg, _write, _write_flush))
|
||||
#ifdef USE_BLE
|
||||
#define MSG_RECV_BLD(msg, iface_num) \
|
||||
(MSG_RECV( \
|
||||
msg, _read, _read_flush, \
|
||||
((iface_num) == BLE_EXT_IFACE_NUM ? BLE_PACKET_SIZE : USB_PACKET_SIZE)))
|
||||
#else
|
||||
#define MSG_RECV_BLD(msg, iface_num) \
|
||||
(MSG_RECV(msg, _read, _read_flush, USB_PACKET_SIZE))
|
||||
#endif
|
||||
|
||||
void send_user_abort(uint8_t iface_num, const char *msg) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled);
|
||||
MSG_SEND_ASSIGN_STRING(message, msg);
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
}
|
||||
|
||||
static void send_msg_features(uint8_t iface_num,
|
||||
@ -320,14 +303,66 @@ static void send_msg_features(uint8_t iface_num,
|
||||
MSG_SEND_ASSIGN_VALUE(bootloader_locked,
|
||||
(secret_bootloader_locked() == sectrue));
|
||||
#endif
|
||||
MSG_SEND(Features);
|
||||
MSG_SEND_BLD(Features);
|
||||
}
|
||||
|
||||
uint32_t process_msg_ComparisonRequest(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf) {
|
||||
uint8_t buffer[BLE_GAP_PASSKEY_LEN];
|
||||
MSG_RECV_INIT(ComparisonRequest);
|
||||
MSG_RECV_CALLBACK(key, _read_authkey, buffer);
|
||||
MSG_RECV_BLD(ComparisonRequest, iface_num);
|
||||
|
||||
uint32_t result = screen_comparison_confirm(buffer, BLE_GAP_PASSKEY_LEN);
|
||||
|
||||
if (result == INPUT_CONFIRM) {
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND_BLD(Success);
|
||||
} else {
|
||||
send_user_abort(iface_num, "Pairing cancelled");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t process_msg_Pairing(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf) {
|
||||
uint8_t buffer[BLE_GAP_PASSKEY_LEN];
|
||||
MSG_RECV_INIT(PairingRequest);
|
||||
MSG_RECV_BLD(PairingRequest, iface_num);
|
||||
|
||||
uint32_t result = screen_pairing_confirm(buffer);
|
||||
|
||||
if (result == INPUT_CONFIRM) {
|
||||
MSG_SEND_INIT(AuthKey);
|
||||
MSG_SEND_CALLBACK(key, _write_authkey, buffer);
|
||||
MSG_SEND_BLD(AuthKey);
|
||||
} else {
|
||||
send_user_abort(iface_num, "Pairing cancelled");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t process_msg_Repair(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf) {
|
||||
MSG_RECV_INIT(RepairRequest);
|
||||
MSG_RECV_BLD(RepairRequest, iface_num);
|
||||
uint32_t result = screen_repair_confirm();
|
||||
if (result == INPUT_CONFIRM) {
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND_BLD(Success);
|
||||
} else {
|
||||
send_user_abort(iface_num, "Pairing cancelled");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
MSG_RECV_INIT(Initialize);
|
||||
MSG_RECV(Initialize);
|
||||
MSG_RECV_BLD(Initialize, iface_num);
|
||||
send_msg_features(iface_num, vhdr, hdr);
|
||||
}
|
||||
|
||||
@ -335,17 +370,17 @@ void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const vendor_header *const vhdr,
|
||||
const image_header *const hdr) {
|
||||
MSG_RECV_INIT(GetFeatures);
|
||||
MSG_RECV(GetFeatures);
|
||||
MSG_RECV_BLD(GetFeatures, iface_num);
|
||||
send_msg_features(iface_num, vhdr, hdr);
|
||||
}
|
||||
|
||||
void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
||||
MSG_RECV_INIT(Ping);
|
||||
MSG_RECV(Ping);
|
||||
MSG_RECV_BLD(Ping, iface_num);
|
||||
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND_ASSIGN_STRING(message, msg_recv.message);
|
||||
MSG_SEND(Success);
|
||||
MSG_SEND_BLD(Success);
|
||||
}
|
||||
|
||||
static uint32_t firmware_remaining, firmware_block, chunk_requested;
|
||||
@ -357,7 +392,7 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size,
|
||||
chunk_requested = 0;
|
||||
|
||||
MSG_RECV_INIT(FirmwareErase);
|
||||
MSG_RECV(FirmwareErase);
|
||||
MSG_RECV_BLD(FirmwareErase, iface_num);
|
||||
|
||||
firmware_remaining = msg_recv.has_length ? msg_recv.length : 0;
|
||||
if ((firmware_remaining > 0) &&
|
||||
@ -370,13 +405,13 @@ void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, 0);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
MSG_SEND_BLD(FirmwareRequest);
|
||||
} else {
|
||||
// invalid firmware size
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Wrong firmware size");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,13 +523,13 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf) {
|
||||
MSG_RECV_INIT(FirmwareUpload);
|
||||
MSG_RECV_CALLBACK(payload, _read_payload, read_offset);
|
||||
const secbool r = MSG_RECV(FirmwareUpload);
|
||||
const secbool r = MSG_RECV_BLD(FirmwareUpload, iface_num);
|
||||
|
||||
if (sectrue != r || chunk_size != (chunk_requested + read_offset)) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
@ -509,7 +544,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_VENDOR_HEADER;
|
||||
}
|
||||
|
||||
@ -517,7 +552,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG;
|
||||
}
|
||||
|
||||
@ -530,7 +565,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_IMAGE_HEADER;
|
||||
}
|
||||
|
||||
@ -538,7 +573,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_IMAGE_MODEL;
|
||||
}
|
||||
|
||||
@ -547,7 +582,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG;
|
||||
}
|
||||
|
||||
@ -628,7 +663,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
chunk_requested = chunk_limit - read_offset;
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, read_offset);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
MSG_SEND_BLD(FirmwareRequest);
|
||||
|
||||
firmware_remaining -= read_offset;
|
||||
return (int)firmware_remaining;
|
||||
@ -644,7 +679,7 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Firmware too big");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_FIRMWARE_TOO_BIG;
|
||||
}
|
||||
|
||||
@ -656,14 +691,14 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
MSG_SEND_BLD(FirmwareRequest);
|
||||
return (int)firmware_remaining;
|
||||
}
|
||||
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return UPLOAD_ERR_INVALID_CHUNK_HASH;
|
||||
}
|
||||
|
||||
@ -693,10 +728,10 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size,
|
||||
MSG_SEND_INIT(FirmwareRequest);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
|
||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
||||
MSG_SEND(FirmwareRequest);
|
||||
MSG_SEND_BLD(FirmwareRequest);
|
||||
} else {
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND(Success);
|
||||
MSG_SEND_BLD(Success);
|
||||
}
|
||||
return (int)firmware_remaining;
|
||||
}
|
||||
@ -711,11 +746,11 @@ int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Could not erase flash");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
return WIPE_ERR_CANNOT_ERASE;
|
||||
} else {
|
||||
MSG_SEND_INIT(Success);
|
||||
MSG_SEND(Success);
|
||||
MSG_SEND_BLD(Success);
|
||||
return WIPE_OK;
|
||||
}
|
||||
}
|
||||
@ -740,7 +775,7 @@ void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
||||
MSG_SEND_INIT(Failure);
|
||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_UnexpectedMessage);
|
||||
MSG_SEND_ASSIGN_STRING(message, "Unexpected message");
|
||||
MSG_SEND(Failure);
|
||||
MSG_SEND_BLD(Failure);
|
||||
}
|
||||
|
||||
#ifdef USE_OPTIGA
|
||||
|
@ -25,8 +25,10 @@
|
||||
#include "secbool.h"
|
||||
#include TREZOR_BOARD
|
||||
|
||||
#define USB_IFACE_NUM 0
|
||||
#define BLE_INT_IFACE_NUM 16
|
||||
#define BLE_EXT_IFACE_NUM 17
|
||||
#define USB_TIMEOUT 500
|
||||
#define USB_PACKET_SIZE 64
|
||||
|
||||
#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2
|
||||
|
||||
@ -75,4 +77,11 @@ void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size,
|
||||
|
||||
secbool bootloader_WipeDevice(void);
|
||||
|
||||
uint32_t process_msg_ComparisonRequest(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf);
|
||||
uint32_t process_msg_Pairing(uint8_t iface_num, uint32_t msg_size,
|
||||
uint8_t *buf);
|
||||
|
||||
uint32_t process_msg_Repair(uint8_t iface_num, uint32_t msg_size, uint8_t *buf);
|
||||
|
||||
#endif
|
||||
|
@ -42,6 +42,18 @@ PB_BIND(FirmwareUpload, FirmwareUpload, AUTO)
|
||||
PB_BIND(UnlockBootloader, UnlockBootloader, AUTO)
|
||||
|
||||
|
||||
PB_BIND(PairingRequest, PairingRequest, AUTO)
|
||||
|
||||
|
||||
PB_BIND(AuthKey, AuthKey, AUTO)
|
||||
|
||||
|
||||
PB_BIND(RepairRequest, RepairRequest, AUTO)
|
||||
|
||||
|
||||
PB_BIND(ComparisonRequest, ComparisonRequest, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -23,7 +23,11 @@ typedef enum _MessageType {
|
||||
MessageType_MessageType_ButtonRequest = 26,
|
||||
MessageType_MessageType_ButtonAck = 27,
|
||||
MessageType_MessageType_GetFeatures = 55,
|
||||
MessageType_MessageType_UnlockBootloader = 96
|
||||
MessageType_MessageType_UnlockBootloader = 96,
|
||||
MessageType_MessageType_PairingRequest = 8003,
|
||||
MessageType_MessageType_AuthKey = 8004,
|
||||
MessageType_MessageType_RepairRequest = 8005,
|
||||
MessageType_MessageType_ComparisonRequest = 8008
|
||||
} MessageType;
|
||||
|
||||
typedef enum _FailureType {
|
||||
@ -38,10 +42,18 @@ typedef enum _ButtonRequestType {
|
||||
} ButtonRequestType;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _AuthKey {
|
||||
pb_callback_t key;
|
||||
} AuthKey;
|
||||
|
||||
typedef struct _ButtonAck {
|
||||
char dummy_field;
|
||||
} ButtonAck;
|
||||
|
||||
typedef struct _ComparisonRequest {
|
||||
pb_callback_t key;
|
||||
} ComparisonRequest;
|
||||
|
||||
typedef struct _GetFeatures {
|
||||
char dummy_field;
|
||||
} GetFeatures;
|
||||
@ -50,6 +62,14 @@ typedef struct _Initialize {
|
||||
char dummy_field;
|
||||
} Initialize;
|
||||
|
||||
typedef struct _PairingRequest {
|
||||
char dummy_field;
|
||||
} PairingRequest;
|
||||
|
||||
typedef struct _RepairRequest {
|
||||
char dummy_field;
|
||||
} RepairRequest;
|
||||
|
||||
typedef struct _UnlockBootloader {
|
||||
char dummy_field;
|
||||
} UnlockBootloader;
|
||||
@ -137,8 +157,8 @@ typedef struct _Success {
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _MessageType_MIN MessageType_MessageType_Initialize
|
||||
#define _MessageType_MAX MessageType_MessageType_UnlockBootloader
|
||||
#define _MessageType_ARRAYSIZE ((MessageType)(MessageType_MessageType_UnlockBootloader+1))
|
||||
#define _MessageType_MAX MessageType_MessageType_ComparisonRequest
|
||||
#define _MessageType_ARRAYSIZE ((MessageType)(MessageType_MessageType_ComparisonRequest+1))
|
||||
|
||||
#define _FailureType_MIN FailureType_Failure_UnexpectedMessage
|
||||
#define _FailureType_MAX FailureType_Failure_ProcessError
|
||||
@ -166,6 +186,10 @@ extern "C" {
|
||||
#define FirmwareRequest_init_default {0, 0}
|
||||
#define FirmwareUpload_init_default {{{NULL}, NULL}, false, {0, {0}}}
|
||||
#define UnlockBootloader_init_default {0}
|
||||
#define PairingRequest_init_default {0}
|
||||
#define AuthKey_init_default {{{NULL}, NULL}}
|
||||
#define RepairRequest_init_default {0}
|
||||
#define ComparisonRequest_init_default {{{NULL}, NULL}}
|
||||
#define Initialize_init_zero {0}
|
||||
#define GetFeatures_init_zero {0}
|
||||
#define Features_init_zero {false, "", 0, 0, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0, false, "", false, 0, false, 0, false, 0, false, "", false, "", false, 0, false, 0, false, 0}
|
||||
@ -178,8 +202,14 @@ extern "C" {
|
||||
#define FirmwareRequest_init_zero {0, 0}
|
||||
#define FirmwareUpload_init_zero {{{NULL}, NULL}, false, {0, {0}}}
|
||||
#define UnlockBootloader_init_zero {0}
|
||||
#define PairingRequest_init_zero {0}
|
||||
#define AuthKey_init_zero {{{NULL}, NULL}}
|
||||
#define RepairRequest_init_zero {0}
|
||||
#define ComparisonRequest_init_zero {{{NULL}, NULL}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define AuthKey_key_tag 1
|
||||
#define ComparisonRequest_key_tag 1
|
||||
#define ButtonRequest_code_tag 1
|
||||
#define Failure_code_tag 1
|
||||
#define Failure_message_tag 2
|
||||
@ -294,6 +324,26 @@ X(a, STATIC, OPTIONAL, BYTES, hash, 2)
|
||||
#define UnlockBootloader_CALLBACK NULL
|
||||
#define UnlockBootloader_DEFAULT NULL
|
||||
|
||||
#define PairingRequest_FIELDLIST(X, a) \
|
||||
|
||||
#define PairingRequest_CALLBACK NULL
|
||||
#define PairingRequest_DEFAULT NULL
|
||||
|
||||
#define AuthKey_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, REQUIRED, BYTES, key, 1)
|
||||
#define AuthKey_CALLBACK pb_default_field_callback
|
||||
#define AuthKey_DEFAULT NULL
|
||||
|
||||
#define RepairRequest_FIELDLIST(X, a) \
|
||||
|
||||
#define RepairRequest_CALLBACK NULL
|
||||
#define RepairRequest_DEFAULT NULL
|
||||
|
||||
#define ComparisonRequest_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, REQUIRED, BYTES, key, 1)
|
||||
#define ComparisonRequest_CALLBACK pb_default_field_callback
|
||||
#define ComparisonRequest_DEFAULT NULL
|
||||
|
||||
extern const pb_msgdesc_t Initialize_msg;
|
||||
extern const pb_msgdesc_t GetFeatures_msg;
|
||||
extern const pb_msgdesc_t Features_msg;
|
||||
@ -306,6 +356,10 @@ extern const pb_msgdesc_t FirmwareErase_msg;
|
||||
extern const pb_msgdesc_t FirmwareRequest_msg;
|
||||
extern const pb_msgdesc_t FirmwareUpload_msg;
|
||||
extern const pb_msgdesc_t UnlockBootloader_msg;
|
||||
extern const pb_msgdesc_t PairingRequest_msg;
|
||||
extern const pb_msgdesc_t AuthKey_msg;
|
||||
extern const pb_msgdesc_t RepairRequest_msg;
|
||||
extern const pb_msgdesc_t ComparisonRequest_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define Initialize_fields &Initialize_msg
|
||||
@ -320,9 +374,15 @@ extern const pb_msgdesc_t UnlockBootloader_msg;
|
||||
#define FirmwareRequest_fields &FirmwareRequest_msg
|
||||
#define FirmwareUpload_fields &FirmwareUpload_msg
|
||||
#define UnlockBootloader_fields &UnlockBootloader_msg
|
||||
#define PairingRequest_fields &PairingRequest_msg
|
||||
#define AuthKey_fields &AuthKey_msg
|
||||
#define RepairRequest_fields &RepairRequest_msg
|
||||
#define ComparisonRequest_fields &ComparisonRequest_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* FirmwareUpload_size depends on runtime parameters */
|
||||
/* AuthKey_size depends on runtime parameters */
|
||||
/* ComparisonRequest_size depends on runtime parameters */
|
||||
#define ButtonAck_size 0
|
||||
#define ButtonRequest_size 2
|
||||
#define Failure_size 260
|
||||
@ -331,7 +391,9 @@ extern const pb_msgdesc_t UnlockBootloader_msg;
|
||||
#define FirmwareRequest_size 12
|
||||
#define GetFeatures_size 0
|
||||
#define Initialize_size 0
|
||||
#define PairingRequest_size 0
|
||||
#define Ping_size 258
|
||||
#define RepairRequest_size 0
|
||||
#define Success_size 258
|
||||
#define UnlockBootloader_size 0
|
||||
|
||||
|
@ -18,6 +18,10 @@ enum MessageType {
|
||||
MessageType_ButtonAck = 27;
|
||||
MessageType_GetFeatures = 55;
|
||||
MessageType_UnlockBootloader = 96;
|
||||
MessageType_PairingRequest = 8003;
|
||||
MessageType_AuthKey = 8004;
|
||||
MessageType_RepairRequest = 8005;
|
||||
MessageType_ComparisonRequest = 8008;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,3 +159,43 @@ message FirmwareUpload {
|
||||
*/
|
||||
message UnlockBootloader {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request: initiates new pairing request
|
||||
* @start
|
||||
* @next AuthKey
|
||||
* @next Failure
|
||||
*/
|
||||
message PairingRequest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Request: confirms pairing request with auth key entered on the device
|
||||
* @end
|
||||
*/
|
||||
message AuthKey {
|
||||
required bytes key = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request: initiates repairing request
|
||||
* @start
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message RepairRequest {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Request: initiates new pairing request with numeric comparison
|
||||
* @start
|
||||
* @next Success
|
||||
* @next Failure
|
||||
*/
|
||||
message ComparisonRequest {
|
||||
required bytes key = 1;
|
||||
}
|
||||
|
151
core/embed/extmod/modtrezorio/modtrezorio-ble.h
Normal file
151
core/embed/extmod/modtrezorio/modtrezorio-ble.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 "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_int(self, msg: bytes) -> int:
|
||||
/// """
|
||||
/// Sends internal message to NRF.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorio_BLE_write_int(mp_obj_t self, mp_obj_t msg) {
|
||||
mp_buffer_info_t buf = {0};
|
||||
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
|
||||
ble_int_comm_send(buf.buf, buf.len, INTERNAL_MESSAGE);
|
||||
return MP_OBJ_NEW_SMALL_INT(buf.len);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_int_obj,
|
||||
mod_trezorio_BLE_write_int);
|
||||
|
||||
/// def write_ext(self, msg: bytes) -> int:
|
||||
/// """
|
||||
/// Sends message over BLE
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorio_BLE_write_ext(mp_obj_t self, mp_obj_t msg) {
|
||||
mp_buffer_info_t buf = {0};
|
||||
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
|
||||
ble_int_comm_send(buf.buf, buf.len, EXTERNAL_MESSAGE);
|
||||
return MP_OBJ_NEW_SMALL_INT(buf.len);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_ext_obj,
|
||||
mod_trezorio_BLE_write_ext);
|
||||
|
||||
/// def erase_bonds() -> None:
|
||||
/// """
|
||||
/// Erases all BLE bonds
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorio_BLE_erase_bonds(void) {
|
||||
bool result = send_erase_bonds();
|
||||
if (result) {
|
||||
return mp_const_none;
|
||||
} else {
|
||||
mp_raise_msg(&mp_type_RuntimeError, "Erase bonds failed.");
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorio_BLE_erase_bonds_obj,
|
||||
mod_trezorio_BLE_erase_bonds);
|
||||
|
||||
/// def disconnect() -> None:
|
||||
/// """
|
||||
/// Disconnect BLE
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorio_BLE_disconnect(void) {
|
||||
bool result = send_disconnect();
|
||||
if (result) {
|
||||
return mp_const_none;
|
||||
} else {
|
||||
mp_raise_msg(&mp_type_RuntimeError, "Disconnect failed.");
|
||||
}
|
||||
}
|
||||
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_int),
|
||||
MP_ROM_PTR(&mod_trezorio_BLE_write_int_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_write_ext),
|
||||
MP_ROM_PTR(&mod_trezorio_BLE_write_ext_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_erase_bonds),
|
||||
MP_ROM_PTR(&mod_trezorio_BLE_erase_bonds_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};
|
@ -19,15 +19,22 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ble.h"
|
||||
#include "ble/int_comm_defs.h"
|
||||
#include "ble/state.h"
|
||||
#include "button.h"
|
||||
#include "common.h"
|
||||
#include "display.h"
|
||||
#include "embed/extmod/trezorobj.h"
|
||||
|
||||
#define BLE_EVENTS_IFACE (252)
|
||||
#define USB_DATA_IFACE (253)
|
||||
#define INPUT_IFACE (255)
|
||||
#define TOUCH_INPUT_FLAG (0x400000)
|
||||
#define BUTTON_INPUT_FLAG (0x800000)
|
||||
#define USB_RW_IFACE_MAX (15) // 0-15 reserved for USB
|
||||
#define BLE_IFACE_INT (16)
|
||||
#define BLE_IFACE_EXT (17)
|
||||
#define POLL_READ (0x0000)
|
||||
#define POLL_WRITE (0x0100)
|
||||
|
||||
@ -146,35 +153,81 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
|
||||
ret->items[1] = usb_connected ? mp_const_true : mp_const_false;
|
||||
return mp_const_true;
|
||||
}
|
||||
} else if (mode == POLL_READ) {
|
||||
if (sectrue == usb_hid_can_read(iface)) {
|
||||
uint8_t buf[64] = {0};
|
||||
int len = usb_hid_read(iface, buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||
return mp_const_true;
|
||||
}
|
||||
#ifdef USE_BLE
|
||||
else if (iface == BLE_EVENTS_IFACE) {
|
||||
ble_event_poll();
|
||||
uint8_t connected = ble_connected();
|
||||
if (connected != ble_connected_previously) {
|
||||
ble_connected_previously = connected;
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = connected ? mp_const_true : mp_const_false;
|
||||
return mp_const_true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (iface <= USB_RW_IFACE_MAX) {
|
||||
if (mode == POLL_READ) {
|
||||
if (sectrue == usb_hid_can_read(iface)) {
|
||||
uint8_t buf[64] = {0};
|
||||
int len = usb_hid_read(iface, buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||
return mp_const_true;
|
||||
}
|
||||
} else if (sectrue == usb_webusb_can_read(iface)) {
|
||||
uint8_t buf[64] = {0};
|
||||
int len = usb_webusb_read(iface, buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||
return mp_const_true;
|
||||
}
|
||||
}
|
||||
} else if (sectrue == usb_webusb_can_read(iface)) {
|
||||
uint8_t buf[64] = {0};
|
||||
int len = usb_webusb_read(iface, buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
} else if (mode == POLL_WRITE) {
|
||||
if (sectrue == usb_hid_can_write(iface)) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||
ret->items[1] = mp_const_none;
|
||||
return mp_const_true;
|
||||
} else if (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)) {
|
||||
}
|
||||
#ifdef USE_BLE
|
||||
else if (iface == BLE_IFACE_INT) {
|
||||
if (mode == POLL_READ) {
|
||||
uint8_t buf[64] = {0};
|
||||
int len = ble_int_comm_receive(buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||
return mp_const_true;
|
||||
}
|
||||
} else if (mode == POLL_WRITE) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_const_none;
|
||||
return mp_const_true;
|
||||
} else if (sectrue == usb_webusb_can_write(iface)) {
|
||||
}
|
||||
} else if (iface == BLE_IFACE_EXT) {
|
||||
if (mode == POLL_READ) {
|
||||
uint8_t buf[BLE_PACKET_SIZE] = {0};
|
||||
int len = ble_ext_comm_receive(buf, sizeof(buf));
|
||||
if (len > 0) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||
return mp_const_true;
|
||||
}
|
||||
} else if (mode == POLL_WRITE) {
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = mp_const_none;
|
||||
return mp_const_true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mp_hal_ticks_ms() >= deadline) {
|
||||
|
@ -34,6 +34,8 @@
|
||||
|
||||
// Whether USB data pins were connected on last check (USB configured)
|
||||
bool usb_connected_previously = true;
|
||||
uint8_t ble_connected_previously = false;
|
||||
bool ble_last_internal = false;
|
||||
|
||||
#define CHECK_PARAM_RANGE(value, minimum, maximum) \
|
||||
if (value < minimum || value > maximum) { \
|
||||
@ -48,6 +50,9 @@ bool usb_connected_previously = true;
|
||||
#include "modtrezorio-webusb.h"
|
||||
#include "modtrezorio-usb.h"
|
||||
// clang-format on
|
||||
#ifdef USE_BLE
|
||||
#include "modtrezorio-ble.h"
|
||||
#endif
|
||||
#ifdef USE_SBU
|
||||
#include "modtrezorio-sbu.h"
|
||||
#endif
|
||||
@ -57,7 +62,7 @@ bool usb_connected_previously = true;
|
||||
#endif
|
||||
|
||||
/// package: trezorio.__init__
|
||||
/// from . import fatfs, sdcard
|
||||
/// from . import fatfs, sdcard, ble
|
||||
|
||||
/// POLL_READ: int # wait until interface is readable and return read data
|
||||
/// POLL_WRITE: int # wait until interface is writable
|
||||
@ -74,8 +79,9 @@ bool usb_connected_previously = true;
|
||||
/// BUTTON_RIGHT: int # button number of right button
|
||||
|
||||
/// USB_CHECK: int # interface id for check of USB data connection
|
||||
/// BLE_CHECK: int # interface id for check of BLE data connection
|
||||
|
||||
/// 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)},
|
||||
@ -87,6 +93,9 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
|
||||
#ifdef USE_SD_CARD
|
||||
{MP_ROM_QSTR(MP_QSTR_fatfs), MP_ROM_PTR(&mod_trezorio_fatfs_module)},
|
||||
{MP_ROM_QSTR(MP_QSTR_sdcard), MP_ROM_PTR(&mod_trezorio_sdcard_module)},
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
{MP_ROM_QSTR(MP_QSTR_ble), MP_ROM_PTR(&mod_trezorio_BLE_module)},
|
||||
#endif
|
||||
{MP_ROM_QSTR(MP_QSTR_INPUT), MP_ROM_INT(INPUT_IFACE)},
|
||||
#ifdef USE_TOUCH
|
||||
@ -118,6 +127,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_POLL_WRITE), MP_ROM_INT(POLL_WRITE)},
|
||||
|
||||
{MP_ROM_QSTR(MP_QSTR_USB_CHECK), MP_ROM_INT(USB_DATA_IFACE)},
|
||||
{MP_ROM_QSTR(MP_QSTR_BLE_CHECK), MP_ROM_INT(BLE_EVENTS_IFACE)},
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals,
|
||||
|
@ -295,6 +295,7 @@ STATIC mp_obj_str_t mod_trezorutils_full_name_obj = {
|
||||
/// """Minor version."""
|
||||
/// VERSION_PATCH: int
|
||||
/// """Patch version."""
|
||||
/// USE_BLE: bool
|
||||
/// USE_SD_CARD: bool
|
||||
/// """Whether the hardware supports SD card."""
|
||||
/// USE_BACKLIGHT: bool
|
||||
@ -346,6 +347,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_TOUCH
|
||||
{MP_ROM_QSTR(MP_QSTR_USE_TOUCH), mp_const_true},
|
||||
#else
|
||||
|
@ -76,6 +76,11 @@
|
||||
#include "optiga_transport.h"
|
||||
#include "secret.h"
|
||||
#endif
|
||||
#ifdef USE_BLE
|
||||
#include "ble.h"
|
||||
#include "ble/dfu.h"
|
||||
#include "ble/state.h"
|
||||
#endif
|
||||
#include "unit_variant.h"
|
||||
|
||||
#ifdef SYSTEM_VIEW
|
||||
@ -85,7 +90,9 @@
|
||||
#include "rng.h"
|
||||
#include "supervise.h"
|
||||
#ifdef USE_SECP256K1_ZKP
|
||||
#include "ble/messages.h"
|
||||
#include "zkp_context.h"
|
||||
|
||||
#endif
|
||||
|
||||
// from util.s
|
||||
@ -178,6 +185,14 @@ int main(void) {
|
||||
memzero(secret, sizeof(secret));
|
||||
#endif
|
||||
|
||||
#ifdef USE_BLE
|
||||
dfu_init();
|
||||
ble_comm_init();
|
||||
send_state_request();
|
||||
wait_for_answer();
|
||||
ble_start();
|
||||
#endif
|
||||
|
||||
#if !defined TREZOR_MODEL_1
|
||||
drop_privileges();
|
||||
#endif
|
||||
@ -284,6 +299,11 @@ void SVC_C_Handler(uint32_t *stack) {
|
||||
clear_firmware_header = false;
|
||||
// break is omitted here because we want to continue to reboot below
|
||||
case SVC_REBOOT_TO_BOOTLOADER:
|
||||
#ifdef USE_BLE
|
||||
stop_advertising();
|
||||
ble_stop();
|
||||
// TODO: make sure that no answer is pending from NRF
|
||||
#endif
|
||||
// if not going from copy image header & reboot, clean preventively this
|
||||
// part of CCMRAM
|
||||
if (clear_firmware_header) {
|
||||
|
135
core/embed/lib/ble/dfu.c
Normal file
135
core/embed/lib/ble/dfu.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 "common.h"
|
||||
#include TREZOR_BOARD
|
||||
#include "ble.h"
|
||||
#include "common.h"
|
||||
#include "dfu.h"
|
||||
#include "fwu.h"
|
||||
|
||||
static TFwu sFwu;
|
||||
|
||||
static uint32_t tick_start = 0;
|
||||
|
||||
void txFunction(struct SFwu *fwu, uint8_t *buf, uint8_t len);
|
||||
static uint8_t readData(uint8_t *data, int maxLen);
|
||||
|
||||
void dfu_init(void) {}
|
||||
|
||||
dfu_result_t dfu_update_process(void) {
|
||||
while (1) {
|
||||
// Can send 4 chars...
|
||||
// (On a microcontroller, you'd use the TX Empty interrupt or test a
|
||||
// register.)
|
||||
|
||||
fwuCanSendData(&sFwu, 4);
|
||||
|
||||
// Data available? Get up to 4 bytes...
|
||||
// (On a microcontroller, you'd use the RX Available interrupt or test a
|
||||
// register.)
|
||||
uint8_t rxBuf[4];
|
||||
uint8_t rxLen = readData(rxBuf, 4);
|
||||
if (rxLen > 0) {
|
||||
fwuDidReceiveData(&sFwu, rxBuf, rxLen);
|
||||
}
|
||||
|
||||
// Give the firmware update module a timeslot to continue the process.
|
||||
EFwuProcessStatus status = fwuYield(&sFwu, 0);
|
||||
|
||||
if (status == FWU_STATUS_COMPLETION) {
|
||||
ble_reset();
|
||||
return DFU_SUCCESS;
|
||||
}
|
||||
|
||||
if (status == FWU_STATUS_FAILURE) {
|
||||
return DFU_FAIL;
|
||||
}
|
||||
|
||||
if (hal_ticks_ms() - tick_start > 2000) {
|
||||
return DFU_FAIL;
|
||||
}
|
||||
|
||||
if (fwuIsReadyForChunk(&sFwu)) {
|
||||
return DFU_NEXT_CHUNK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dfu_result_t dfu_update_init(uint8_t *data, uint32_t len, uint32_t binary_len) {
|
||||
sFwu.commandObject = data;
|
||||
sFwu.commandObjectLen = len;
|
||||
sFwu.dataObject = NULL;
|
||||
sFwu.dataObjectLen = binary_len;
|
||||
sFwu.txFunction = txFunction;
|
||||
sFwu.responseTimeoutMillisec = 2000;
|
||||
|
||||
if (!ble_reset_to_bootloader()) {
|
||||
return DFU_FAIL;
|
||||
}
|
||||
|
||||
tick_start = hal_ticks_ms();
|
||||
|
||||
// Prepare the firmware update process.
|
||||
fwuInit(&sFwu);
|
||||
|
||||
// Start the firmware update process.
|
||||
fwuExec(&sFwu);
|
||||
|
||||
return dfu_update_process();
|
||||
}
|
||||
|
||||
dfu_result_t dfu_update_chunk(uint8_t *data, uint32_t len) {
|
||||
tick_start = hal_ticks_ms();
|
||||
|
||||
fwuSendChunk(&sFwu, data, len);
|
||||
|
||||
return dfu_update_process();
|
||||
}
|
||||
|
||||
dfu_result_t dfu_update_do(uint8_t *datfile, uint32_t datfile_len,
|
||||
uint8_t *binfile, uint32_t binfile_len) {
|
||||
uint32_t chunk_offset = 0;
|
||||
uint32_t rem_data = binfile_len;
|
||||
|
||||
dfu_result_t res = dfu_update_init(datfile, datfile_len, binfile_len);
|
||||
|
||||
while (res == DFU_NEXT_CHUNK) {
|
||||
// Send the next chunk of the data object.
|
||||
uint32_t chunk_size = 4096;
|
||||
if (rem_data < 4096) {
|
||||
chunk_size = rem_data;
|
||||
rem_data = 0;
|
||||
} else {
|
||||
rem_data -= 4096;
|
||||
}
|
||||
res = dfu_update_chunk(&binfile[chunk_offset], chunk_size);
|
||||
chunk_offset += chunk_size;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void txFunction(struct SFwu *fwu, uint8_t *buf, uint8_t len) {
|
||||
ble_comm_send(buf, len);
|
||||
}
|
||||
|
||||
static uint8_t readData(uint8_t *data, int maxLen) {
|
||||
return ble_comm_receive(data, maxLen);
|
||||
}
|
17
core/embed/lib/ble/dfu.h
Normal file
17
core/embed/lib/ble/dfu.h
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
#ifndef __DFU_H__
|
||||
#define __DFU_H__
|
||||
|
||||
typedef enum {
|
||||
DFU_NEXT_CHUNK,
|
||||
DFU_SUCCESS,
|
||||
DFU_FAIL,
|
||||
} dfu_result_t;
|
||||
|
||||
void dfu_init(void);
|
||||
dfu_result_t dfu_update_init(uint8_t *data, uint32_t len, uint32_t binary_len);
|
||||
dfu_result_t dfu_update_chunk(uint8_t *data, uint32_t len);
|
||||
dfu_result_t dfu_update_do(uint8_t *datfile, uint32_t datfile_len,
|
||||
uint8_t *binfile, uint32_t binfile_len);
|
||||
|
||||
#endif
|
664
core/embed/lib/ble/fwu.c
Normal file
664
core/embed/lib/ble/fwu.c
Normal file
@ -0,0 +1,664 @@
|
||||
//
|
||||
// fwu.c
|
||||
// nrf52-dfu
|
||||
//
|
||||
// C library for the Nordic firmware update protocol.
|
||||
//
|
||||
// Created by Andreas Schweizer on 30.11.2018.
|
||||
// Copyright © 2018-2019 Classy Code GmbH
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#include "fwu.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// TODO too big, split in separate files!
|
||||
|
||||
typedef enum {
|
||||
FWU_PS_IDLE = 0,
|
||||
FWU_PS_PING = 10,
|
||||
FWU_PS_RCPT_NOTIF = 20,
|
||||
FWU_PS_MTU = 30,
|
||||
FWU_PS_OBJ1_SELECT = 40,
|
||||
FWU_PS_OBJ1_CREATE = 50,
|
||||
FWU_PS_OBJ1_WRITE = 60,
|
||||
FWU_PS_OBJ1_CRC_GET = 70,
|
||||
FWU_PS_OBJ1_EXECUTE = 80,
|
||||
FWU_PS_OBJ2_SELECT = 90,
|
||||
FWU_PS_OBJ2_WAIT_FOR_CHUNK = 91,
|
||||
FWU_PS_OBJ2_CREATE = 100,
|
||||
FWU_PS_OBJ2_WRITE = 110,
|
||||
FWU_PS_OBJ2_CRC_GET = 120,
|
||||
FWU_PS_OBJ2_EXECUTE = 130,
|
||||
FWU_PS_FAIL = 254,
|
||||
FWU_PS_DONE = 255,
|
||||
} EFwuProcessState;
|
||||
|
||||
// Process requests, triggering process state transitions.
|
||||
typedef enum {
|
||||
FWU_PR_NONE = 0,
|
||||
FWU_PR_START = 1,
|
||||
FWU_PR_RECEIVED_RESPONSE,
|
||||
FWU_PR_REQUEST_FAILED,
|
||||
FWU_PR_REQUEST_SENT,
|
||||
} EFwuProcessRequest;
|
||||
|
||||
typedef enum {
|
||||
FWU_CS_IDLE = 0,
|
||||
FWU_CS_SEND = 1, // sending data from the private request buffer
|
||||
FWU_CS_RECEIVE = 2, // receiving data into the private response buffer
|
||||
FWU_CS_FAIL = 3,
|
||||
FWU_CS_DONE = 4,
|
||||
} EFwuCommandState;
|
||||
|
||||
// Command requests, triggering command state transitions.
|
||||
typedef enum {
|
||||
FWU_CR_NONE = 0,
|
||||
FWU_CR_SEND = 1,
|
||||
FWU_CR_SENDONLY = 2,
|
||||
FWU_CR_EOM_RECEIVED = 3,
|
||||
FWU_CR_RX_OVERFLOW = 4,
|
||||
FWU_CR_INVALID_ESCAPE_SEQ,
|
||||
} EFwuCommandRequest;
|
||||
|
||||
#define FWU_EOM 0xC0
|
||||
#define FWU_RESPONSE_START 0x60
|
||||
#define FWU_RESPONSE_SUCCESS 0x01
|
||||
|
||||
// PING 09 01 C0 -> 60 09 01 01 C0
|
||||
static uint8_t sPingRequest[] = {0x09, 0x01};
|
||||
static uint8_t sPingRequestLen = 2;
|
||||
|
||||
// SET RECEIPT 02 00 00 C0 -> 60 02 01 C0
|
||||
static uint8_t sSetReceiptRequest[] = {0x02, 0x00, 0x00};
|
||||
static uint8_t sSetReceiptRequestLen = 3;
|
||||
|
||||
// Get the preferred MTU size on the request.
|
||||
// GET MTU 07 -> 60 07 01 83 00 C0
|
||||
static uint8_t sGetMtuRequest[] = {0x07};
|
||||
static uint8_t sGetMtuRequestLen = 1;
|
||||
|
||||
// Triggers the last transferred object of the specified type to be selected
|
||||
// and queries information (max size, cur offset, cur CRC) about the object.
|
||||
// If there's no object of the specified type, the object type is still
|
||||
// selected, CRC and offset are 0 in this case.
|
||||
// SELECT OBJECT 06 01 C0 -> 60 06 01 00 01 00 00 00 00 00 00 00 00 00 00 C0
|
||||
static uint8_t sSelectObjectRequest[] = {0x06, 0x01};
|
||||
static uint8_t sSelectObjectRequestLen = 2;
|
||||
|
||||
// Creating a command or data object; the target reserves the space, resets the
|
||||
// progress since the last Execute command and selects the new object.)
|
||||
// CREATE OBJECT 01 01 87 00 00 00 C0 -> 60 01 01 C0
|
||||
static uint8_t sCreateObjectRequest[] = {0x01, 0x01, 0x87, 0x00, 0x00, 0x00};
|
||||
static uint8_t sCreateObjectRequestLen = 6;
|
||||
|
||||
// CRC GET 03 C0 -> 60 03 01 87 00 00 00 38 f4 97 72 C0
|
||||
static uint8_t sGetCrcRequest[] = {0x03};
|
||||
static uint8_t sGetCrcRequestLen = 1;
|
||||
|
||||
// Execute an object after it has been fully transmitted.
|
||||
// EXECUTE OBJECT 04 C0 -> 60 04 01 C0
|
||||
static uint8_t sExecuteObjectRequest[] = {0x04};
|
||||
static uint8_t sExecuteObjectRequestLen = 1;
|
||||
|
||||
static void fwuYieldProcessFsm(TFwu *fwu, uint32_t elapsedMillisec);
|
||||
static void fwuYieldCommandFsm(TFwu *fwu, uint32_t elapsedMillisec);
|
||||
|
||||
static EFwuResponseStatus fwuTestReceivedPacketValid(TFwu *fwu);
|
||||
|
||||
// Don't send more than FWU_REQUEST_BUF_SIZE bytes.
|
||||
// Don't include the EOM.
|
||||
static void fwuPrepareSendBuffer(TFwu *fwu, uint8_t *data, uint8_t len);
|
||||
|
||||
static void fwuPrepareLargeObjectSendBuffer(TFwu *fwu, uint8_t requestCode);
|
||||
|
||||
// static void fwuDebugPrintStatus(TFwu *fwu, char *msg);
|
||||
|
||||
static void updateCrc(TFwu *fwu, uint8_t b);
|
||||
static void fwuSignalFailure(TFwu *fwu, EFwuResponseStatus reason);
|
||||
static inline uint16_t fwuLittleEndianToHost16(uint8_t *bytes);
|
||||
static inline uint32_t fwuLittleEndianToHost32(uint8_t *bytes);
|
||||
static inline void fwuHostToLittleEndian32(uint32_t v, uint8_t *bytes);
|
||||
|
||||
// First function to call to set up the internal state in the FWU structure.
|
||||
void fwuInit(TFwu *fwu) {
|
||||
fwu->privateProcessState = FWU_PS_IDLE;
|
||||
fwu->privateProcessRequest = FWU_PR_NONE;
|
||||
fwu->privateCommandState = FWU_CS_IDLE;
|
||||
|
||||
fwu->processStatus = FWU_STATUS_UNDEFINED;
|
||||
fwu->responseStatus = FWU_RSP_OK;
|
||||
}
|
||||
|
||||
// Execute the firmware update.
|
||||
void fwuExec(TFwu *fwu) {
|
||||
// Start with sending a PING command to the target to see if it's there...
|
||||
fwu->privateProcessRequest = FWU_PR_START;
|
||||
}
|
||||
|
||||
// Call regularly to allow asynchronous processing to continue.
|
||||
EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec) {
|
||||
// Nothing to do if processing has failed or successfully completed...
|
||||
if (fwu->processStatus == FWU_STATUS_FAILURE ||
|
||||
fwu->privateProcessState == FWU_PS_FAIL) {
|
||||
return FWU_STATUS_FAILURE;
|
||||
} else if (fwu->processStatus == FWU_STATUS_COMPLETION ||
|
||||
fwu->privateProcessState == FWU_PS_DONE) {
|
||||
return FWU_STATUS_COMPLETION;
|
||||
}
|
||||
|
||||
// Processing is ongoing, yield to FSMs.
|
||||
fwuYieldCommandFsm(fwu, elapsedMillisec);
|
||||
fwuYieldProcessFsm(fwu, elapsedMillisec);
|
||||
|
||||
return fwu->processStatus;
|
||||
}
|
||||
|
||||
// Call after data from the target has been received.
|
||||
void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len) {
|
||||
while (len > 0) {
|
||||
if (fwu->privateResponseLen == FWU_RESPONSE_BUF_SIZE) {
|
||||
fwu->privateCommandRequest = FWU_CR_RX_OVERFLOW;
|
||||
return;
|
||||
}
|
||||
uint8_t c = *bytes++;
|
||||
if (c == FWU_EOM) {
|
||||
fwu->privateCommandRequest = FWU_CR_EOM_RECEIVED;
|
||||
}
|
||||
|
||||
if (c == 0xDB) {
|
||||
fwu->privateResponseEscapeCharacter = 1;
|
||||
} else {
|
||||
if (fwu->privateResponseEscapeCharacter) {
|
||||
fwu->privateResponseEscapeCharacter = 0;
|
||||
if (c == 0xDC) {
|
||||
c = 0xC0;
|
||||
} else if (c == 0xDD) {
|
||||
c = 0xDB;
|
||||
} else {
|
||||
fwu->privateCommandRequest = FWU_CR_INVALID_ESCAPE_SEQ;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fwu->privateResponseBuf[fwu->privateResponseLen++] = c;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
// Inform the FWU module that it may send maxLen bytes of data to the target.
|
||||
void fwuCanSendData(TFwu *fwu, uint8_t maxLen) {
|
||||
fwu->privateSendBufSpace = maxLen;
|
||||
}
|
||||
|
||||
void fwuSendChunk(TFwu *fwu, uint8_t *buf, uint32_t len) {
|
||||
if (fwu->privateProcessState == FWU_PS_OBJ2_WAIT_FOR_CHUNK &&
|
||||
fwu->privateDataObjectSize == len) {
|
||||
fwu->dataObject = buf;
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
bool fwuIsReadyForChunk(TFwu *fwu) {
|
||||
return fwu->privateProcessState == FWU_PS_OBJ2_WAIT_FOR_CHUNK;
|
||||
}
|
||||
|
||||
static void fwuYieldProcessFsm(TFwu *fwu, uint32_t elapsedMillisec) {
|
||||
uint8_t tmpPrivateProcessRequest = fwu->privateProcessRequest;
|
||||
fwu->privateProcessRequest = FWU_PR_NONE;
|
||||
|
||||
// No processing in final states
|
||||
if (fwu->privateProcessState == FWU_PS_DONE ||
|
||||
fwu->privateProcessState == FWU_PS_FAIL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Failure handling
|
||||
if (tmpPrivateProcessRequest == FWU_PR_REQUEST_FAILED) {
|
||||
fwu->privateProcessState = FWU_PS_FAIL;
|
||||
fwu->processStatus = FWU_STATUS_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Executing the firmware update process.
|
||||
switch (fwu->privateProcessState) {
|
||||
case FWU_PS_IDLE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_START) {
|
||||
// Send a PING and switch to the PING state to wait for the response.
|
||||
fwuPrepareSendBuffer(fwu, sPingRequest, sPingRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_PING;
|
||||
}
|
||||
break;
|
||||
|
||||
// PING: Check if the nRF52 DFU code is listening
|
||||
case FWU_PS_PING:
|
||||
// Wait for the PING response, then verify it.
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
// ID match?
|
||||
if (fwu->privateRequestBuf[1] == fwu->privateResponseBuf[3]) {
|
||||
// Send a SET_RECEIPT and switch to the corresponding state to wait
|
||||
// for the response.
|
||||
fwuPrepareSendBuffer(fwu, sSetReceiptRequest, sSetReceiptRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_RCPT_NOTIF;
|
||||
} else {
|
||||
fwuSignalFailure(fwu, FWU_RSP_PING_ID_MISMATCH);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// RCPT_NOTIF: Define Receipt settings
|
||||
case FWU_PS_RCPT_NOTIF:
|
||||
// Wait for the SET_RECEIPT response.
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
// Send a SET_RECEIPT and switch to the corresponding state to wait for
|
||||
// the response.
|
||||
fwuPrepareSendBuffer(fwu, sGetMtuRequest, sGetMtuRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_MTU;
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_MTU: Get maximum transmission unit size
|
||||
case FWU_PS_MTU:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
fwu->privateMtuSize =
|
||||
fwuLittleEndianToHost16(&fwu->privateResponseBuf[3]);
|
||||
// Send a SET_RECEIPT and switch to the corresponding state to wait for
|
||||
// the response.
|
||||
sSelectObjectRequest[1] = 0x01; // select object 1 (command object)
|
||||
fwuPrepareSendBuffer(fwu, sSelectObjectRequest,
|
||||
sSelectObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ1_SELECT;
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ1_SELECT: Select the INIT command object
|
||||
case FWU_PS_OBJ1_SELECT:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
uint32_t maxSize = fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
|
||||
if (maxSize < fwu->commandObjectLen) {
|
||||
fwuSignalFailure(fwu, FWU_RSP_INIT_COMMAND_TOO_LARGE);
|
||||
} else {
|
||||
sCreateObjectRequest[1] = 0x01; // create type 1 object (COMMAND)
|
||||
fwuHostToLittleEndian32(fwu->commandObjectLen,
|
||||
&sCreateObjectRequest[2]);
|
||||
fwuPrepareSendBuffer(fwu, sCreateObjectRequest,
|
||||
sCreateObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ1_CREATE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ1_CREATE: Create the INIT command object
|
||||
case FWU_PS_OBJ1_CREATE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
fwu->privateProcessState = FWU_PS_OBJ1_WRITE;
|
||||
fwu->privateObjectBuf = fwu->commandObject;
|
||||
fwu->privateObjectLen = fwu->commandObjectLen;
|
||||
fwu->privateObjectIx = 0;
|
||||
fwu->privateObjectCrc = 0xffffffff;
|
||||
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ1_WRITE: Write the INIT command object
|
||||
case FWU_PS_OBJ1_WRITE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_REQUEST_SENT) {
|
||||
// more to send?
|
||||
if (fwu->privateObjectIx == fwu->privateObjectLen) {
|
||||
// no - request the CRC of the written data...
|
||||
fwuPrepareSendBuffer(fwu, sGetCrcRequest, sGetCrcRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ1_CRC_GET;
|
||||
} else {
|
||||
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ1_CRC_GET: Checksum verification
|
||||
case FWU_PS_OBJ1_CRC_GET:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
// uint32_t actualLen =
|
||||
// fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
|
||||
uint32_t actualCks =
|
||||
fwuLittleEndianToHost32(&fwu->privateResponseBuf[7]);
|
||||
if (actualCks == ~fwu->privateObjectCrc) {
|
||||
// Checksum is OK; execute the command!
|
||||
fwuPrepareSendBuffer(fwu, sExecuteObjectRequest,
|
||||
sExecuteObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ1_EXECUTE;
|
||||
} else {
|
||||
fwuSignalFailure(fwu, FWU_RSP_CHECKSUM_ERROR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FWU_PS_OBJ1_EXECUTE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
sSelectObjectRequest[1] = 0x02; // select object 2 (DATA object)
|
||||
fwu->privateDataObjectOffset = 0; // from the beginning
|
||||
fwuPrepareSendBuffer(fwu, sSelectObjectRequest,
|
||||
sSelectObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_SELECT;
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ2_SELECT: Select the DATA object
|
||||
case FWU_PS_OBJ2_SELECT:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
fwu->privateDataObjectMaxSize =
|
||||
fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
|
||||
fwu->privateObjectCrc =
|
||||
0xffffffff; // do it here because it's global for the entire blob
|
||||
// We'll create and execute multiple data objects, so it's ok if the
|
||||
// actual size is greater than max size.
|
||||
fwu->privateDataObjectSize =
|
||||
(fwu->dataObjectLen -
|
||||
fwu->privateDataObjectOffset); // nof bytes remaining
|
||||
if (fwu->privateDataObjectSize > fwu->privateDataObjectMaxSize) {
|
||||
fwu->privateDataObjectSize = fwu->privateDataObjectMaxSize;
|
||||
}
|
||||
sCreateObjectRequest[1] = 0x02; // create type 2 object (COMMAND)
|
||||
fwuHostToLittleEndian32(fwu->privateDataObjectSize,
|
||||
&sCreateObjectRequest[2]);
|
||||
fwuPrepareSendBuffer(fwu, sCreateObjectRequest,
|
||||
sCreateObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_WAIT_FOR_CHUNK;
|
||||
}
|
||||
break;
|
||||
case FWU_PS_OBJ2_WAIT_FOR_CHUNK:
|
||||
break;
|
||||
// FWU_PS_OBJ2_CREATE: Create the DATA object
|
||||
case FWU_PS_OBJ2_CREATE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_WRITE;
|
||||
fwu->privateObjectBuf = fwu->dataObject;
|
||||
fwu->privateObjectLen = fwu->privateDataObjectSize;
|
||||
fwu->privateObjectIx = 0;
|
||||
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ2_WRITE: Write the DATA object
|
||||
case FWU_PS_OBJ2_WRITE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_REQUEST_SENT) {
|
||||
// more to send?
|
||||
if (fwu->privateObjectIx == fwu->privateObjectLen) {
|
||||
// no - request the CRC of the written data...
|
||||
fwuPrepareSendBuffer(fwu, sGetCrcRequest, sGetCrcRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_CRC_GET;
|
||||
} else {
|
||||
fwuPrepareLargeObjectSendBuffer(fwu, 0x08);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// FWU_PS_OBJ2_CRC_GET: Checksum verification
|
||||
case FWU_PS_OBJ2_CRC_GET:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
// uint32_t actualLen =
|
||||
// fwuLittleEndianToHost32(&fwu->privateResponseBuf[3]);
|
||||
uint32_t actualCks =
|
||||
fwuLittleEndianToHost32(&fwu->privateResponseBuf[7]);
|
||||
if (actualCks == ~fwu->privateObjectCrc) {
|
||||
// Checksum is OK; execute the command!
|
||||
fwuPrepareSendBuffer(fwu, sExecuteObjectRequest,
|
||||
sExecuteObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_EXECUTE;
|
||||
|
||||
} else {
|
||||
fwuSignalFailure(fwu, FWU_RSP_CHECKSUM_ERROR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FWU_PS_OBJ2_EXECUTE:
|
||||
if (tmpPrivateProcessRequest == FWU_PR_RECEIVED_RESPONSE) {
|
||||
fwu->privateDataObjectOffset += fwu->privateDataObjectSize;
|
||||
if (fwu->privateDataObjectOffset == fwu->dataObjectLen) {
|
||||
fwu->privateProcessState = FWU_PS_DONE;
|
||||
fwu->processStatus = FWU_STATUS_COMPLETION;
|
||||
|
||||
} else {
|
||||
// We'll create and execute multiple data objects, so it's ok if the
|
||||
// actual size is greater than max size.
|
||||
fwu->privateDataObjectSize =
|
||||
(fwu->dataObjectLen -
|
||||
fwu->privateDataObjectOffset); // nof bytes remaining
|
||||
if (fwu->privateDataObjectSize > fwu->privateDataObjectMaxSize) {
|
||||
fwu->privateDataObjectSize = fwu->privateDataObjectMaxSize;
|
||||
}
|
||||
sCreateObjectRequest[1] = 0x02; // create type 2 object (COMMAND)
|
||||
fwuHostToLittleEndian32(fwu->privateDataObjectSize,
|
||||
&sCreateObjectRequest[2]);
|
||||
fwuPrepareSendBuffer(fwu, sCreateObjectRequest,
|
||||
sCreateObjectRequestLen);
|
||||
fwu->privateProcessState = FWU_PS_OBJ2_WAIT_FOR_CHUNK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fwu->privateProcessState = FWU_PS_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void fwuYieldCommandFsm(TFwu *fwu, uint32_t elapsedMillisec) {
|
||||
uint8_t toSend;
|
||||
|
||||
// Automatically return from final states to IDLE.
|
||||
if (fwu->privateCommandState == FWU_CS_DONE ||
|
||||
fwu->privateCommandState == FWU_CS_FAIL) {
|
||||
fwu->privateCommandState = FWU_CS_IDLE;
|
||||
}
|
||||
|
||||
// Timeout?
|
||||
if (fwu->privateCommandState != FWU_CS_IDLE) {
|
||||
if (fwu->privateCommandTimeoutRemainingMillisec < elapsedMillisec) {
|
||||
fwu->privateCommandTimeoutRemainingMillisec = 0;
|
||||
} else {
|
||||
fwu->privateCommandTimeoutRemainingMillisec -= elapsedMillisec;
|
||||
}
|
||||
if (fwu->privateCommandTimeoutRemainingMillisec == 0) {
|
||||
fwuSignalFailure(fwu, FWU_RSP_TIMEOUT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Catch errors
|
||||
if (fwu->privateCommandRequest == FWU_CR_RX_OVERFLOW) {
|
||||
fwuSignalFailure(fwu, FWU_RSP_RX_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
if (fwu->privateCommandRequest == FWU_CR_INVALID_ESCAPE_SEQ) {
|
||||
fwuSignalFailure(fwu, FWU_RSP_RX_INVALID_ESCAPE_SEQ);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (fwu->privateCommandState) {
|
||||
case FWU_CS_IDLE:
|
||||
// Ready and waiting for a transmission request.
|
||||
if (fwu->privateCommandRequest == FWU_CR_SEND ||
|
||||
fwu->privateCommandRequest == FWU_CR_SENDONLY) {
|
||||
fwu->privateCommandSendOnly =
|
||||
fwu->privateCommandRequest == FWU_CR_SENDONLY ? 1 : 0;
|
||||
fwu->privateCommandRequest = FWU_CR_NONE;
|
||||
fwu->privateCommandState = FWU_CS_SEND;
|
||||
fwu->privateCommandTimeoutRemainingMillisec =
|
||||
fwu->responseTimeoutMillisec;
|
||||
}
|
||||
break;
|
||||
case FWU_CS_SEND:
|
||||
// Continue sending data until the entire request has been sent.
|
||||
toSend = fwu->privateRequestLen - fwu->privateRequestIx;
|
||||
if (toSend == 0) {
|
||||
if (fwu->privateCommandSendOnly) {
|
||||
// This was a fire-and-forget request; we don't expect a response.
|
||||
fwu->privateProcessRequest = FWU_PR_REQUEST_SENT;
|
||||
fwu->privateCommandState = FWU_CS_DONE;
|
||||
} else {
|
||||
// The request has been sent; wait for response.
|
||||
fwu->privateCommandState = FWU_CS_RECEIVE;
|
||||
}
|
||||
} else if (fwu->privateSendBufSpace > 0) {
|
||||
uint8_t n = fwu->privateSendBufSpace;
|
||||
if (n > toSend) {
|
||||
n = toSend;
|
||||
}
|
||||
fwu->txFunction(fwu, &fwu->privateRequestBuf[fwu->privateRequestIx], n);
|
||||
fwu->privateRequestIx += n;
|
||||
}
|
||||
break;
|
||||
case FWU_CS_RECEIVE:
|
||||
// Continue receiving data until the end-of-message marker has been
|
||||
// received.
|
||||
if (fwu->privateCommandRequest == FWU_CR_EOM_RECEIVED) {
|
||||
fwu->privateCommandRequest = FWU_CR_NONE;
|
||||
EFwuResponseStatus responseStatus = fwuTestReceivedPacketValid(fwu);
|
||||
if (responseStatus == FWU_RSP_OK) {
|
||||
// Inform the process state machine that command reception has
|
||||
// completed.
|
||||
fwu->privateProcessRequest = FWU_PR_RECEIVED_RESPONSE;
|
||||
fwu->privateCommandState = FWU_CS_DONE;
|
||||
} else {
|
||||
fwu->responseStatus = responseStatus;
|
||||
fwu->privateCommandState = FWU_CS_FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fwu->privateCommandState = FWU_CS_FAIL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static EFwuResponseStatus fwuTestReceivedPacketValid(TFwu *fwu) {
|
||||
// 60 <cmd> <ok> C0
|
||||
if (fwu->privateResponseLen < 4) {
|
||||
return FWU_RSP_TOO_SHORT;
|
||||
}
|
||||
if (fwu->privateResponseBuf[0] != FWU_RESPONSE_START) {
|
||||
return FWU_RSP_START_MARKER_MISSING;
|
||||
}
|
||||
if (fwu->privateResponseBuf[1] != fwu->privateRequestBuf[0]) {
|
||||
return FWU_RSP_REQUEST_REFERENCE_INVALID;
|
||||
}
|
||||
if (fwu->privateResponseBuf[2] != FWU_RESPONSE_SUCCESS) {
|
||||
return FWU_RSP_ERROR_RESPONSE;
|
||||
}
|
||||
if (fwu->privateResponseBuf[fwu->privateResponseLen - 1] != FWU_EOM) {
|
||||
return FWU_RSP_END_MARKER_MISSING;
|
||||
}
|
||||
return FWU_RSP_OK;
|
||||
}
|
||||
|
||||
static void fwuPrepareLargeObjectSendBuffer(TFwu *fwu, uint8_t requestCode) {
|
||||
uint16_t bytesTodo = fwu->privateObjectLen - fwu->privateObjectIx;
|
||||
uint16_t bufSpace = FWU_REQUEST_BUF_SIZE - 2;
|
||||
|
||||
uint16_t i;
|
||||
uint8_t *p = &fwu->privateRequestBuf[0];
|
||||
|
||||
*p++ = requestCode;
|
||||
fwu->privateRequestLen = 2; // including requestCode and FWU_EOM
|
||||
fwu->privateRequestIx = 0;
|
||||
|
||||
if (bytesTodo > 32) {
|
||||
bytesTodo = 32;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytesTodo && bufSpace >= 2; i++) {
|
||||
uint8_t b = fwu->privateObjectBuf[fwu->privateObjectIx];
|
||||
// SLIP escape characters: C0->DBDC, DB->DBDD
|
||||
if (b == 0xC0 || b == 0xDB) {
|
||||
*p++ = 0xDB;
|
||||
*p++ = (b == 0xC0) ? 0xDC : 0xDD;
|
||||
fwu->privateRequestLen += 2;
|
||||
bufSpace -= 2;
|
||||
} else {
|
||||
*p++ = b;
|
||||
fwu->privateRequestLen++;
|
||||
bufSpace--;
|
||||
}
|
||||
updateCrc(fwu, b);
|
||||
fwu->privateObjectIx++;
|
||||
}
|
||||
|
||||
*p = FWU_EOM;
|
||||
fwu->privateCommandRequest = FWU_CR_SENDONLY;
|
||||
}
|
||||
|
||||
static void fwuPrepareSendBuffer(TFwu *fwu, uint8_t *data, uint8_t len) {
|
||||
// TODO assert privateCommandState == FWU_CS_IDLE | _DONE | _FAIL
|
||||
// TODO assert len <= FWU_REQUEST_BUF_SIZE
|
||||
|
||||
uint8_t i;
|
||||
uint8_t *p = &fwu->privateRequestBuf[0];
|
||||
|
||||
fwu->privateRequestIx = 0;
|
||||
fwu->privateRequestLen = len + 1;
|
||||
fwu->privateResponseLen = 0;
|
||||
|
||||
// Copy the data into our internal buffer.
|
||||
for (i = 0; i < len; i++) {
|
||||
*p++ = *data++;
|
||||
}
|
||||
|
||||
// Add the end-of-message marker.
|
||||
*p = FWU_EOM;
|
||||
|
||||
// Ready to send!
|
||||
fwu->privateCommandRequest = FWU_CR_SEND;
|
||||
}
|
||||
|
||||
static void updateCrc(TFwu *fwu, uint8_t b) {
|
||||
uint8_t i;
|
||||
uint32_t crc = fwu->privateObjectCrc;
|
||||
crc ^= b;
|
||||
for (i = 0; i < 8; i++) {
|
||||
uint32_t m = (crc & 1) ? 0xffffffff : 0;
|
||||
crc = (crc >> 1) ^ (0xedb88320u & m);
|
||||
}
|
||||
fwu->privateObjectCrc = crc;
|
||||
}
|
||||
|
||||
static void fwuSignalFailure(TFwu *fwu, EFwuResponseStatus reason) {
|
||||
fwu->responseStatus = reason;
|
||||
fwu->privateCommandState = FWU_CS_FAIL;
|
||||
// Signal failure to process state machine
|
||||
fwu->privateProcessRequest = FWU_PR_REQUEST_FAILED;
|
||||
}
|
||||
|
||||
static inline uint16_t fwuLittleEndianToHost16(uint8_t *bytes) {
|
||||
return bytes[0] | ((uint16_t)bytes[1] << 8);
|
||||
}
|
||||
|
||||
static inline uint32_t fwuLittleEndianToHost32(uint8_t *bytes) {
|
||||
return bytes[0] | ((uint16_t)bytes[1] << 8) | ((uint32_t)bytes[2] << 16) |
|
||||
((uint32_t)bytes[3] << 24);
|
||||
}
|
||||
|
||||
static inline void fwuHostToLittleEndian32(uint32_t v, uint8_t *bytes) {
|
||||
uint8_t i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
*bytes++ = v & 0xff;
|
||||
v = v >> 8;
|
||||
}
|
||||
}
|
128
core/embed/lib/ble/fwu.h
Normal file
128
core/embed/lib/ble/fwu.h
Normal file
@ -0,0 +1,128 @@
|
||||
//
|
||||
// fwu.h
|
||||
// nrf52-dfu
|
||||
//
|
||||
// C library for the Nordic firmware update protocol.
|
||||
//
|
||||
// Created by Andreas Schweizer on 30.11.2018.
|
||||
// Copyright © 2018-2019 Classy Code GmbH
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#ifndef __FWU_H__
|
||||
#define __FWU_H__ 1
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct SFwu;
|
||||
|
||||
#define FWU_REQUEST_BUF_SIZE 67
|
||||
#define FWU_RESPONSE_BUF_SIZE 16
|
||||
|
||||
typedef enum {
|
||||
FWU_STATUS_UNDEFINED = 0,
|
||||
FWU_STATUS_FAILURE = 1,
|
||||
FWU_STATUS_COMPLETION = 2,
|
||||
} EFwuProcessStatus;
|
||||
|
||||
typedef enum {
|
||||
FWU_RSP_OK = 0,
|
||||
FWU_RSP_TOO_SHORT = 1,
|
||||
FWU_RSP_START_MARKER_MISSING = 2,
|
||||
FWU_RSP_END_MARKER_MISSING = 3,
|
||||
FWU_RSP_REQUEST_REFERENCE_INVALID = 4,
|
||||
FWU_RSP_ERROR_RESPONSE = 5,
|
||||
FWU_RSP_TIMEOUT = 6,
|
||||
FWU_RSP_PING_ID_MISMATCH = 7,
|
||||
FWU_RSP_RX_OVERFLOW = 8,
|
||||
FWU_RSP_INIT_COMMAND_TOO_LARGE = 9,
|
||||
FWU_RSP_CHECKSUM_ERROR = 10,
|
||||
FWU_RSP_DATA_OBJECT_TOO_LARGE = 11,
|
||||
FWU_RSP_RX_INVALID_ESCAPE_SEQ = 12,
|
||||
} EFwuResponseStatus;
|
||||
|
||||
typedef void (*FTxFunction)(struct SFwu *fwu, uint8_t *buf, uint8_t len);
|
||||
|
||||
typedef struct SFwu {
|
||||
// --- public - define these before calling fwuInit ---
|
||||
// .dat
|
||||
uint8_t *commandObject;
|
||||
uint32_t commandObjectLen;
|
||||
// .bin
|
||||
uint8_t *dataObject;
|
||||
uint32_t dataObjectLen;
|
||||
// Sending bytes to the target
|
||||
FTxFunction txFunction;
|
||||
// Timeout when waiting for a response from the target
|
||||
uint32_t responseTimeoutMillisec;
|
||||
// --- public - result codes
|
||||
// Overall process status code
|
||||
EFwuProcessStatus processStatus;
|
||||
// Response status code
|
||||
EFwuResponseStatus responseStatus;
|
||||
// --- private, don't modify ---
|
||||
uint32_t privateDataObjectOffset;
|
||||
uint32_t privateDataObjectSize;
|
||||
uint32_t privateDataObjectMaxSize;
|
||||
uint8_t privateProcessState;
|
||||
uint8_t privateCommandState;
|
||||
uint8_t privateCommandSendOnly;
|
||||
uint32_t privateCommandTimeoutRemainingMillisec;
|
||||
uint8_t privateRequestBuf[FWU_REQUEST_BUF_SIZE + 1];
|
||||
uint8_t privateRequestLen;
|
||||
uint8_t privateRequestIx;
|
||||
uint8_t privateResponseBuf[FWU_RESPONSE_BUF_SIZE];
|
||||
uint8_t privateResponseEscapeCharacter;
|
||||
uint8_t privateResponseLen;
|
||||
uint32_t privateResponseTimeElapsedMillisec;
|
||||
uint8_t privateSendBufSpace;
|
||||
uint8_t privateProcessRequest;
|
||||
uint8_t privateCommandRequest;
|
||||
uint16_t privateMtuSize;
|
||||
// sending a large object buffer
|
||||
uint8_t *privateObjectBuf;
|
||||
uint32_t privateObjectLen;
|
||||
uint32_t privateObjectIx;
|
||||
uint32_t privateObjectCrc;
|
||||
} TFwu;
|
||||
|
||||
// First function to call to set up the internal state in the FWU structure.
|
||||
void fwuInit(TFwu *fwu);
|
||||
|
||||
// Execute the firmware update.
|
||||
void fwuExec(TFwu *fwu);
|
||||
|
||||
// Call regularly to allow asynchronous processing to continue.
|
||||
EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec);
|
||||
|
||||
// Call after data from the target has been received.
|
||||
void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len);
|
||||
|
||||
// Inform the FWU module that it may send maxLen bytes of data to the target.
|
||||
void fwuCanSendData(TFwu *fwu, uint8_t maxLen);
|
||||
|
||||
// Call to send a chunk of the data object to the target.
|
||||
void fwuSendChunk(TFwu *fwu, uint8_t *buf, uint32_t len);
|
||||
|
||||
// Call to check if a chunk of the data object can be sent to the target.
|
||||
bool fwuIsReadyForChunk(TFwu *fwu);
|
||||
|
||||
#endif // __FWU_H__
|
49
core/embed/lib/ble/int_comm_defs.h
Normal file
49
core/embed/lib/ble/int_comm_defs.h
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
#ifndef __INT_COMM_DEFS__
|
||||
#define __INT_COMM_DEFS__
|
||||
|
||||
#define BLE_PACKET_SIZE (244)
|
||||
#define USB_DATA_SIZE (64)
|
||||
|
||||
#define COMM_HEADER_SIZE (3)
|
||||
#define COMM_FOOTER_SIZE (1)
|
||||
#define OVERHEAD_SIZE (COMM_HEADER_SIZE + COMM_FOOTER_SIZE)
|
||||
#define UART_PACKET_SIZE (USB_DATA_SIZE + OVERHEAD_SIZE)
|
||||
|
||||
#define EOM (0x55)
|
||||
#define INTERNAL_EVENT (0xA2)
|
||||
#define EXTERNAL_MESSAGE (0xA1)
|
||||
#define INTERNAL_MESSAGE (0xA0)
|
||||
|
||||
typedef struct {
|
||||
uint8_t msg_id;
|
||||
uint8_t connected;
|
||||
uint8_t advertising;
|
||||
uint8_t advertising_whitelist;
|
||||
|
||||
uint8_t peer_count;
|
||||
uint8_t reserved[2];
|
||||
uint8_t sd_version_number;
|
||||
|
||||
uint16_t sd_company_id;
|
||||
uint16_t sd_subversion_number;
|
||||
|
||||
uint32_t app_version;
|
||||
uint32_t bld_version;
|
||||
|
||||
} event_status_msg_t;
|
||||
|
||||
typedef enum {
|
||||
INTERNAL_EVENT_STATUS = 0x01,
|
||||
INTERNAL_EVENT_SUCCESS = 0x02,
|
||||
INTERNAL_EVENT_FAILURE = 0x03,
|
||||
} InternalEvent_t;
|
||||
|
||||
typedef enum {
|
||||
INTERNAL_CMD_SEND_STATE = 0x00,
|
||||
INTERNAL_CMD_ADVERTISING_ON = 0x01,
|
||||
INTERNAL_CMD_ADVERTISING_OFF = 0x02,
|
||||
INTERNAL_CMD_ERASE_BONDS = 0x03,
|
||||
INTERNAL_CMD_DISCONNECT = 0x04,
|
||||
} InternalCmd_t;
|
||||
#endif
|
124
core/embed/lib/ble/messages.c
Normal file
124
core/embed/lib/ble/messages.c
Normal file
@ -0,0 +1,124 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ble.h"
|
||||
#include "common.h"
|
||||
#include "int_comm_defs.h"
|
||||
#include "messages.h"
|
||||
#include "state.h"
|
||||
|
||||
void process_poll(uint8_t *data, uint32_t len) {
|
||||
uint8_t cmd = data[0];
|
||||
|
||||
switch (cmd) {
|
||||
// case INTERNAL_EVENT_INITIALIZED: {
|
||||
// set_connected(false);
|
||||
// set_initialized(true);
|
||||
// break;
|
||||
// }
|
||||
case INTERNAL_EVENT_STATUS: {
|
||||
event_status_msg_t *msg = (event_status_msg_t *)data;
|
||||
set_status(msg);
|
||||
set_initialized(true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool wait_for_answer(void) {
|
||||
uint8_t buf[64] = {0};
|
||||
|
||||
uint32_t ticks_start = hal_ticks_ms();
|
||||
int len = 0;
|
||||
|
||||
while (len == 0) {
|
||||
if (hal_ticks_ms() - ticks_start > 1000) {
|
||||
// timeout
|
||||
return false;
|
||||
}
|
||||
|
||||
len = ble_int_event_receive(buf, sizeof(buf));
|
||||
|
||||
if (len > 0) {
|
||||
process_poll(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void send_state_request(void) {
|
||||
uint8_t cmd = INTERNAL_CMD_SEND_STATE;
|
||||
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
|
||||
}
|
||||
|
||||
void send_advertising_on(bool whitelist) {
|
||||
uint8_t data[2];
|
||||
data[0] = INTERNAL_CMD_ADVERTISING_ON;
|
||||
data[1] = whitelist ? 1 : 0;
|
||||
ble_int_comm_send(data, sizeof(data), INTERNAL_EVENT);
|
||||
}
|
||||
|
||||
void send_advertising_off(void) {
|
||||
uint8_t cmd = INTERNAL_CMD_ADVERTISING_OFF;
|
||||
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
|
||||
}
|
||||
|
||||
bool send_erase_bonds(void) {
|
||||
if (!ble_firmware_running()) {
|
||||
return false;
|
||||
}
|
||||
uint8_t cmd = INTERNAL_CMD_ERASE_BONDS;
|
||||
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
|
||||
|
||||
uint8_t buf[64] = {0};
|
||||
|
||||
uint32_t ticks_start = hal_ticks_ms();
|
||||
int len = 0;
|
||||
|
||||
while (len == 0) {
|
||||
len = ble_int_event_receive(buf, sizeof(buf));
|
||||
|
||||
if (hal_ticks_ms() - ticks_start > 1000) {
|
||||
// timeout
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf[0] == INTERNAL_EVENT_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool send_disconnect(void) {
|
||||
if (!ble_firmware_running()) {
|
||||
return false;
|
||||
}
|
||||
uint8_t cmd = INTERNAL_CMD_DISCONNECT;
|
||||
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
|
||||
|
||||
uint8_t buf[64] = {0};
|
||||
|
||||
uint32_t ticks_start = hal_ticks_ms();
|
||||
int len = 0;
|
||||
|
||||
while (len == 0) {
|
||||
len = ble_int_event_receive(buf, sizeof(buf));
|
||||
|
||||
if (hal_ticks_ms() - ticks_start > 1000) {
|
||||
// timeout
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf[0] == INTERNAL_EVENT_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
20
core/embed/lib/ble/messages.h
Normal file
20
core/embed/lib/ble/messages.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __BLE_MESSAGES__
|
||||
#define __BLE_MESSAGES__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool wait_for_answer(void);
|
||||
|
||||
void process_poll(uint8_t *data, uint32_t len);
|
||||
|
||||
void send_state_request(void);
|
||||
|
||||
void send_advertising_on(bool whitelist);
|
||||
|
||||
void send_advertising_off(void);
|
||||
|
||||
bool send_erase_bonds(void);
|
||||
|
||||
bool send_disconnect(void);
|
||||
|
||||
#endif
|
73
core/embed/lib/ble/state.c
Normal file
73
core/embed/lib/ble/state.c
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
#include "ble/state.h"
|
||||
#include "ble.h"
|
||||
#include "messages.h"
|
||||
|
||||
static bool ble_state_connected = false;
|
||||
static bool ble_state_initialized = false;
|
||||
static bool ble_advertising_wanted = false;
|
||||
static bool ble_advertising_wl_wanted = false;
|
||||
static bool ble_advertising = false;
|
||||
static bool ble_advertising_wl = false;
|
||||
static bool ble_dfu_mode = false;
|
||||
static uint8_t ble_peer_count = 0;
|
||||
|
||||
bool ble_connected(void) {
|
||||
return ble_state_connected && ble_firmware_running();
|
||||
}
|
||||
|
||||
void set_status(event_status_msg_t *msg) {
|
||||
if (ble_state_connected != msg->connected) {
|
||||
ble_advertising_wanted = msg->peer_count > 0;
|
||||
ble_advertising_wl_wanted = true;
|
||||
}
|
||||
ble_state_connected = msg->connected;
|
||||
|
||||
ble_peer_count = msg->peer_count;
|
||||
if (msg->peer_count > 0 && !ble_initialized()) {
|
||||
ble_advertising_wanted = true;
|
||||
ble_advertising_wl_wanted = true;
|
||||
}
|
||||
|
||||
if (ble_advertising_wanted != msg->advertising ||
|
||||
(ble_advertising_wl_wanted != msg->advertising_whitelist)) {
|
||||
if (ble_advertising_wanted && !ble_state_connected) {
|
||||
send_advertising_on(ble_advertising_wl_wanted);
|
||||
}
|
||||
if (!ble_advertising_wanted && ble_advertising) {
|
||||
send_advertising_off();
|
||||
}
|
||||
}
|
||||
ble_advertising = msg->advertising;
|
||||
ble_advertising_wl = msg->advertising_whitelist;
|
||||
}
|
||||
|
||||
void set_initialized(bool initialized) { ble_state_initialized = initialized; }
|
||||
|
||||
bool ble_initialized(void) {
|
||||
return ble_state_initialized && ble_firmware_running();
|
||||
}
|
||||
|
||||
void start_advertising(bool whitelist) {
|
||||
ble_advertising_wl_wanted = whitelist;
|
||||
ble_advertising_wanted = true;
|
||||
if (!ble_advertising || ble_advertising_wl != whitelist) {
|
||||
send_advertising_on(whitelist);
|
||||
} else {
|
||||
send_state_request();
|
||||
}
|
||||
}
|
||||
|
||||
void stop_advertising(void) {
|
||||
ble_advertising_wanted = false;
|
||||
if (ble_advertising) {
|
||||
send_advertising_off();
|
||||
} else {
|
||||
send_state_request();
|
||||
}
|
||||
}
|
||||
|
||||
void ble_set_dfu_mode(bool dfu) { ble_dfu_mode = dfu; }
|
||||
|
||||
bool is_ble_dfu_mode(void) { return ble_dfu_mode; }
|
26
core/embed/lib/ble/state.h
Normal file
26
core/embed/lib/ble/state.h
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
#ifndef __BLE_STATE__
|
||||
#define __BLE_STATE__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "int_comm_defs.h"
|
||||
|
||||
bool ble_initialized(void);
|
||||
|
||||
void set_initialized(bool initialized);
|
||||
|
||||
bool ble_connected(void);
|
||||
|
||||
void set_status(event_status_msg_t *msg);
|
||||
|
||||
void start_advertising(bool whitelist);
|
||||
|
||||
void stop_advertising(void);
|
||||
|
||||
void ble_set_dfu_mode(bool dfu);
|
||||
|
||||
bool is_ble_dfu_mode(void);
|
||||
|
||||
#endif
|
87
core/embed/lib/protob_helpers.c
Normal file
87
core/embed/lib/protob_helpers.c
Normal file
@ -0,0 +1,87 @@
|
||||
|
||||
|
||||
#include "protob_helpers.h"
|
||||
|
||||
secbool send_protob_msg(uint8_t iface_num, uint16_t msg_id,
|
||||
const pb_msgdesc_t *fields, const void *msg,
|
||||
bool (*write)(pb_ostream_t *stream,
|
||||
const pb_byte_t *buf, size_t count),
|
||||
void (*write_flush)(write_state *state)) {
|
||||
// determine message size by serializing it into a dummy stream
|
||||
pb_ostream_t sizestream = {.callback = NULL,
|
||||
.state = NULL,
|
||||
.max_size = SIZE_MAX,
|
||||
.bytes_written = 0,
|
||||
.errmsg = NULL};
|
||||
if (false == pb_encode(&sizestream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
const uint32_t msg_size = sizestream.bytes_written;
|
||||
|
||||
write_state state = {
|
||||
.iface_num = iface_num,
|
||||
.packet_index = 0,
|
||||
.packet_pos = MSG_HEADER1_LEN,
|
||||
.buf =
|
||||
{
|
||||
'?',
|
||||
'#',
|
||||
'#',
|
||||
(msg_id >> 8) & 0xFF,
|
||||
msg_id & 0xFF,
|
||||
(msg_size >> 24) & 0xFF,
|
||||
(msg_size >> 16) & 0xFF,
|
||||
(msg_size >> 8) & 0xFF,
|
||||
msg_size & 0xFF,
|
||||
},
|
||||
};
|
||||
|
||||
pb_ostream_t stream = {.callback = write,
|
||||
.state = &state,
|
||||
.max_size = SIZE_MAX,
|
||||
.bytes_written = 0,
|
||||
.errmsg = NULL};
|
||||
|
||||
if (false == pb_encode(&stream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
write_flush(&state);
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
secbool recv_protob_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const pb_msgdesc_t *fields, void *msg,
|
||||
bool (*read)(pb_istream_t *stream, pb_byte_t *buf,
|
||||
size_t count),
|
||||
void (*read_flush)(read_state *state),
|
||||
uint16_t packet_size) {
|
||||
read_state state = {.iface_num = iface_num,
|
||||
.packet_index = 0,
|
||||
.packet_pos = MSG_HEADER1_LEN,
|
||||
.packet_size = packet_size,
|
||||
.buf = buf};
|
||||
|
||||
pb_istream_t stream = {.callback = read,
|
||||
.state = &state,
|
||||
.bytes_left = msg_size,
|
||||
.errmsg = NULL};
|
||||
|
||||
if (false == pb_decode_noinit(&stream, fields, msg)) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
read_flush(&state);
|
||||
|
||||
return sectrue;
|
||||
}
|
||||
|
||||
secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id,
|
||||
uint32_t *msg_size) {
|
||||
if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
|
||||
return secfalse;
|
||||
}
|
||||
*msg_id = (buf[3] << 8) + buf[4];
|
||||
*msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8];
|
||||
return sectrue;
|
||||
}
|
87
core/embed/lib/protob_helpers.h
Normal file
87
core/embed/lib/protob_helpers.h
Normal file
@ -0,0 +1,87 @@
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "secbool.h"
|
||||
|
||||
#define USB_PACKET_SIZE 64
|
||||
#define MSG_HEADER1_LEN 9
|
||||
#define MSG_HEADER2_LEN 1
|
||||
|
||||
#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default
|
||||
#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \
|
||||
{ msg_send.FIELD = VALUE; }
|
||||
#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
msg_send.FIELD = VALUE; \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \
|
||||
strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \
|
||||
strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \
|
||||
}
|
||||
#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \
|
||||
{ \
|
||||
msg_send.has_##FIELD = true; \
|
||||
memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \
|
||||
memcpy(msg_send.FIELD.bytes, VALUE, \
|
||||
MIN(LEN, sizeof(msg_send.FIELD.bytes))); \
|
||||
msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \
|
||||
}
|
||||
#define MSG_SEND_CALLBACK(FIELD, CALLBACK, ARGUMENT) \
|
||||
{ \
|
||||
msg_send.FIELD.funcs.encode = &CALLBACK; \
|
||||
msg_send.FIELD.arg = (void *)ARGUMENT; \
|
||||
}
|
||||
#define MSG_SEND(TYPE, WRITE, WRITE_FLUSH) \
|
||||
send_protob_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, \
|
||||
&msg_send, WRITE, WRITE_FLUSH)
|
||||
|
||||
#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default
|
||||
#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \
|
||||
{ \
|
||||
msg_recv.FIELD.funcs.decode = &CALLBACK; \
|
||||
msg_recv.FIELD.arg = (void *)ARGUMENT; \
|
||||
}
|
||||
#define MSG_RECV(TYPE, READ, READ_FLUSH, PACKET_SIZE) \
|
||||
recv_protob_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv, READ, \
|
||||
READ_FLUSH, PACKET_SIZE)
|
||||
|
||||
typedef struct {
|
||||
uint8_t iface_num;
|
||||
uint8_t packet_index;
|
||||
uint8_t packet_pos;
|
||||
uint8_t buf[USB_PACKET_SIZE];
|
||||
} write_state;
|
||||
|
||||
typedef struct {
|
||||
uint8_t iface_num;
|
||||
uint8_t packet_index;
|
||||
uint8_t packet_pos;
|
||||
uint16_t packet_size;
|
||||
uint8_t *buf;
|
||||
} read_state;
|
||||
|
||||
secbool send_protob_msg(uint8_t iface_num, uint16_t msg_id,
|
||||
const pb_msgdesc_t *fields, const void *msg,
|
||||
bool (*write_fnc)(pb_ostream_t *stream,
|
||||
const pb_byte_t *buf, size_t count),
|
||||
void (*write_flush)(write_state *state));
|
||||
|
||||
secbool recv_protob_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf,
|
||||
const pb_msgdesc_t *fields, void *msg,
|
||||
bool (*read)(pb_istream_t *stream, pb_byte_t *buf,
|
||||
size_t count),
|
||||
void (*read_flush)(read_state *state),
|
||||
uint16_t packet_size);
|
||||
|
||||
secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id,
|
||||
uint32_t *msg_size);
|
@ -23,6 +23,7 @@ clippy = []
|
||||
jpeg = []
|
||||
disp_i8080_8bit_dw = [] # write pixels directly to peripheral
|
||||
disp_i8080_16bit_dw = [] # write pixels directly to peripheral
|
||||
ble = []
|
||||
debug = ["ui_debug"]
|
||||
sbu = []
|
||||
sd_card = []
|
||||
|
@ -358,6 +358,9 @@ fn generate_trezorhal_bindings() {
|
||||
.no_copy("buffer_jpeg_t")
|
||||
.no_copy("buffer_jpeg_work_t")
|
||||
.no_copy("buffer_blurring_t")
|
||||
// ble
|
||||
.allowlist_function("ble_connected")
|
||||
.allowlist_function("start_advertising")
|
||||
//usb
|
||||
.allowlist_function("usb_configured")
|
||||
// touch
|
||||
|
@ -28,6 +28,11 @@ void screen_welcome(void);
|
||||
void screen_boot_empty(bool fading);
|
||||
uint32_t screen_unlock_bootloader_confirm(void);
|
||||
void screen_unlock_bootloader_success(void);
|
||||
|
||||
uint32_t screen_pairing_confirm(uint8_t* buffer);
|
||||
uint32_t screen_comparison_confirm(uint8_t* code, uint8_t code_len);
|
||||
uint32_t screen_repair_confirm(void);
|
||||
|
||||
void display_image(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen);
|
||||
void display_icon(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen,
|
||||
uint16_t fg_color, uint16_t bg_color);
|
||||
|
9
core/embed/rust/src/trezorhal/ble.rs
Normal file
9
core/embed/rust/src/trezorhal/ble.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use super::ffi;
|
||||
|
||||
pub fn ble_connected() -> bool {
|
||||
unsafe { ffi::ble_connected() }
|
||||
}
|
||||
|
||||
pub fn start_advertising(whitelist: bool) {
|
||||
unsafe { ffi::start_advertising(whitelist) }
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
pub mod bip39;
|
||||
#[cfg(feature = "ble")]
|
||||
pub mod ble;
|
||||
#[macro_use]
|
||||
#[allow(unused_macros)]
|
||||
pub mod fatal_error;
|
||||
|
@ -14,17 +14,19 @@ use crate::{
|
||||
theme::{
|
||||
button_bld, button_confirm, button_wipe_cancel, button_wipe_confirm, BLD_BG,
|
||||
BLD_FG, BLD_WIPE_COLOR, CHECK24, CHECK40, DOWNLOAD32, FIRE32, FIRE40,
|
||||
TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40, WELCOME_COLOR, X24,
|
||||
TEXT_NORMAL, TEXT_WIPE_BOLD, TEXT_WIPE_NORMAL, WARNING40,
|
||||
WELCOME_COLOR, X24,
|
||||
},
|
||||
welcome::Welcome,
|
||||
},
|
||||
component::{Button, ResultScreen, WelcomeScreen},
|
||||
component::{Button, PinKeyboard, ResultScreen, WelcomeScreen},
|
||||
constant,
|
||||
theme::{BACKLIGHT_DIM, BACKLIGHT_NORMAL, FG, WHITE},
|
||||
},
|
||||
util::{from_c_array, from_c_str},
|
||||
},
|
||||
};
|
||||
use core::slice;
|
||||
use heapless::String;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
@ -39,6 +41,7 @@ use crate::{trezorhal::secbool::secbool, ui::model_tt::theme::BLACK};
|
||||
use confirm::Confirm;
|
||||
use intro::Intro;
|
||||
use menu::Menu;
|
||||
use crate::ui::geometry::Alignment;
|
||||
|
||||
use self::theme::{RESULT_FW_INSTALL, RESULT_INITIAL, RESULT_WIPE};
|
||||
|
||||
@ -73,13 +76,16 @@ fn fadeout() {
|
||||
display::fade_backlight_duration(BACKLIGHT_DIM, 150);
|
||||
}
|
||||
|
||||
fn run<F>(frame: &mut F) -> u32
|
||||
fn run<F>(frame: &mut F, clear: bool) -> u32
|
||||
where
|
||||
F: Component,
|
||||
F::Msg: ReturnToC,
|
||||
{
|
||||
frame.place(constant::screen());
|
||||
fadeout();
|
||||
if clear {
|
||||
display::rect_fill(screen(), BLACK);
|
||||
}
|
||||
display::sync();
|
||||
frame.paint();
|
||||
fadein();
|
||||
@ -190,7 +196,7 @@ extern "C" fn screen_install_confirm(
|
||||
Some(("FW FINGERPRINT", fingerprint_str)),
|
||||
);
|
||||
|
||||
run(&mut frame)
|
||||
run(&mut frame, false)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -216,12 +222,12 @@ extern "C" fn screen_wipe_confirm() -> u32 {
|
||||
None,
|
||||
);
|
||||
|
||||
run(&mut frame)
|
||||
run(&mut frame, false)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_menu(firmware_present: secbool) -> u32 {
|
||||
run(&mut Menu::new(firmware_present))
|
||||
run(&mut Menu::new(firmware_present), true)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -248,7 +254,7 @@ extern "C" fn screen_intro(
|
||||
|
||||
let mut frame = Intro::new(title_str.as_str(), version_str.as_str(), fw_ok);
|
||||
|
||||
run(&mut frame)
|
||||
run(&mut frame, false)
|
||||
}
|
||||
|
||||
fn screen_progress(
|
||||
@ -433,3 +439,65 @@ extern "C" fn bld_continue_label(bg_color: cty::uint16_t) {
|
||||
Color::from_u16(bg_color),
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_pairing_confirm(buffer: *const cty::uint8_t) -> u32 {
|
||||
let pin_slice = unsafe { slice::from_raw_parts_mut(buffer as *mut u8, 6) };
|
||||
|
||||
let mut pin = PinKeyboard::new("Enter passkey", "", None, true);
|
||||
let res = run(&mut pin, true);
|
||||
|
||||
if res == 2 {
|
||||
let pin = pin.pin().as_bytes();
|
||||
if pin.len() == 6 {
|
||||
pin_slice.copy_from_slice(&pin[0..6]);
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_comparison_confirm(code: *const cty::uint8_t, code_len: u8) -> u32 {
|
||||
let code = unwrap!(unsafe { from_c_array(code, code_len as usize) });
|
||||
|
||||
|
||||
let right = Button::with_text("YES").styled(button_confirm());
|
||||
let left = Button::with_text("CANCEL").styled(button_bld());
|
||||
let title = Label::new("DO THE NUMBERS MATCH?", Alignment::Start, theme::TEXT_BOLD)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = Confirm::new(
|
||||
BLD_BG,
|
||||
left,
|
||||
right,
|
||||
ConfirmTitle::Text(title),
|
||||
Label::new(code, Alignment::Center, TEXT_NORMAL),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
run(&mut frame, true)
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn screen_repair_confirm() -> u32 {
|
||||
let msg = Label::new("Allow repair?", Alignment::Center, TEXT_NORMAL);
|
||||
let right = Button::with_text("ALLOW").styled(button_confirm());
|
||||
let left = Button::with_text("DENY").styled(button_bld());
|
||||
let title = Label::new("REPAIR", Alignment::Start, theme::TEXT_BOLD)
|
||||
.vertically_centered();
|
||||
|
||||
let mut frame = Confirm::new(
|
||||
BLD_BG,
|
||||
left,
|
||||
right,
|
||||
ConfirmTitle::Text(title),
|
||||
msg,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
run(&mut frame, true)
|
||||
}
|
||||
|
@ -231,6 +231,8 @@ pub fn button_bld() -> ButtonStyleSheet {
|
||||
}
|
||||
}
|
||||
|
||||
pub const TEXT_NORMAL_BLACK: TextStyle = TextStyle::new(Font::NORMAL, WHITE, BLACK, WHITE, WHITE);
|
||||
|
||||
pub const TEXT_TITLE: TextStyle = TextStyle::new(
|
||||
Font::BOLD,
|
||||
BLD_TITLE_COLOR,
|
||||
|
@ -19,9 +19,10 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, ToPrimitive)]
|
||||
pub enum PinKeyboardMsg {
|
||||
Confirmed,
|
||||
Cancelled,
|
||||
Cancelled = 1,
|
||||
Confirmed = 2,
|
||||
}
|
||||
|
||||
const MAX_LENGTH: usize = 50;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include TREZOR_BOARD
|
||||
#include "ble/state.h"
|
||||
#include "buffers.h"
|
||||
#include "button.h"
|
||||
#include "common.h"
|
||||
|
@ -123,7 +123,6 @@ static uint32_t nrf_sw_backend_hash_sha256_finalize(void * const p_context,
|
||||
|
||||
}
|
||||
|
||||
|
||||
const nrf_crypto_hash_info_t g_nrf_crypto_hash_sha256_info =
|
||||
{
|
||||
.init_fn = nrf_sw_backend_hash_sha256_init,
|
||||
|
@ -27,6 +27,7 @@ SRC_FILES += \
|
||||
$(SDK_ROOT)/components/libraries/crc16/crc16.c \
|
||||
$(SDK_ROOT)/components/libraries/timer/drv_rtc.c \
|
||||
$(SDK_ROOT)/components/libraries/fds/fds.c \
|
||||
$(SDK_ROOT)/components/libraries/fds/fds.c \
|
||||
$(SDK_ROOT)/components/libraries/hardfault/hardfault_implementation.c \
|
||||
$(SDK_ROOT)/components/libraries/util/nrf_assert.c \
|
||||
$(SDK_ROOT)/components/libraries/atomic_fifo/nrf_atfifo.c \
|
||||
|
208
core/embed/segger/SEGGER_MMD/HardFaultHandler.S
Normal file
208
core/embed/segger/SEGGER_MMD/HardFaultHandler.S
Normal file
@ -0,0 +1,208 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 2014 - 2020 SEGGER Microcontroller GmbH *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or *
|
||||
* without modification, are permitted provided that the following *
|
||||
* conditions are met: *
|
||||
* *
|
||||
* - Redistributions of source code must retain the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* - Neither the name of SEGGER Microcontroller GmbH *
|
||||
* nor the names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without specific *
|
||||
* prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
|
||||
* DISCLAIMED. *
|
||||
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR *
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
|
||||
* DAMAGE. *
|
||||
* *
|
||||
**********************************************************************
|
||||
;
|
||||
;----------------------------------------------------------------------
|
||||
;File : HardFaultHandler.S
|
||||
;Purpose : HardFault exception handler for IAR, Keil and GNU assembler.
|
||||
; Evaluates used stack (MSP, PSP) and passes appropiate stack
|
||||
; pointer to the HardFaultHandler "C"-routine.
|
||||
;------------- END-OF-HEADER ------------------------------------------
|
||||
;*/
|
||||
|
||||
#ifndef __IAR_SYSTEMS_ASM__
|
||||
#ifndef __CC_ARM
|
||||
#ifndef __clang__
|
||||
#ifndef __GNUC__
|
||||
#error "Unsupported assembler!"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* Forward declarations of segments used
|
||||
;*
|
||||
;**********************************************************************
|
||||
;*/
|
||||
|
||||
#if (defined(__IAR_SYSTEMS_ASM__))
|
||||
|
||||
SECTION CODE:CODE:NOROOT(2)
|
||||
SECTION CSTACK:DATA:NOROOT(3)
|
||||
|
||||
#elif (defined(__CC_ARM))
|
||||
|
||||
AREA OSKERNEL, CODE, READONLY, ALIGN=2
|
||||
PRESERVE8
|
||||
|
||||
#endif
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* Publics
|
||||
;*
|
||||
;**********************************************************************
|
||||
;*/
|
||||
|
||||
#if (defined(__IAR_SYSTEMS_ASM__))
|
||||
|
||||
SECTION .text:CODE:NOROOT(2)
|
||||
PUBLIC HardFault_Handler
|
||||
|
||||
#elif (defined(__CC_ARM))
|
||||
|
||||
EXPORT HardFault_Handler
|
||||
|
||||
#elif (defined(__clang__) || defined(__GNUC__))
|
||||
|
||||
.global HardFault_Handler
|
||||
.type HardFault_Handler, function
|
||||
|
||||
#endif
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* Externals, code
|
||||
;*
|
||||
;**********************************************************************
|
||||
;*/
|
||||
|
||||
#if (defined(__IAR_SYSTEMS_ASM__))
|
||||
|
||||
EXTERN HardFaultHandler
|
||||
|
||||
#elif (defined(__CC_ARM))
|
||||
|
||||
IMPORT HardFaultHandler
|
||||
|
||||
#elif (defined(__clang__) || defined(__GNUC__))
|
||||
|
||||
.extern HardFaultHandler
|
||||
|
||||
#endif
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* CODE segment
|
||||
;*
|
||||
;**********************************************************************
|
||||
;*/
|
||||
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
|
||||
.syntax unified
|
||||
.thumb
|
||||
.balign 4
|
||||
.text
|
||||
|
||||
#else
|
||||
|
||||
THUMB
|
||||
|
||||
#endif
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* Global functions
|
||||
;*
|
||||
;**********************************************************************
|
||||
;*/
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* HardFault_Handler()
|
||||
;*
|
||||
;* Function description
|
||||
;* Evaluates the used stack (MSP, PSP) and passes the appropiate
|
||||
;* stack pointer to the HardFaultHandler "C"-routine.
|
||||
;*
|
||||
;* Notes
|
||||
;* (1) Ensure that HardFault_Handler is part of the exception table
|
||||
;*/
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
HardFault_Handler:
|
||||
#else
|
||||
HardFault_Handler
|
||||
#endif
|
||||
#if (defined(__IAR_SYSTEMS_ASM__) && defined(__ARM6M__) && (__CORE__ == __ARM6M__)) || \
|
||||
(defined(__CC_ARM) && defined(__TARGET_ARCH_6S_M)) || \
|
||||
(defined(__clang__) && defined(__ARM_ARCH) && (__ARM_ARCH == 6)) || \
|
||||
(defined(__GNUC__) && (defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_8M_BASE__)))
|
||||
;// This version is for Cortex M0
|
||||
movs R0, #4
|
||||
mov R1, LR
|
||||
tst R0, R1 ;// Check EXC_RETURN in Link register bit 2.
|
||||
bne Uses_PSP
|
||||
mrs R0, MSP ;// Stacking was using MSP.
|
||||
b Pass_StackPtr
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
Uses_PSP:
|
||||
#else
|
||||
Uses_PSP
|
||||
#endif
|
||||
mrs R0, PSP ;// Stacking was using PSP.
|
||||
#if (defined(__GNUC__) || defined(__clang__))
|
||||
Pass_StackPtr:
|
||||
#else
|
||||
Pass_StackPtr
|
||||
#endif
|
||||
#else
|
||||
;// This version is for Cortex M3, Cortex M4 and Cortex M4F
|
||||
tst LR, #4 ;// Check EXC_RETURN in Link register bit 2.
|
||||
ite EQ
|
||||
mrseq R0, MSP ;// Stacking was using MSP.
|
||||
mrsne R0, PSP ;// Stacking was using PSP.
|
||||
#endif
|
||||
#if (defined(__CC_ARM))
|
||||
ALIGN
|
||||
#endif
|
||||
ldr R1,=HardFaultHandler
|
||||
bx R1 ;// Stack pointer passed through R0.
|
||||
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
.end
|
||||
#else
|
||||
END
|
||||
#endif
|
||||
|
||||
;/****** End Of File *************************************************/
|
155
core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c
Normal file
155
core/embed/segger/SEGGER_MMD/JLINK_MONITOR.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* SEGGER strongly recommends to not make any changes *
|
||||
* to or modify the source code of this software in order to stay *
|
||||
* compatible with the monitor mode protocol and J-Link. *
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or *
|
||||
* without modification, are permitted provided that the following *
|
||||
* conditions are met: *
|
||||
* *
|
||||
* - Redistributions of source code must retain the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* - Redistributions in binary form must reproduce the above *
|
||||
* copyright notice, this list of conditions and the following *
|
||||
* disclaimer in the documentation and/or other materials provided *
|
||||
* with the distribution. *
|
||||
* *
|
||||
* - Neither the name of SEGGER Microcontroller GmbH *
|
||||
* nor the names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without specific *
|
||||
* prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
|
||||
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
|
||||
* DAMAGE. *
|
||||
* *
|
||||
**********************************************************************
|
||||
----------------------------------------------------------------------
|
||||
File : JLINK_MONITOR.c
|
||||
Purpose : Implementation of debug monitor for J-Link monitor mode debug on Cortex-M devices.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
#include "JLINK_MONITOR.h"
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Configuration
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Types
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Static data
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Local functions
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Global functions
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* JLINK_MONITOR_OnExit()
|
||||
*
|
||||
* Function description
|
||||
* Called from DebugMon_Handler(), once per debug exit.
|
||||
* May perform some target specific operations to be done on debug mode exit.
|
||||
*
|
||||
* Notes
|
||||
* (1) Must not keep the CPU busy for more than 100 ms
|
||||
*/
|
||||
void JLINK_MONITOR_OnExit(void) {
|
||||
//
|
||||
// Add custom code here
|
||||
//
|
||||
// BSP_ClrLED(0);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* JLINK_MONITOR_OnEnter()
|
||||
*
|
||||
* Function description
|
||||
* Called from DebugMon_Handler(), once per debug entry.
|
||||
* May perform some target specific operations to be done on debug mode entry
|
||||
*
|
||||
* Notes
|
||||
* (1) Must not keep the CPU busy for more than 100 ms
|
||||
*/
|
||||
void JLINK_MONITOR_OnEnter(void) {
|
||||
//
|
||||
// Add custom code here
|
||||
//
|
||||
// BSP_SetLED(0);
|
||||
// BSP_ClrLED(1);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* JLINK_MONITOR_OnPoll()
|
||||
*
|
||||
* Function description
|
||||
* Called periodically from DebugMon_Handler(), to perform some actions that need to be performed periodically during debug mode.
|
||||
*
|
||||
* Notes
|
||||
* (1) Must not keep the CPU busy for more than 100 ms
|
||||
*/
|
||||
void JLINK_MONITOR_OnPoll(void) {
|
||||
//
|
||||
// Add custom code here
|
||||
//
|
||||
// BSP_ToggleLED(0);
|
||||
// _Delay(500000);
|
||||
}
|
||||
|
||||
/****** End Of File *************************************************/
|
65
core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h
Normal file
65
core/embed/segger/SEGGER_MMD/JLINK_MONITOR.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 1995 - 2018 SEGGER Microcontroller GmbH *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* SEGGER strongly recommends to not make any changes *
|
||||
* to or modify the source code of this software in order to stay *
|
||||
* compatible with the monitor mode protocol and J-Link. *
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or *
|
||||
* without modification, are permitted provided that the following *
|
||||
* conditions are met: *
|
||||
* *
|
||||
* - Redistributions of source code must retain the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* - Redistributions in binary form must reproduce the above *
|
||||
* copyright notice, this list of conditions and the following *
|
||||
* disclaimer in the documentation and/or other materials provided *
|
||||
* with the distribution. *
|
||||
* *
|
||||
* - Neither the name of SEGGER Microcontroller GmbH *
|
||||
* nor the names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without specific *
|
||||
* prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
|
||||
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
|
||||
* DAMAGE. *
|
||||
* *
|
||||
**********************************************************************
|
||||
----------------------------------------------------------------------
|
||||
File : JLINK_MONITOR.h
|
||||
Purpose : Header file of debug monitor for J-Link monitor mode debug on Cortex-M devices.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef JLINK_MONITOR_H
|
||||
#define JLINK_MONITOR_H
|
||||
|
||||
void JLINK_MONITOR_OnExit (void);
|
||||
void JLINK_MONITOR_OnEnter (void);
|
||||
void JLINK_MONITOR_OnPoll (void);
|
||||
|
||||
#endif
|
||||
|
||||
/****** End Of File *************************************************/
|
927
core/embed/segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S
Normal file
927
core/embed/segger/SEGGER_MMD/JLINK_MONITOR_ISR_SES.S
Normal file
@ -0,0 +1,927 @@
|
||||
/*********************************************************************
|
||||
* (c) SEGGER Microcontroller GmbH *
|
||||
* The Embedded Experts *
|
||||
* www.segger.com *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 1995 - 2020 SEGGER Microcontroller GmbH *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* SEGGER strongly recommends to not make any changes *
|
||||
* to or modify the source code of this software in order to stay *
|
||||
* compatible with the monitor mode protocol and J-Link. *
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or *
|
||||
* without modification, are permitted provided that the following *
|
||||
* conditions are met: *
|
||||
* *
|
||||
* - Redistributions of source code must retain the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* - Redistributions in binary form must reproduce the above *
|
||||
* copyright notice, this list of conditions and the following *
|
||||
* disclaimer in the documentation and/or other materials provided *
|
||||
* with the distribution. *
|
||||
* *
|
||||
* - Neither the name of SEGGER Microcontroller GmbH *
|
||||
* nor the names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without specific *
|
||||
* prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
|
||||
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
|
||||
* DAMAGE. *
|
||||
* *
|
||||
**********************************************************************
|
||||
----------------------------------------------------------------------
|
||||
File : JLINK_MONITOR_ISR_SES.s
|
||||
Purpose : Implementation of debug monitor for J-Link monitor mode
|
||||
debug on Cortex-M devices, supporting SES compiler.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
.name JLINK_MONITOR_ISR
|
||||
.syntax unified
|
||||
|
||||
.extern JLINK_MONITOR_OnEnter
|
||||
.extern JLINK_MONITOR_OnExit
|
||||
.extern JLINK_MONITOR_OnPoll
|
||||
|
||||
.global DebugMon_Handler
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines, configurable
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
#define _MON_VERSION 100 // V x.yy
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines, fixed
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
#define _APP_SP_OFF_R0 0x00
|
||||
#define _APP_SP_OFF_R1 0x04
|
||||
#define _APP_SP_OFF_R2 0x08
|
||||
#define _APP_SP_OFF_R3 0x0C
|
||||
#define _APP_SP_OFF_R12 0x10
|
||||
#define _APP_SP_OFF_R14_LR 0x14
|
||||
#define _APP_SP_OFF_PC 0x18
|
||||
#define _APP_SP_OFF_XPSR 0x1C
|
||||
#define _APP_SP_OFF_S0 0x20
|
||||
#define _APP_SP_OFF_S1 0x24
|
||||
#define _APP_SP_OFF_S2 0x28
|
||||
#define _APP_SP_OFF_S3 0x2C
|
||||
#define _APP_SP_OFF_S4 0x30
|
||||
#define _APP_SP_OFF_S5 0x34
|
||||
#define _APP_SP_OFF_S6 0x38
|
||||
#define _APP_SP_OFF_S7 0x3C
|
||||
#define _APP_SP_OFF_S8 0x40
|
||||
#define _APP_SP_OFF_S9 0x44
|
||||
#define _APP_SP_OFF_S10 0x48
|
||||
#define _APP_SP_OFF_S11 0x4C
|
||||
#define _APP_SP_OFF_S12 0x50
|
||||
#define _APP_SP_OFF_S13 0x54
|
||||
#define _APP_SP_OFF_S14 0x58
|
||||
#define _APP_SP_OFF_S15 0x5C
|
||||
#define _APP_SP_OFF_FPSCR 0x60
|
||||
|
||||
#define _NUM_BYTES_BASIC_STACKFRAME 32
|
||||
#define _NUM_BYTES_EXTENDED_STACKFRAME 104 // Values for stackframes are explained at location where they are used
|
||||
|
||||
#define _SYSTEM_DCRDR_OFF 0x00
|
||||
#define _SYSTEM_DEMCR_OFF 0x04
|
||||
|
||||
#define _SYSTEM_DHCSR 0xE000EDF0 // Debug Halting Control and Status Register (DHCSR)
|
||||
#define _SYSTEM_DCRSR 0xE000EDF4 // Debug Core Register Selector Register (DCRSR)
|
||||
#define _SYSTEM_DCRDR 0xE000EDF8 // Debug Core Register Data Register (DCRDR)
|
||||
#define _SYSTEM_DEMCR 0xE000EDFC // Debug Exception and Monitor Control Register (DEMCR)
|
||||
|
||||
#define _SYSTEM_FPCCR 0xE000EF34 // Floating-Point Context Control Register (FPCCR)
|
||||
#define _SYSTEM_FPCAR 0xE000EF38 // Floating-Point Context Address Register (FPCAR)
|
||||
#define _SYSTEM_FPDSCR 0xE000EF3C // Floating-Point Default Status Control Register (FPDSCR)
|
||||
#define _SYSTEM_MVFR0 0xE000EF40 // Media and FP Feature Register 0 (MVFR0)
|
||||
#define _SYSTEM_MVFR1 0xE000EF44 // Media and FP Feature Register 1 (MVFR1)
|
||||
|
||||
/*
|
||||
* Defines for determining if the current debug config supports FPU registers
|
||||
* For some compilers like IAR EWARM when disabling the FPU in the compiler settings an error is thrown when
|
||||
*/
|
||||
#ifdef __FPU_PRESENT
|
||||
#if __FPU_PRESENT
|
||||
#define _HAS_FPU_REGS 1
|
||||
#else
|
||||
#define _HAS_FPU_REGS 0
|
||||
#endif
|
||||
#else
|
||||
#define _HAS_FPU_REGS 0
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Signature of monitor
|
||||
*
|
||||
* Function description
|
||||
* Needed for targets where also a boot ROM is present that possibly specifies a vector table with a valid debug monitor exception entry
|
||||
*/
|
||||
.section .text, "ax"
|
||||
|
||||
//
|
||||
// JLINKMONHANDLER
|
||||
//
|
||||
.byte 0x4A
|
||||
.byte 0x4C
|
||||
.byte 0x49
|
||||
.byte 0x4E
|
||||
.byte 0x4B
|
||||
.byte 0x4D
|
||||
.byte 0x4F
|
||||
.byte 0x4E
|
||||
.byte 0x48
|
||||
.byte 0x41
|
||||
.byte 0x4E
|
||||
.byte 0x44
|
||||
.byte 0x4C
|
||||
.byte 0x45
|
||||
.byte 0x52
|
||||
.byte 0x00 // Align to 8-bytes
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* DebugMon_Handler()
|
||||
*
|
||||
* Function description
|
||||
* Debug monitor handler. CPU enters this handler in case a "halt" request is made from the debugger.
|
||||
* This handler is also responsible for handling commands that are sent by the debugger.
|
||||
*
|
||||
* Notes
|
||||
* This is actually the ISR for the debug inerrupt (exception no. 12)
|
||||
*/
|
||||
.thumb_func
|
||||
|
||||
DebugMon_Handler:
|
||||
/*
|
||||
General procedure:
|
||||
DCRDR is used as communication register
|
||||
DEMCR[19] is used as ready flag
|
||||
For the command J-Link sends to the monitor: DCRDR[7:0] == Cmd, DCRDR[31:8] == ParamData
|
||||
|
||||
1) Monitor sets DEMCR[19] whenever it is ready to receive new commands/data
|
||||
DEMCR[19] is initially set on debug monitor entry
|
||||
2) J-Link will clear DEMCR[19] it has placed command/data in DCRDR for monitor
|
||||
3) Monitor will wait for DEMCR[19] to be cleared
|
||||
4) Monitor will process command (May cause additional data transfers etc., depends on command). Monitor will set DEMCR[19] whenever it has placed data for J-Link in the DCRDR
|
||||
5) No restart-CPU command? => Back to 2), Otherwise => 6)
|
||||
6) Monitor will clear DEMCR[19] 19 to indicate that it is no longer ready
|
||||
*/
|
||||
PUSH {LR}
|
||||
BL JLINK_MONITOR_OnEnter
|
||||
POP {LR}
|
||||
LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR
|
||||
B.N _IndicateMonReady
|
||||
_WaitProbeReadIndicateMonRdy: // while(_SYSTEM_DEMCR & (1uL << 19)); => Wait until J-Link has read item
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR
|
||||
LSLS R0,R0,#+12
|
||||
BMI.N _WaitProbeReadIndicateMonRdy
|
||||
_IndicateMonReady:
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
/*
|
||||
During command loop:
|
||||
R0 = Tmp
|
||||
R1 = Tmp
|
||||
R2 = Tmp
|
||||
R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset)
|
||||
R12 = Tmp
|
||||
|
||||
Outside command loop R0-R3 and R12 may be overwritten by MONITOR_OnPoll()
|
||||
*/
|
||||
_WaitForJLinkCmd: // do {
|
||||
PUSH {LR}
|
||||
BL JLINK_MONITOR_OnPoll
|
||||
POP {LR}
|
||||
LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
LSRS R0,R0,#+20 // DEMCR[19] -> Carry Clear? => J-Link has placed command for us
|
||||
BCS _WaitForJLinkCmd
|
||||
/*
|
||||
Perform command
|
||||
Command is placed by J-Link in DCRDR[7:0] and additional parameter data is stored in DCRDR[31:8]
|
||||
J-Link clears DEMCR[19] to indicate that it placed a command/data or read data
|
||||
Monitor sets DEMCR[19] to indicate that it placed data or read data / is ready for a new command
|
||||
Setting DEMCR[19] indicates "monitor ready for new command / data" and also indicates: "data has been placed in DCRDR by monitor, for J-Link"
|
||||
Therefore it is responsibility of the commands to respond to the commands accordingly
|
||||
|
||||
Commands for debug monitor
|
||||
Commands must not exceed 0xFF (255) as we only defined 8-bits for command-part. Higher 24-bits are parameter info for current command
|
||||
|
||||
Protocol for different commands:
|
||||
J-Link: Cmd -> DCRDR, DEMCR[19] -> 0 => Cmd placed by probe
|
||||
*/
|
||||
LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // ParamInfo = _SYSTEM_DCRDR
|
||||
LSRS R1,R0,#+8 // ParamInfo >>= 8
|
||||
LSLS R0,R0,#+24
|
||||
LSRS R0,R0,#+24 // Cmd = ParamInfo & 0xFF
|
||||
//
|
||||
// switch (Cmd)
|
||||
//
|
||||
CMP R0,#+0
|
||||
BEQ.N _HandleGetMonVersion // case _MON_CMD_GET_MONITOR_VERSION
|
||||
CMP R0,#+2
|
||||
BEQ.N _HandleReadReg // case _MON_CMD_READ_REG
|
||||
BCC.N _HandleRestartCPU // case _MON_CMD_RESTART_CPU
|
||||
CMP R0,#+3
|
||||
BEQ.N _HandleWriteReg_Veneer // case _MON_CMD_WRITE_REG
|
||||
B.N _IndicateMonReady // default : while (1);
|
||||
/*
|
||||
Return
|
||||
_MON_CMD_RESTART_CPU
|
||||
CPU: DEMCR[19] -> 0 => Monitor no longer ready
|
||||
*/
|
||||
_HandleRestartCPU:
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR &= ~(1uL << 19); => Clear MON_REQ to indicate that monitor is no longer active
|
||||
BIC R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
PUSH {LR}
|
||||
BL JLINK_MONITOR_OnExit
|
||||
POP {PC}
|
||||
//
|
||||
// Place data section here to not get in trouble with load-offsets
|
||||
//
|
||||
.section .text, "ax", %progbits
|
||||
.align 2
|
||||
_AddrDCRDR:
|
||||
.long 0xE000EDF8
|
||||
_AddrCPACR:
|
||||
.long 0xE000ED88
|
||||
|
||||
.section .text, "ax"
|
||||
.thumb_func
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* _HandleGetMonVersion
|
||||
;*
|
||||
;*/
|
||||
_HandleGetMonVersion:
|
||||
/*
|
||||
_MON_CMD_GET_MONITOR_VERSION
|
||||
CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready
|
||||
J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read
|
||||
CPU: DEMCR[19] -> 1 => Mon ready
|
||||
*/
|
||||
MOVS R0,#+_MON_VERSION
|
||||
STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // _SYSTEM_DCRDR = x
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready
|
||||
B _WaitProbeReadIndicateMonRdy
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* _HandleReadReg
|
||||
*
|
||||
*/
|
||||
_HandleWriteReg_Veneer:
|
||||
B.N _HandleWriteReg
|
||||
_HandleReadReg:
|
||||
/*
|
||||
_MON_CMD_READ_REG
|
||||
CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready
|
||||
J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read
|
||||
CPU: DEMCR[19] -> 1 => Mon ready
|
||||
|
||||
|
||||
Register indexes
|
||||
0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!)
|
||||
16: XPSR
|
||||
17: MSP
|
||||
18: PSP
|
||||
19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
20: FPSCR
|
||||
21-52: FPS0-FPS31
|
||||
|
||||
|
||||
Register usage when entering this "subroutine":
|
||||
R0 Cmd
|
||||
R1 ParamInfo
|
||||
R2 ---
|
||||
R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset)
|
||||
R12 ---
|
||||
|
||||
Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension
|
||||
LR Return to Return SP Frame type
|
||||
---------------------------------------------------------
|
||||
0xFFFFFFE1 Handler mode. MSP Extended
|
||||
0xFFFFFFE9 Thread mode MSP Extended
|
||||
0xFFFFFFED Thread mode PSP Extended
|
||||
0xFFFFFFF1 Handler mode. MSP Basic
|
||||
0xFFFFFFF9 Thread mode MSP Basic
|
||||
0xFFFFFFFD Thread mode PSP Basic
|
||||
|
||||
So LR[2] == 1 => Return stack == PSP else MSP
|
||||
|
||||
R0-R3, R12, PC, xPSR can be read from application stackpointer
|
||||
Other regs can be read directly
|
||||
*/
|
||||
LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP
|
||||
ITE CS
|
||||
MRSCS R2,PSP
|
||||
MRSCC R2,MSP
|
||||
CMP R1,#+4 // if (RegIndex < 4) { (R0-R3)
|
||||
BCS _HandleReadRegR4
|
||||
LDR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3)
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR4:
|
||||
CMP R1,#+5 // if (RegIndex < 5) { (R4)
|
||||
BCS _HandleReadRegR5
|
||||
MOV R0,R4
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR5:
|
||||
CMP R1,#+6 // if (RegIndex < 6) { (R5)
|
||||
BCS _HandleReadRegR6
|
||||
MOV R0,R5
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR6:
|
||||
CMP R1,#+7 // if (RegIndex < 7) { (R6)
|
||||
BCS _HandleReadRegR7
|
||||
MOV R0,R6
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR7:
|
||||
CMP R1,#+8 // if (RegIndex < 8) { (R7)
|
||||
BCS _HandleReadRegR8
|
||||
MOV R0,R7
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR8:
|
||||
CMP R1,#+9 // if (RegIndex < 9) { (R8)
|
||||
BCS _HandleReadRegR9
|
||||
MOV R0,R8
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR9:
|
||||
CMP R1,#+10 // if (RegIndex < 10) { (R9)
|
||||
BCS _HandleReadRegR10
|
||||
MOV R0,R9
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR10:
|
||||
CMP R1,#+11 // if (RegIndex < 11) { (R10)
|
||||
BCS _HandleReadRegR11
|
||||
MOV R0,R10
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR11:
|
||||
CMP R1,#+12 // if (RegIndex < 12) { (R11)
|
||||
BCS _HandleReadRegR12
|
||||
MOV R0,R11
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR12:
|
||||
CMP R1,#+14 // if (RegIndex < 14) { (R12)
|
||||
BCS _HandleReadRegR14
|
||||
LDR R0,[R2, #+_APP_SP_OFF_R12]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR14:
|
||||
CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR)
|
||||
BCS _HandleReadRegR15
|
||||
LDR R0,[R2, #+_APP_SP_OFF_R14_LR]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR15:
|
||||
CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC)
|
||||
BCS _HandleReadRegXPSR
|
||||
LDR R0,[R2, #+_APP_SP_OFF_PC]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegXPSR:
|
||||
CMP R1,#+17 // if (RegIndex < 17) { (xPSR)
|
||||
BCS _HandleReadRegMSP
|
||||
LDR R0,[R2, #+_APP_SP_OFF_XPSR]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegMSP:
|
||||
/*
|
||||
Stackpointer is tricky because we need to get some info about the SP used in the user app, first
|
||||
|
||||
Handle reading R0-R3 which can be read right from application stackpointer
|
||||
|
||||
Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension
|
||||
LR Return to Return SP Frame type
|
||||
---------------------------------------------------------
|
||||
0xFFFFFFE1 Handler mode. MSP Extended
|
||||
0xFFFFFFE9 Thread mode MSP Extended
|
||||
0xFFFFFFED Thread mode PSP Extended
|
||||
0xFFFFFFF1 Handler mode. MSP Basic
|
||||
0xFFFFFFF9 Thread mode MSP Basic
|
||||
0xFFFFFFFD Thread mode PSP Basic
|
||||
|
||||
So LR[2] == 1 => Return stack == PSP else MSP
|
||||
Per architecture definition: Inside monitor (exception) SP = MSP
|
||||
|
||||
Stack pointer handling is complicated because it is different what is pushed on the stack before entering the monitor ISR...
|
||||
Cortex-M: 8 regs
|
||||
Cortex-M + forced-stack-alignment: 8 regs + 1 dummy-word if stack was not 8-byte aligned
|
||||
Cortex-M + FPU: 8 regs + 17 FPU regs + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned
|
||||
Cortex-M + FPU + lazy mode: 8 regs + 17 dummy-words + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned
|
||||
*/
|
||||
CMP R1,#+18 // if (RegIndex < 18) { (MSP)
|
||||
BCS _HandleReadRegPSP
|
||||
MRS R0,MSP
|
||||
LSRS R1,LR,#+3 // LR[2] -> Carry == 0 => CPU was running on MSP => Needs correction
|
||||
BCS _HandleReadRegDone_Veneer // CPU was running on PSP? => No correction necessary
|
||||
_HandleSPCorrection:
|
||||
LSRS R1,LR,#+5 // LR[4] -> Carry == 0 => extended stack frame has been allocated. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry
|
||||
ITE CS
|
||||
ADDCS R0,R0,#+_NUM_BYTES_BASIC_STACKFRAME
|
||||
ADDCC R0,R0,#+_NUM_BYTES_EXTENDED_STACKFRAME
|
||||
LDR R1,[R2, #+_APP_SP_OFF_XPSR] // Get xPSR from application stack (R2 has been set to app stack on beginning of _HandleReadReg)
|
||||
LSRS R1,R1,#+5 // xPSR[9] -> Carry == 1 => Stack has been force-aligned before pushing regs. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry
|
||||
IT CS
|
||||
ADDCS R0,R0,#+4
|
||||
B _HandleReadRegDone
|
||||
_HandleReadRegPSP: // RegIndex == 18
|
||||
CMP R1,#+19 // if (RegIndex < 19) {
|
||||
BCS _HandleReadRegCFBP
|
||||
MRS R0,PSP // PSP is not touched by monitor
|
||||
LSRS R1,LR,#+3 // LR[2] -> Carry == 1 => CPU was running on PSP => Needs correction
|
||||
BCC _HandleReadRegDone_Veneer // CPU was running on MSP? => No correction of PSP necessary
|
||||
B _HandleSPCorrection
|
||||
_HandleReadRegCFBP:
|
||||
/*
|
||||
CFBP is a register that can only be read via debug probe and is a merger of the following regs:
|
||||
CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode
|
||||
*/
|
||||
CMP R1,#+20 // if (RegIndex < 20) { (CFBP)
|
||||
BCS _HandleReadRegFPU
|
||||
MOVS R0,#+0
|
||||
MRS R2,PRIMASK
|
||||
ORRS R0,R2 // Merge PRIMASK into CFBP[7:0]
|
||||
MRS R2,BASEPRI
|
||||
LSLS R2,R2,#+8 // Merge BASEPRI into CFBP[15:8]
|
||||
ORRS R0,R2
|
||||
MRS R2,FAULTMASK
|
||||
LSLS R2,R2,#+16 // Merge FAULTMASK into CFBP[23:16]
|
||||
ORRS R0,R2
|
||||
MRS R2,CONTROL
|
||||
LSRS R1,LR,#3 // LR[2] -> Carry. CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior
|
||||
IT CS // As J-Link sees value of CONTROL at application time, we need reconstruct original value of CONTROL
|
||||
ORRCS R2,R2,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor
|
||||
LSRS R1,LR,#+5 // LR[4] == NOT(CONTROL.FPCA) -> Carry
|
||||
ITE CS // Merge original value of FPCA (CONTROL[2]) into read data
|
||||
BICCS R2,R2,#+0x04 // Remember LR contains NOT(CONTROL)
|
||||
ORRCC R2,R2,#+0x04
|
||||
LSLS R2,R2,#+24
|
||||
ORRS R0,R2
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegFPU:
|
||||
#if _HAS_FPU_REGS
|
||||
CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31)
|
||||
BCS _HandleReadRegDone_Veneer
|
||||
/*
|
||||
Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled
|
||||
If not, access to floating point is not possible
|
||||
CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
*/
|
||||
LDR R0,_AddrCPACR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+8
|
||||
LSRS R0,R0,#+28
|
||||
CMP R0,#+0xF
|
||||
BEQ _HandleReadRegFPU_Allowed
|
||||
CMP R0,#+0x5
|
||||
BNE _HandleReadRegDone_Veneer
|
||||
_HandleReadRegFPU_Allowed:
|
||||
CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR)
|
||||
BCS _HandleReadRegFPS0_FPS31
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleReadFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleReadFPSCRLazyMode
|
||||
LDR R0,[R2, #+_APP_SP_OFF_FPSCR]
|
||||
B _HandleReadRegDone
|
||||
_HandleReadFPSCRLazyMode:
|
||||
VMRS R0,FPSCR
|
||||
B _HandleReadRegDone
|
||||
_HandleReadRegFPS0_FPS31: // RegIndex == 21-52
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleReadFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleReadFPS0_FPS31LazyMode
|
||||
SUBS R1,#+21 // Convert absolute reg index into rel. one
|
||||
LSLS R1,R1,#+2 // RegIndex to position on stack
|
||||
ADDS R1,#+_APP_SP_OFF_S0
|
||||
LDR R0,[R2, R1]
|
||||
_HandleReadRegDone_Veneer:
|
||||
B _HandleReadRegDone
|
||||
_HandleReadFPS0_FPS31LazyMode:
|
||||
SUBS R1,#+20 // convert abs. RegIndex into rel. one
|
||||
MOVS R0,#+6
|
||||
MULS R1,R0,R1
|
||||
LDR R0,=_HandleReadRegUnknown
|
||||
SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1)
|
||||
ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr
|
||||
BX R0
|
||||
//
|
||||
// Table for reading FPS0-FPS31
|
||||
//
|
||||
VMOV R0,S31 // v = FPSx
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S30
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S29
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S28
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S27
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S26
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S25
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S24
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S23
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S22
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S21
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S20
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S19
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S18
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S17
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S16
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S15
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S14
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S13
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S12
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S11
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S10
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S9
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S8
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S7
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S6
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S5
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S4
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S3
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S2
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S1
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S0
|
||||
B _HandleReadRegDone
|
||||
#else
|
||||
B _HandleReadRegUnknown
|
||||
_HandleReadRegDone_Veneer:
|
||||
B _HandleReadRegDone
|
||||
#endif
|
||||
_HandleReadRegUnknown:
|
||||
MOVS R0,#+0 // v = 0
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegDone:
|
||||
|
||||
// Send register content to J-Link and wait until J-Link has read the data
|
||||
|
||||
STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // DCRDR = v;
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready
|
||||
B _WaitProbeReadIndicateMonRdy
|
||||
|
||||
// Data section for register addresses
|
||||
|
||||
_HandleWriteReg:
|
||||
/*
|
||||
_MON_CMD_WRITE_REG
|
||||
CPU: DEMCR[19] -> 1 => Mon ready
|
||||
J-Link: Data -> DCRDR, DEMCR[19] -> 0 => Data placed by probe
|
||||
CPU: DCRDR -> Read, Process command, DEMCR[19] -> 1 => Data read & mon ready
|
||||
|
||||
Register indexes
|
||||
0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!)
|
||||
16: XPSR
|
||||
17: MSP
|
||||
18: PSP
|
||||
19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
20: FPSCR
|
||||
21-52: FPS0-FPS31
|
||||
|
||||
|
||||
Register usage when entering this "subroutine":
|
||||
R0 Cmd
|
||||
R1 ParamInfo
|
||||
R2 ---
|
||||
R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset)
|
||||
R12 ---
|
||||
|
||||
Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension
|
||||
LR Return to Return SP Frame type
|
||||
---------------------------------------------------------
|
||||
0xFFFFFFE1 Handler mode. MSP Extended
|
||||
0xFFFFFFE9 Thread mode MSP Extended
|
||||
0xFFFFFFED Thread mode PSP Extended
|
||||
0xFFFFFFF1 Handler mode. MSP Basic
|
||||
0xFFFFFFF9 Thread mode MSP Basic
|
||||
0xFFFFFFFD Thread mode PSP Basic
|
||||
|
||||
So LR[2] == 1 => Return stack == PSP else MSP
|
||||
|
||||
R0-R3, R12, PC, xPSR can be written via application stackpointer
|
||||
Other regs can be written directly
|
||||
|
||||
|
||||
Read register data from J-Link into R0
|
||||
*/
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Monitor is ready to receive register data
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
_HandleWRegWaitUntilDataRecv:
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
LSLS R0,R0,#+12
|
||||
BMI.N _HandleWRegWaitUntilDataRecv // DEMCR[19] == 0 => J-Link has placed new data for us
|
||||
LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // Get register data
|
||||
//
|
||||
// Determine application SP
|
||||
//
|
||||
LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP
|
||||
ITE CS
|
||||
MRSCS R2,PSP
|
||||
MRSCC R2,MSP
|
||||
CMP R1,#+4 // if (RegIndex < 4) { (R0-R3)
|
||||
BCS _HandleWriteRegR4
|
||||
STR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3)
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR4:
|
||||
CMP R1,#+5 // if (RegIndex < 5) { (R4)
|
||||
BCS _HandleWriteRegR5
|
||||
MOV R4,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR5:
|
||||
CMP R1,#+6 // if (RegIndex < 6) { (R5)
|
||||
BCS _HandleWriteRegR6
|
||||
MOV R5,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR6:
|
||||
CMP R1,#+7 // if (RegIndex < 7) { (R6)
|
||||
BCS _HandleWriteRegR7
|
||||
MOV R6,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR7:
|
||||
CMP R1,#+8 // if (RegIndex < 8) { (R7)
|
||||
BCS _HandleWriteRegR8
|
||||
MOV R7,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR8:
|
||||
CMP R1,#+9 // if (RegIndex < 9) { (R8)
|
||||
BCS _HandleWriteRegR9
|
||||
MOV R8,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR9:
|
||||
CMP R1,#+10 // if (RegIndex < 10) { (R9)
|
||||
BCS _HandleWriteRegR10
|
||||
MOV R9,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR10:
|
||||
CMP R1,#+11 // if (RegIndex < 11) { (R10)
|
||||
BCS _HandleWriteRegR11
|
||||
MOV R10,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR11:
|
||||
CMP R1,#+12 // if (RegIndex < 12) { (R11)
|
||||
BCS _HandleWriteRegR12
|
||||
MOV R11,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR12:
|
||||
CMP R1,#+14 // if (RegIndex < 14) { (R12)
|
||||
BCS _HandleWriteRegR14
|
||||
STR R0,[R2, #+_APP_SP_OFF_R12]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR14:
|
||||
CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR)
|
||||
BCS _HandleWriteRegR15
|
||||
STR R0,[R2, #+_APP_SP_OFF_R14_LR]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR15:
|
||||
CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC)
|
||||
BCS _HandleWriteRegXPSR
|
||||
STR R0,[R2, #+_APP_SP_OFF_PC]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegXPSR:
|
||||
CMP R1,#+17 // if (RegIndex < 17) { (xPSR)
|
||||
BCS _HandleWriteRegMSP
|
||||
STR R0,[R2, #+_APP_SP_OFF_XPSR]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegMSP:
|
||||
//
|
||||
// For now, SP cannot be modified because it is needed to jump back from monitor mode
|
||||
//
|
||||
CMP R1,#+18 // if (RegIndex < 18) { (MSP)
|
||||
BCS _HandleWriteRegPSP
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegPSP: // RegIndex == 18
|
||||
CMP R1,#+19 // if (RegIndex < 19) {
|
||||
BCS _HandleWriteRegCFBP
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegCFBP:
|
||||
/*
|
||||
CFBP is a register that can only be read via debug probe and is a merger of the following regs:
|
||||
CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode
|
||||
*/
|
||||
CMP R1,#+20 // if (RegIndex < 20) { (CFBP)
|
||||
BCS _HandleWriteRegFPU
|
||||
LSLS R1,R0,#+24
|
||||
LSRS R1,R1,#+24 // Extract CFBP[7:0] => PRIMASK
|
||||
MSR PRIMASK,R1
|
||||
LSLS R1,R0,#+16
|
||||
LSRS R1,R1,#+24 // Extract CFBP[15:8] => BASEPRI
|
||||
MSR BASEPRI,R1
|
||||
LSLS R1,R0,#+8 // Extract CFBP[23:16] => FAULTMASK
|
||||
LSRS R1,R1,#+24
|
||||
MSR FAULTMASK,R1
|
||||
LSRS R1,R0,#+24 // Extract CFBP[31:24] => CONTROL
|
||||
LSRS R0,R1,#2 // Current CONTROL[1] -> Carry
|
||||
ITE CS // Update saved CONTROL.SPSEL (CONTROL[1]). CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior
|
||||
ORRCS LR,LR,#+4
|
||||
BICCC LR,LR,#+4
|
||||
BIC R1,R1,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor. Otherwise behavior is UNPREDICTABLE
|
||||
LSRS R0,R1,#+3 // New CONTROL.FPCA (CONTROL[2]) -> Carry
|
||||
ITE CS // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BICCS LR,LR,#+0x10 // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
ORRCC LR,LR,#+0x10
|
||||
MRS R0,CONTROL
|
||||
LSRS R0,R0,#+3 // CONTROL[2] -> Carry
|
||||
ITE CS // Preserve original value of current CONTROL[2]
|
||||
ORRCS R1,R1,#+0x04
|
||||
BICCC R1,R1,#+0x04
|
||||
MSR CONTROL,R1
|
||||
ISB // Necessary after writing to CONTROL, see ARM DDI0403D, B1.4.4 The special-purpose CONTROL register
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegFPU:
|
||||
#if _HAS_FPU_REGS
|
||||
CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31)
|
||||
BCS _HandleWriteRegDone_Veneer
|
||||
/*
|
||||
Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled
|
||||
If not, access to floating point is not possible
|
||||
CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
*/
|
||||
MOV R12,R0 // Save register data
|
||||
LDR R0,_AddrCPACR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+8
|
||||
LSRS R0,R0,#+28
|
||||
CMP R0,#+0xF
|
||||
BEQ _HandleWriteRegFPU_Allowed
|
||||
CMP R0,#+0x5
|
||||
BNE _HandleWriteRegDone_Veneer
|
||||
_HandleWriteRegFPU_Allowed:
|
||||
CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR)
|
||||
BCS _HandleWriteRegFPS0_FPS31
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleWriteFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleWriteFPSCRLazyMode
|
||||
STR R12,[R2, #+_APP_SP_OFF_FPSCR]
|
||||
B _HandleWriteRegDone
|
||||
_HandleWriteFPSCRLazyMode:
|
||||
VMSR FPSCR,R12
|
||||
B _HandleWriteRegDone
|
||||
_HandleWriteRegFPS0_FPS31: // RegIndex == 21-52
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleWriteFPS0_FPS31LazyMode
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleWriteFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
SUBS R1,#+21 // Convert absolute reg index into rel. one
|
||||
LSLS R1,R1,#+2 // RegIndex to position on stack
|
||||
ADDS R1,#+_APP_SP_OFF_S0
|
||||
STR R12,[R2, R1]
|
||||
_HandleWriteRegDone_Veneer:
|
||||
B _HandleWriteRegDone
|
||||
_HandleWriteFPS0_FPS31LazyMode:
|
||||
SUBS R1,#+20 // Convert abs. RegIndex into rel. one
|
||||
MOVS R0,#+6
|
||||
MULS R1,R0,R1
|
||||
LDR R0,=_HandleReadRegUnknown
|
||||
SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1)
|
||||
ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr
|
||||
BX R0
|
||||
//
|
||||
// Table for reading FPS0-FPS31
|
||||
//
|
||||
VMOV S31,R12 // v = FPSx
|
||||
B _HandleWriteRegDone
|
||||
VMOV S30,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S29,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S28,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S27,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S26,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S25,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S24,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S23,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S22,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S21,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S20,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S19,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S18,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S17,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S16,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S15,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S14,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S13,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S12,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S11,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S10,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S9,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S8,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S7,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S6,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S5,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S4,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S3,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S2,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S1,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S0,R12
|
||||
B _HandleWriteRegDone
|
||||
#else
|
||||
B _HandleWriteRegUnknown
|
||||
#endif
|
||||
_HandleWriteRegUnknown:
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegDone:
|
||||
B _IndicateMonReady // Indicate that monitor has read data, processed command and is ready for a new one
|
||||
.end
|
||||
/****** End Of File *************************************************/
|
280
core/embed/segger/SEGGER_MMD/SEGGER_HardFaultHandler.c
Normal file
280
core/embed/segger/SEGGER_MMD/SEGGER_HardFaultHandler.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 2014 - 2020 SEGGER Microcontroller GmbH *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
* *
|
||||
* All rights reserved. *
|
||||
* *
|
||||
* Redistribution and use in source and binary forms, with or *
|
||||
* without modification, are permitted provided that the following *
|
||||
* conditions are met: *
|
||||
* *
|
||||
* - Redistributions of source code must retain the above copyright *
|
||||
* notice, this list of conditions and the following disclaimer. *
|
||||
* *
|
||||
* - Neither the name of SEGGER Microcontroller GmbH *
|
||||
* nor the names of its contributors may be used to endorse or *
|
||||
* promote products derived from this software without specific *
|
||||
* prior written permission. *
|
||||
* *
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
|
||||
* DISCLAIMED. *
|
||||
* IN NO EVENT SHALL SEGGER Microcontroller GmbH BE LIABLE FOR *
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
|
||||
* DAMAGE. *
|
||||
* *
|
||||
**********************************************************************
|
||||
----------------------------------------------------------------------
|
||||
File : SEGGER_HardFaultHandler.c
|
||||
Purpose : Generic SEGGER HardFault handler for Cortex-M
|
||||
Literature:
|
||||
[1] Analyzing HardFaults on Cortex-M CPUs (https://www.segger.com/downloads/appnotes/AN00016_AnalyzingHardFaultsOnCortexM.pdf)
|
||||
|
||||
Additional information:
|
||||
This HardFault handler enables user-friendly analysis of hard faults
|
||||
in debug configurations.
|
||||
If a release configuration requires a HardFault handler,
|
||||
a specific HardFault handler should be included instead,
|
||||
which for example issues a reset or lits an error LED.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
#define SYSHND_CTRL (*(volatile unsigned int*) (0xE000ED24u)) // System Handler Control and State Register
|
||||
#define NVIC_MFSR (*(volatile unsigned char*) (0xE000ED28u)) // Memory Management Fault Status Register
|
||||
#define NVIC_BFSR (*(volatile unsigned char*) (0xE000ED29u)) // Bus Fault Status Register
|
||||
#define NVIC_UFSR (*(volatile unsigned short*)(0xE000ED2Au)) // Usage Fault Status Register
|
||||
#define NVIC_HFSR (*(volatile unsigned int*) (0xE000ED2Cu)) // Hard Fault Status Register
|
||||
#define NVIC_DFSR (*(volatile unsigned int*) (0xE000ED30u)) // Debug Fault Status Register
|
||||
#define NVIC_BFAR (*(volatile unsigned int*) (0xE000ED38u)) // Bus Fault Manage Address Register
|
||||
#define NVIC_AFSR (*(volatile unsigned int*) (0xE000ED3Cu)) // Auxiliary Fault Status Register
|
||||
|
||||
#ifndef DEBUG // Should be overwritten by project settings
|
||||
#define DEBUG (0) // in debug builds
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Prototypes
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void HardFaultHandler(unsigned int* pStack);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Static data
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
#if DEBUG
|
||||
static volatile unsigned int _Continue; // Set this variable to 1 to run further
|
||||
|
||||
static struct {
|
||||
struct {
|
||||
volatile unsigned int r0; // Register R0
|
||||
volatile unsigned int r1; // Register R1
|
||||
volatile unsigned int r2; // Register R2
|
||||
volatile unsigned int r3; // Register R3
|
||||
volatile unsigned int r12; // Register R12
|
||||
volatile unsigned int lr; // Link register
|
||||
volatile unsigned int pc; // Program counter
|
||||
union {
|
||||
volatile unsigned int byte;
|
||||
struct {
|
||||
unsigned int IPSR : 8; // Interrupt Program Status register (IPSR)
|
||||
unsigned int EPSR : 19; // Execution Program Status register (EPSR)
|
||||
unsigned int APSR : 5; // Application Program Status register (APSR)
|
||||
} bits;
|
||||
} psr; // Program status register.
|
||||
} SavedRegs;
|
||||
|
||||
union {
|
||||
volatile unsigned int byte;
|
||||
struct {
|
||||
unsigned int MEMFAULTACT : 1; // Read as 1 if memory management fault is active
|
||||
unsigned int BUSFAULTACT : 1; // Read as 1 if bus fault exception is active
|
||||
unsigned int UnusedBits1 : 1;
|
||||
unsigned int USGFAULTACT : 1; // Read as 1 if usage fault exception is active
|
||||
unsigned int UnusedBits2 : 3;
|
||||
unsigned int SVCALLACT : 1; // Read as 1 if SVC exception is active
|
||||
unsigned int MONITORACT : 1; // Read as 1 if debug monitor exception is active
|
||||
unsigned int UnusedBits3 : 1;
|
||||
unsigned int PENDSVACT : 1; // Read as 1 if PendSV exception is active
|
||||
unsigned int SYSTICKACT : 1; // Read as 1 if SYSTICK exception is active
|
||||
unsigned int USGFAULTPENDED : 1; // Usage fault pended; usage fault started but was replaced by a higher-priority exception
|
||||
unsigned int MEMFAULTPENDED : 1; // Memory management fault pended; memory management fault started but was replaced by a higher-priority exception
|
||||
unsigned int BUSFAULTPENDED : 1; // Bus fault pended; bus fault handler was started but was replaced by a higher-priority exception
|
||||
unsigned int SVCALLPENDED : 1; // SVC pended; SVC was started but was replaced by a higher-priority exception
|
||||
unsigned int MEMFAULTENA : 1; // Memory management fault handler enable
|
||||
unsigned int BUSFAULTENA : 1; // Bus fault handler enable
|
||||
unsigned int USGFAULTENA : 1; // Usage fault handler enable
|
||||
} bits;
|
||||
} syshndctrl; // System Handler Control and State Register (0xE000ED24)
|
||||
|
||||
union {
|
||||
volatile unsigned char byte;
|
||||
struct {
|
||||
unsigned char IACCVIOL : 1; // Instruction access violation
|
||||
unsigned char DACCVIOL : 1; // Data access violation
|
||||
unsigned char UnusedBits : 1;
|
||||
unsigned char MUNSTKERR : 1; // Unstacking error
|
||||
unsigned char MSTKERR : 1; // Stacking error
|
||||
unsigned char UnusedBits2 : 2;
|
||||
unsigned char MMARVALID : 1; // Indicates the MMAR is valid
|
||||
} bits;
|
||||
} mfsr; // Memory Management Fault Status Register (0xE000ED28)
|
||||
|
||||
union {
|
||||
volatile unsigned int byte;
|
||||
struct {
|
||||
unsigned int IBUSERR : 1; // Instruction access violation
|
||||
unsigned int PRECISERR : 1; // Precise data access violation
|
||||
unsigned int IMPREISERR : 1; // Imprecise data access violation
|
||||
unsigned int UNSTKERR : 1; // Unstacking error
|
||||
unsigned int STKERR : 1; // Stacking error
|
||||
unsigned int UnusedBits : 2;
|
||||
unsigned int BFARVALID : 1; // Indicates BFAR is valid
|
||||
} bits;
|
||||
} bfsr; // Bus Fault Status Register (0xE000ED29)
|
||||
volatile unsigned int bfar; // Bus Fault Manage Address Register (0xE000ED38)
|
||||
|
||||
union {
|
||||
volatile unsigned short byte;
|
||||
struct {
|
||||
unsigned short UNDEFINSTR : 1; // Attempts to execute an undefined instruction
|
||||
unsigned short INVSTATE : 1; // Attempts to switch to an invalid state (e.g., ARM)
|
||||
unsigned short INVPC : 1; // Attempts to do an exception with a bad value in the EXC_RETURN number
|
||||
unsigned short NOCP : 1; // Attempts to execute a coprocessor instruction
|
||||
unsigned short UnusedBits : 4;
|
||||
unsigned short UNALIGNED : 1; // Indicates that an unaligned access fault has taken place
|
||||
unsigned short DIVBYZERO : 1; // Indicates a divide by zero has taken place (can be set only if DIV_0_TRP is set)
|
||||
} bits;
|
||||
} ufsr; // Usage Fault Status Register (0xE000ED2A)
|
||||
|
||||
union {
|
||||
volatile unsigned int byte;
|
||||
struct {
|
||||
unsigned int UnusedBits : 1;
|
||||
unsigned int VECTBL : 1; // Indicates hard fault is caused by failed vector fetch
|
||||
unsigned int UnusedBits2 : 28;
|
||||
unsigned int FORCED : 1; // Indicates hard fault is taken because of bus fault/memory management fault/usage fault
|
||||
unsigned int DEBUGEVT : 1; // Indicates hard fault is triggered by debug event
|
||||
} bits;
|
||||
} hfsr; // Hard Fault Status Register (0xE000ED2C)
|
||||
|
||||
union {
|
||||
volatile unsigned int byte;
|
||||
struct {
|
||||
unsigned int HALTED : 1; // Halt requested in NVIC
|
||||
unsigned int BKPT : 1; // BKPT instruction executed
|
||||
unsigned int DWTTRAP : 1; // DWT match occurred
|
||||
unsigned int VCATCH : 1; // Vector fetch occurred
|
||||
unsigned int EXTERNAL : 1; // EDBGRQ signal asserted
|
||||
} bits;
|
||||
} dfsr; // Debug Fault Status Register (0xE000ED30)
|
||||
|
||||
volatile unsigned int afsr; // Auxiliary Fault Status Register (0xE000ED3C), Vendor controlled (optional)
|
||||
} HardFaultRegs;
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Global functions
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* HardFaultHandler()
|
||||
*
|
||||
* Function description
|
||||
* C part of the hard fault handler which is called by the assembler
|
||||
* function HardFault_Handler
|
||||
*/
|
||||
void HardFaultHandler(unsigned int* pStack) {
|
||||
//
|
||||
// In case we received a hard fault because of a breakpoint instruction, we return.
|
||||
// This may happen when using semihosting for printf outputs and no debugger is connected,
|
||||
// i.e. when running a "Debug" configuration in release mode.
|
||||
//
|
||||
if (NVIC_HFSR & (1u << 31)) {
|
||||
NVIC_HFSR |= (1u << 31); // Reset Hard Fault status
|
||||
*(pStack + 6u) += 2u; // PC is located on stack at SP + 24 bytes. Increment PC by 2 to skip break instruction.
|
||||
return; // Return to interrupted application
|
||||
}
|
||||
#if DEBUG
|
||||
//
|
||||
// Read NVIC registers
|
||||
//
|
||||
HardFaultRegs.syshndctrl.byte = SYSHND_CTRL; // System Handler Control and State Register
|
||||
HardFaultRegs.mfsr.byte = NVIC_MFSR; // Memory Fault Status Register
|
||||
HardFaultRegs.bfsr.byte = NVIC_BFSR; // Bus Fault Status Register
|
||||
HardFaultRegs.bfar = NVIC_BFAR; // Bus Fault Manage Address Register
|
||||
HardFaultRegs.ufsr.byte = NVIC_UFSR; // Usage Fault Status Register
|
||||
HardFaultRegs.hfsr.byte = NVIC_HFSR; // Hard Fault Status Register
|
||||
HardFaultRegs.dfsr.byte = NVIC_DFSR; // Debug Fault Status Register
|
||||
HardFaultRegs.afsr = NVIC_AFSR; // Auxiliary Fault Status Register
|
||||
//
|
||||
// Halt execution
|
||||
// If NVIC registers indicate readable memory, change the variable value to != 0 to continue execution.
|
||||
//
|
||||
_Continue = 0u;
|
||||
while (_Continue == 0u) {
|
||||
}
|
||||
//
|
||||
// Read saved registers from the stack.
|
||||
//
|
||||
HardFaultRegs.SavedRegs.r0 = pStack[0]; // Register R0
|
||||
HardFaultRegs.SavedRegs.r1 = pStack[1]; // Register R1
|
||||
HardFaultRegs.SavedRegs.r2 = pStack[2]; // Register R2
|
||||
HardFaultRegs.SavedRegs.r3 = pStack[3]; // Register R3
|
||||
HardFaultRegs.SavedRegs.r12 = pStack[4]; // Register R12
|
||||
HardFaultRegs.SavedRegs.lr = pStack[5]; // Link register LR
|
||||
HardFaultRegs.SavedRegs.pc = pStack[6]; // Program counter PC
|
||||
HardFaultRegs.SavedRegs.psr.byte = pStack[7]; // Program status word PSR
|
||||
//
|
||||
// Halt execution
|
||||
// To step out of the HardFaultHandler, change the variable value to != 0.
|
||||
//
|
||||
_Continue = 0u;
|
||||
while (_Continue == 0u) {
|
||||
}
|
||||
#else
|
||||
//
|
||||
// If this module is included in a release configuration, simply stay in the HardFault handler
|
||||
//
|
||||
(void)pStack;
|
||||
do {
|
||||
} while (1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************** End of file ****************************/
|
@ -15,6 +15,7 @@
|
||||
#define USE_SBU 1
|
||||
#define USE_RGB_COLORS 1
|
||||
#define USE_BACKLIGHT 1
|
||||
#define USE_BLE 1
|
||||
#endif
|
||||
|
||||
#ifdef TREZOR_MODEL_1
|
||||
|
@ -14,6 +14,7 @@
|
||||
#define USE_RGB_COLORS 1
|
||||
#define USE_BACKLIGHT 1
|
||||
#define USE_DISP_I8080_16BIT_DW 1
|
||||
#define USE_BLE 1
|
||||
|
||||
#define DISPLAY_PANEL_INIT_SEQ lhs200kb_if21_init_seq
|
||||
#define DISPLAY_PANEL_ROTATE lhs200kb_if21_rotate
|
||||
|
24
core/embed/trezorhal/boards/trezor_t3w1_d1_NRF.h
Normal file
24
core/embed/trezorhal/boards/trezor_t3w1_d1_NRF.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _TREZOR_T3W1_H
|
||||
#define _TREZOR_T3W1_H
|
||||
|
||||
// settings for nrf52
|
||||
#define SPI_SCK_PIN 14
|
||||
#define SPI_MISO_PIN 15
|
||||
#define SPI_MOSI_PIN 16
|
||||
#define SPI_SS_PIN 13
|
||||
|
||||
#define RX_PIN_NUMBER 26
|
||||
#define TX_PIN_NUMBER 27
|
||||
#define CTS_PIN_NUMBER 6
|
||||
#define RTS_PIN_NUMBER 8
|
||||
|
||||
#define LED_ORANGE_PIN 22
|
||||
#define LED_GREEN_PIN 24
|
||||
#define BUTTON_USER_PIN 21
|
||||
|
||||
#define STAY_BLD_PIN 40
|
||||
#define GPIO_1_PIN 28
|
||||
#define GPIO_2_PIN 29
|
||||
#define GPIO_3_PIN 30
|
||||
|
||||
#endif //_TREZOR_T3W1_H
|
421
core/embed/trezorhal/stm32f4/ble.c
Normal file
421
core/embed/trezorhal/stm32f4/ble.c
Normal file
@ -0,0 +1,421 @@
|
||||
/*
|
||||
* 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 STM32_HAL_H
|
||||
#include TREZOR_BOARD
|
||||
|
||||
#include "ble.h"
|
||||
#include <string.h>
|
||||
#include "ble/int_comm_defs.h"
|
||||
#include "ble/messages.h"
|
||||
#include "ble/state.h"
|
||||
#include "buffers.h"
|
||||
#include "irq.h"
|
||||
#include "supervise.h"
|
||||
|
||||
#define SPI_QUEUE_SIZE 16
|
||||
|
||||
static UART_HandleTypeDef urt;
|
||||
|
||||
static SPI_HandleTypeDef spi = {0};
|
||||
static DMA_HandleTypeDef spi_dma = {0};
|
||||
|
||||
typedef struct {
|
||||
uint8_t buffer[BLE_PACKET_SIZE];
|
||||
bool used;
|
||||
bool ready;
|
||||
} spi_buffer_t;
|
||||
|
||||
BUFFER_SECTION spi_buffer_t spi_queue[SPI_QUEUE_SIZE];
|
||||
static int head = 0, tail = 0;
|
||||
static bool overrun = false;
|
||||
volatile uint16_t overrun_count = 0;
|
||||
volatile uint16_t msg_cntr = 0;
|
||||
volatile uint16_t first_overrun_at = 0;
|
||||
|
||||
static uint8_t int_comm_buffer[USB_DATA_SIZE];
|
||||
static uint16_t int_comm_msg_len = 0;
|
||||
static uint8_t int_event_buffer[USB_DATA_SIZE];
|
||||
static uint16_t int_event_msg_len = 0;
|
||||
|
||||
void ble_comm_init(void) {
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
|
||||
// synchronization signals
|
||||
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
GPIO_InitStructure.Pull = GPIO_PULLUP;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStructure.Pin = GPIO_PIN_1;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
|
||||
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStructure.Pin = GPIO_PIN_12;
|
||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
|
||||
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
|
||||
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStructure.Pin = GPIO_1_PIN;
|
||||
HAL_GPIO_Init(GPIO_1_PORT, &GPIO_InitStructure);
|
||||
|
||||
__HAL_RCC_USART1_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||
|
||||
GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12;
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStructure.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStructure.Alternate = GPIO_AF7_USART1;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||
|
||||
urt.Init.Mode = UART_MODE_TX_RX;
|
||||
urt.Init.BaudRate = 1000000;
|
||||
urt.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
|
||||
urt.Init.OverSampling = UART_OVERSAMPLING_16;
|
||||
urt.Init.Parity = UART_PARITY_NONE, urt.Init.StopBits = UART_STOPBITS_1;
|
||||
urt.Init.WordLength = UART_WORDLENGTH_8B;
|
||||
urt.Instance = USART1;
|
||||
|
||||
HAL_UART_Init(&urt);
|
||||
|
||||
__HAL_RCC_DMA2_CLK_ENABLE();
|
||||
__HAL_RCC_SPI1_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOC_CLK_ENABLE();
|
||||
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
|
||||
GPIO_InitStructure.Pull = GPIO_NOPULL;
|
||||
GPIO_InitStructure.Alternate = GPIO_AF5_SPI1;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;
|
||||
GPIO_InitStructure.Pin = GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6;
|
||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||
GPIO_InitStructure.Pin = GPIO_PIN_5;
|
||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||||
// GPIO_InitStructure.Pin = GPIO_PIN_9;
|
||||
// HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||||
|
||||
spi_dma.Init.Channel = DMA_CHANNEL_3;
|
||||
spi_dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||
spi_dma.Init.PeriphInc = DMA_PINC_DISABLE;
|
||||
spi_dma.Init.MemInc = DMA_MINC_ENABLE;
|
||||
spi_dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
|
||||
spi_dma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
|
||||
spi_dma.Init.Mode = DMA_NORMAL;
|
||||
spi_dma.Init.Priority = DMA_PRIORITY_LOW;
|
||||
spi_dma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
|
||||
spi_dma.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
|
||||
spi_dma.Init.MemBurst = DMA_MBURST_SINGLE;
|
||||
spi_dma.Init.PeriphBurst = DMA_PBURST_SINGLE;
|
||||
spi_dma.Instance = DMA2_Stream0;
|
||||
HAL_DMA_Init(&spi_dma);
|
||||
|
||||
spi.Instance = SPI1;
|
||||
spi.Init.Mode = SPI_MODE_SLAVE;
|
||||
spi.Init.Direction = SPI_DIRECTION_2LINES; // rx only?
|
||||
spi.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||
spi.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||
spi.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||
spi.Init.NSS = SPI_NSS_HARD_INPUT;
|
||||
spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
|
||||
spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||
spi.Init.TIMode = SPI_TIMODE_DISABLE;
|
||||
spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||
spi.Init.CRCPolynomial = 0;
|
||||
spi.hdmarx = &spi_dma;
|
||||
|
||||
spi_dma.Parent = &spi;
|
||||
|
||||
HAL_SPI_Init(&spi);
|
||||
|
||||
set_initialized(false);
|
||||
|
||||
HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE);
|
||||
spi_queue[0].used = true;
|
||||
|
||||
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
|
||||
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
|
||||
GPIO_InitStructure.Alternate = 0;
|
||||
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStructure.Pin = GPIO_2_PIN;
|
||||
HAL_GPIO_Init(GPIO_2_PORT, &GPIO_InitStructure);
|
||||
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
void ble_start(void) {
|
||||
svc_enableIRQ(DMA2_Stream0_IRQn);
|
||||
memset(spi_queue, 0, sizeof(spi_queue));
|
||||
HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE);
|
||||
}
|
||||
|
||||
void ble_stop(void) {
|
||||
svc_disableIRQ(DMA2_Stream0_IRQn);
|
||||
HAL_SPI_DMAStop(&spi);
|
||||
}
|
||||
|
||||
void ble_comm_send(uint8_t *data, uint32_t len) {
|
||||
HAL_UART_Transmit(&urt, data, len, 30);
|
||||
}
|
||||
|
||||
uint32_t ble_comm_receive(uint8_t *data, uint32_t len) {
|
||||
if (urt.Instance->SR & USART_SR_RXNE) {
|
||||
HAL_StatusTypeDef result = HAL_UART_Receive(&urt, data, len, 30);
|
||||
|
||||
if (result == HAL_OK) {
|
||||
return len;
|
||||
} else {
|
||||
if (urt.RxXferCount == len) {
|
||||
return 0;
|
||||
}
|
||||
return len - urt.RxXferCount - 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type) {
|
||||
uint16_t msg_len = len + OVERHEAD_SIZE;
|
||||
uint8_t len_hi = msg_len >> 8;
|
||||
uint8_t len_lo = msg_len & 0xFF;
|
||||
uint8_t eom = EOM;
|
||||
|
||||
HAL_UART_Transmit(&urt, &message_type, 1, 1);
|
||||
HAL_UART_Transmit(&urt, &len_hi, 1, 1);
|
||||
HAL_UART_Transmit(&urt, &len_lo, 1, 1);
|
||||
|
||||
HAL_UART_Transmit(&urt, data, len, 10);
|
||||
HAL_UART_Transmit(&urt, &eom, 1, 1);
|
||||
}
|
||||
|
||||
void flush_line(void) {
|
||||
while (urt.Instance->SR & USART_SR_RXNE) {
|
||||
(void)urt.Instance->DR;
|
||||
}
|
||||
}
|
||||
|
||||
void ble_uart_receive(void) {
|
||||
if (urt.Instance->SR & USART_SR_RXNE) {
|
||||
uint8_t init_byte = 0;
|
||||
|
||||
HAL_UART_Receive(&urt, &init_byte, 1, 1);
|
||||
|
||||
if (init_byte == INTERNAL_EVENT || init_byte == INTERNAL_MESSAGE) {
|
||||
uint8_t len_hi = 0;
|
||||
uint8_t len_lo = 0;
|
||||
HAL_UART_Receive(&urt, &len_hi, 1, 1);
|
||||
HAL_UART_Receive(&urt, &len_lo, 1, 1);
|
||||
|
||||
uint16_t act_len = (len_hi << 8) | len_lo;
|
||||
|
||||
if (act_len > UART_PACKET_SIZE) {
|
||||
flush_line();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *data = NULL;
|
||||
uint16_t *len = NULL;
|
||||
if (init_byte == INTERNAL_EVENT) {
|
||||
data = int_event_buffer;
|
||||
len = &int_event_msg_len;
|
||||
} else if (init_byte == INTERNAL_MESSAGE) {
|
||||
data = int_comm_buffer;
|
||||
len = &int_comm_msg_len;
|
||||
} else {
|
||||
memset(data, 0, USB_DATA_SIZE);
|
||||
*len = 0;
|
||||
flush_line();
|
||||
return;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef result =
|
||||
HAL_UART_Receive(&urt, data, act_len - OVERHEAD_SIZE, 5);
|
||||
|
||||
if (result != HAL_OK) {
|
||||
memset(data, 0, USB_DATA_SIZE);
|
||||
*len = 0;
|
||||
flush_line();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t eom = 0;
|
||||
HAL_UART_Receive(&urt, &eom, 1, 1);
|
||||
|
||||
if (eom == EOM) {
|
||||
*len = act_len - OVERHEAD_SIZE;
|
||||
} else {
|
||||
memset(data, 0, USB_DATA_SIZE);
|
||||
*len = 0;
|
||||
flush_line();
|
||||
}
|
||||
} else {
|
||||
flush_line();
|
||||
}
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
void ble_event_poll() {
|
||||
ble_uart_receive();
|
||||
|
||||
if (int_event_msg_len > 0) {
|
||||
process_poll(int_event_buffer, int_event_msg_len);
|
||||
memset(int_event_buffer, 0, USB_DATA_SIZE);
|
||||
int_event_msg_len = 0;
|
||||
}
|
||||
|
||||
if (!ble_initialized() && !is_ble_dfu_mode() && ble_firmware_running()) {
|
||||
send_state_request();
|
||||
}
|
||||
}
|
||||
|
||||
bool ble_firmware_running(void) {
|
||||
return HAL_GPIO_ReadPin(GPIO_2_PORT, GPIO_2_PIN) != 0;
|
||||
}
|
||||
|
||||
uint32_t ble_int_event_receive(uint8_t *data, uint32_t len) {
|
||||
ble_uart_receive();
|
||||
if (int_event_msg_len > 0) {
|
||||
memcpy(data, int_event_buffer,
|
||||
int_event_msg_len > len ? len : int_event_msg_len);
|
||||
memset(int_event_buffer, 0, USB_DATA_SIZE);
|
||||
uint32_t res = int_event_msg_len;
|
||||
int_event_msg_len = 0;
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len) {
|
||||
ble_uart_receive();
|
||||
if (int_comm_msg_len > 0) {
|
||||
memcpy(data, int_comm_buffer,
|
||||
int_comm_msg_len > len ? len : int_comm_msg_len);
|
||||
memset(int_comm_buffer, 0, USB_DATA_SIZE);
|
||||
uint32_t res = int_comm_msg_len;
|
||||
int_comm_msg_len = 0;
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool start_spi_dma(void) {
|
||||
int tmp_tail = (tail + 1) % SPI_QUEUE_SIZE;
|
||||
if (spi_queue[tmp_tail].used || spi_queue[tmp_tail].ready) {
|
||||
overrun = true;
|
||||
overrun_count++;
|
||||
if (first_overrun_at == 0) {
|
||||
first_overrun_at = msg_cntr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
spi_queue[tmp_tail].used = true;
|
||||
HAL_SPI_Receive_DMA(&spi, spi_queue[tmp_tail].buffer, BLE_PACKET_SIZE);
|
||||
|
||||
tail = tmp_tail;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DMA2_Stream0_IRQHandler(void) {
|
||||
IRQ_ENTER(DMA2_Stream0_IRQn);
|
||||
|
||||
HAL_DMA_IRQHandler(&spi_dma);
|
||||
|
||||
IRQ_EXIT(DMA2_Stream0_IRQn);
|
||||
}
|
||||
|
||||
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||
spi_queue[tail].ready = true;
|
||||
msg_cntr++;
|
||||
start_spi_dma();
|
||||
}
|
||||
|
||||
uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len) {
|
||||
svc_disableIRQ(DMA2_Stream0_IRQn);
|
||||
if (spi_queue[head].ready) {
|
||||
uint8_t *buffer = (uint8_t *)spi_queue[head].buffer;
|
||||
memcpy(data, buffer, len > BLE_PACKET_SIZE ? BLE_PACKET_SIZE : len);
|
||||
|
||||
spi_queue[head].used = false;
|
||||
spi_queue[head].ready = false;
|
||||
head = (head + 1) % SPI_QUEUE_SIZE;
|
||||
|
||||
if (overrun && start_spi_dma()) {
|
||||
// overrun was before, need to restart the DMA
|
||||
overrun = false;
|
||||
}
|
||||
|
||||
if (data[0] != '?') {
|
||||
// bad packet, restart the DMA
|
||||
HAL_SPI_Abort(&spi);
|
||||
|
||||
memset(spi_queue, 0, sizeof(spi_queue));
|
||||
head = 0;
|
||||
tail = 0;
|
||||
overrun = false;
|
||||
HAL_SPI_Receive_DMA(&spi, spi_queue[0].buffer, BLE_PACKET_SIZE);
|
||||
spi_queue[0].used = true;
|
||||
// todo return error?
|
||||
svc_enableIRQ(DMA2_Stream0_IRQn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
svc_enableIRQ(DMA2_Stream0_IRQn);
|
||||
return len > BLE_PACKET_SIZE ? BLE_PACKET_SIZE : len;
|
||||
}
|
||||
|
||||
svc_enableIRQ(DMA2_Stream0_IRQn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ble_reset_to_bootloader(void) {
|
||||
uint32_t tick_start = 0;
|
||||
|
||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
|
||||
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
|
||||
|
||||
HAL_Delay(10);
|
||||
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
|
||||
|
||||
tick_start = HAL_GetTick();
|
||||
|
||||
while (HAL_GPIO_ReadPin(GPIO_1_PORT, GPIO_1_PIN) == GPIO_PIN_RESET) {
|
||||
if (HAL_GetTick() - tick_start > 4000) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
|
||||
|
||||
HAL_Delay(1000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ble_reset(void) {
|
||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
|
||||
HAL_Delay(50);
|
||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
|
||||
return true;
|
||||
}
|
30
core/embed/trezorhal/stm32f4/ble.h
Normal file
30
core/embed/trezorhal/stm32f4/ble.h
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
#ifndef __BLE_COMM_H__
|
||||
#define __BLE_COMM_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "ble/int_comm_defs.h"
|
||||
|
||||
void ble_comm_init(void);
|
||||
|
||||
void ble_start(void);
|
||||
void ble_stop(void);
|
||||
|
||||
void ble_comm_send(uint8_t *data, uint32_t len);
|
||||
uint32_t ble_comm_receive(uint8_t *data, uint32_t len);
|
||||
|
||||
void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type);
|
||||
uint32_t ble_int_event_receive(uint8_t *data, uint32_t len);
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len);
|
||||
uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len);
|
||||
|
||||
void ble_event_poll(void);
|
||||
|
||||
bool ble_firmware_running(void);
|
||||
|
||||
bool ble_reset_to_bootloader(void);
|
||||
|
||||
bool ble_reset(void);
|
||||
|
||||
#endif
|
@ -187,6 +187,15 @@ secbool usb_configured(void) {
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
secbool usb_configured_now(void) {
|
||||
const USBD_HandleTypeDef *pdev = &usb_dev_handle;
|
||||
if (pdev->dev_state == USBD_STATE_SUSPENDED &&
|
||||
pdev->dev_old_state == USBD_STATE_CONFIGURED) {
|
||||
return sectrue;
|
||||
}
|
||||
return secfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility functions for USB interfaces
|
||||
*/
|
||||
|
29
core/embed/trezorhal/unix/ble.c
Normal file
29
core/embed/trezorhal/unix/ble.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "ble.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static bool firmware_running = true;
|
||||
|
||||
void ble_comm_init(void) {}
|
||||
|
||||
void ble_comm_send(uint8_t *data, uint32_t len) {}
|
||||
|
||||
uint32_t ble_comm_receive(uint8_t *data, uint32_t len) { return 0; }
|
||||
|
||||
void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type) {}
|
||||
uint32_t ble_int_event_receive(uint8_t *data, uint32_t len) { return 0; }
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len) { return 0; }
|
||||
uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len) { return 0; }
|
||||
|
||||
void ble_event_poll(void) {}
|
||||
|
||||
bool ble_firmware_running(void) { return firmware_running; }
|
||||
|
||||
bool ble_reset_to_bootloader(void) {
|
||||
firmware_running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ble_reset(void) {
|
||||
firmware_running = true;
|
||||
return true;
|
||||
}
|
19
core/embed/trezorhal/unix/ble.h
Normal file
19
core/embed/trezorhal/unix/ble.h
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void ble_comm_init(void);
|
||||
|
||||
void ble_comm_send(uint8_t *data, uint32_t len);
|
||||
uint32_t ble_comm_receive(uint8_t *data, uint32_t len);
|
||||
|
||||
void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type);
|
||||
uint32_t ble_int_event_receive(uint8_t *data, uint32_t len);
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len);
|
||||
uint32_t ble_ext_comm_receive(uint8_t *data, uint32_t len);
|
||||
|
||||
void ble_event_poll(void);
|
||||
|
||||
bool ble_firmware_running(void);
|
||||
|
||||
bool ble_reset_to_bootloader(void);
|
||||
bool ble_reset(void);
|
@ -144,5 +144,6 @@ void usb_deinit(void);
|
||||
void usb_start(void);
|
||||
void usb_stop(void);
|
||||
secbool usb_configured(void);
|
||||
secbool usb_configured_now(void);
|
||||
|
||||
#endif
|
||||
|
@ -190,7 +190,7 @@ class WebUSB:
|
||||
"""
|
||||
Sends message using USB WebUSB (device) or UDP (emulator).
|
||||
"""
|
||||
from . import fatfs, sdcard
|
||||
from . import fatfs, sdcard, ble
|
||||
POLL_READ: int # wait until interface is readable and return read data
|
||||
POLL_WRITE: int # wait until interface is writable
|
||||
INPUT: int # interface id of unified input events
|
||||
@ -202,4 +202,5 @@ BUTTON_RELEASED: int # button up event
|
||||
BUTTON_LEFT: int # button number of left button
|
||||
BUTTON_RIGHT: int # button number of right button
|
||||
USB_CHECK: int # interface id for check of USB data connection
|
||||
WireInterface = Union[HID, WebUSB]
|
||||
BLE_CHECK: int # interface id for check of BLE data connection
|
||||
WireInterface = Union[HID, WebUSB, BleInterface]
|
||||
|
43
core/mocks/generated/trezorio/ble.pyi
Normal file
43
core/mocks/generated/trezorio/ble.pyi
Normal file
@ -0,0 +1,43 @@
|
||||
from typing import *
|
||||
|
||||
|
||||
# extmod/modtrezorio/modtrezorio-ble.h
|
||||
def update_init(data: bytes, binsize: int) -> int:
|
||||
"""
|
||||
Initializes the BLE firmware update
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorio/modtrezorio-ble.h
|
||||
def update_chunk(chunk: bytes) -> int:
|
||||
"""
|
||||
Writes next chunk of BLE firmware update
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorio/modtrezorio-ble.h
|
||||
def write_int(self, msg: bytes) -> int:
|
||||
"""
|
||||
Sends internal message to NRF.
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorio/modtrezorio-ble.h
|
||||
def write_ext(self, msg: bytes) -> int:
|
||||
"""
|
||||
Sends message over BLE
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorio/modtrezorio-ble.h
|
||||
def erase_bonds() -> None:
|
||||
"""
|
||||
Erases all BLE bonds
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorio/modtrezorio-ble.h
|
||||
def disconnect() -> None:
|
||||
"""
|
||||
Disconnect BLE
|
||||
"""
|
@ -95,6 +95,7 @@ VERSION_MINOR: int
|
||||
"""Minor version."""
|
||||
VERSION_PATCH: int
|
||||
"""Patch version."""
|
||||
USE_BLE: bool
|
||||
USE_SD_CARD: bool
|
||||
"""Whether the hardware supports SD card."""
|
||||
USE_BACKLIGHT: bool
|
||||
|
@ -31,6 +31,7 @@ def stm32f4_common_files(env, defines, sources, paths):
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_sdram.c",
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c",
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim_ex.c",
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c",
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_fmc.c",
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_ll_sdmmc.c",
|
||||
]
|
||||
|
@ -56,7 +56,15 @@ def configure(
|
||||
sources += ["embed/extmod/modtrezorio/ffunicode.c"]
|
||||
features_available.append("sd_card")
|
||||
|
||||
if "sd_card" in features_wanted:
|
||||
if "ble" in features_wanted:
|
||||
sources += ["embed/trezorhal/stm32f4/ble.c"]
|
||||
sources += ["embed/lib/ble/dfu.c"]
|
||||
sources += ["embed/lib/ble/fwu.c"]
|
||||
sources += ["embed/lib/ble/state.c"]
|
||||
sources += ["embed/lib/ble/messages.c"]
|
||||
features_available.append("ble")
|
||||
|
||||
if "ble" in features_wanted or "sd_card" in features_wanted:
|
||||
sources += [
|
||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c"
|
||||
]
|
||||
|
@ -3,8 +3,6 @@ from __future__ import annotations
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import subprocess
|
||||
|
||||
from boards import (
|
||||
discovery,
|
||||
trezor_1,
|
||||
@ -59,7 +57,7 @@ def configure_board(
|
||||
elif model_r_version == 10:
|
||||
return trezor_r_v10.configure(env, features_wanted, defines, sources, paths)
|
||||
raise Exception("Unknown model_r_version")
|
||||
elif model in ('T3W1',):
|
||||
elif model in ("T3W1",):
|
||||
return trezor_t3w1_d1.configure(env, features_wanted, defines, sources, paths)
|
||||
elif model in ("DISC1",):
|
||||
return discovery.configure(env, features_wanted, defines, sources, paths)
|
||||
@ -104,14 +102,14 @@ def get_version_int(file):
|
||||
patch = 0
|
||||
|
||||
file = PROJECT_ROOT / file
|
||||
with open(file, 'r') as f:
|
||||
with open(file, "r") as f:
|
||||
for line in f:
|
||||
if line.startswith('#define VERSION_MAJOR '):
|
||||
major = int(line.split('VERSION_MAJOR')[1].strip())
|
||||
if line.startswith('#define VERSION_MINOR '):
|
||||
minor = int(line.split('VERSION_MINOR')[1].strip())
|
||||
if line.startswith('#define VERSION_PATCH '):
|
||||
patch = int(line.split('VERSION_PATCH')[1].strip())
|
||||
if line.startswith("#define VERSION_MAJOR "):
|
||||
major = int(line.split("VERSION_MAJOR")[1].strip())
|
||||
if line.startswith("#define VERSION_MINOR "):
|
||||
minor = int(line.split("VERSION_MINOR")[1].strip())
|
||||
if line.startswith("#define VERSION_PATCH "):
|
||||
patch = int(line.split("VERSION_PATCH")[1].strip())
|
||||
if major > 99 or minor > 99 or patch > 99:
|
||||
raise Exception("Version number too large")
|
||||
return major * 10000 + minor * 100 + patch
|
||||
|
@ -33,10 +33,14 @@ from trezor import utils
|
||||
|
||||
all_modules
|
||||
import all_modules
|
||||
bluetooth
|
||||
import bluetooth
|
||||
boot
|
||||
import boot
|
||||
main
|
||||
import main
|
||||
mutex
|
||||
import mutex
|
||||
session
|
||||
import session
|
||||
typing
|
||||
@ -319,6 +323,18 @@ apps.management.backup_device
|
||||
import apps.management.backup_device
|
||||
apps.management.backup_types
|
||||
import apps.management.backup_types
|
||||
apps.management.ble.comparison_request
|
||||
import apps.management.ble.comparison_request
|
||||
apps.management.ble.disconnect
|
||||
import apps.management.ble.disconnect
|
||||
apps.management.ble.erase_bonds
|
||||
import apps.management.ble.erase_bonds
|
||||
apps.management.ble.pairing_request
|
||||
import apps.management.ble.pairing_request
|
||||
apps.management.ble.repair_request
|
||||
import apps.management.ble.repair_request
|
||||
apps.management.ble.upload_ble_firmware_init
|
||||
import apps.management.ble.upload_ble_firmware_init
|
||||
apps.management.change_pin
|
||||
import apps.management.change_pin
|
||||
apps.management.change_wipe_code
|
||||
|
@ -333,7 +333,7 @@ def set_homescreen() -> None:
|
||||
def lock_device(interrupt_workflow: bool = True) -> None:
|
||||
if config.has_pin():
|
||||
config.lock()
|
||||
wire.find_handler = get_pinlocked_handler
|
||||
wire.common_find_handler.register_find_handler(get_pinlocked_handler)
|
||||
set_homescreen()
|
||||
if interrupt_workflow:
|
||||
workflow.close_others()
|
||||
@ -344,7 +344,7 @@ def lock_device_if_unlocked() -> None:
|
||||
lock_device(interrupt_workflow=workflow.autolock_interrupts_workflow)
|
||||
|
||||
|
||||
async def unlock_device() -> None:
|
||||
async def unlock_device(skip_button_request: bool = False) -> None:
|
||||
"""Ensure the device is in unlocked state.
|
||||
|
||||
If the storage is locked, attempt to unlock it. Reset the homescreen and the wire
|
||||
@ -354,10 +354,13 @@ async def unlock_device() -> None:
|
||||
|
||||
if not config.is_unlocked():
|
||||
# verify_user_pin will raise if the PIN was invalid
|
||||
await verify_user_pin()
|
||||
await verify_user_pin(skip_button_request=skip_button_request)
|
||||
|
||||
set_homescreen()
|
||||
wire.find_handler = workflow_handlers.find_registered_handler
|
||||
|
||||
wire.common_find_handler.register_find_handler(
|
||||
workflow_handlers.find_registered_handler
|
||||
)
|
||||
|
||||
|
||||
def get_pinlocked_handler(
|
||||
@ -413,7 +416,10 @@ def boot() -> None:
|
||||
workflow_handlers.register(msg_type, handler) # type: ignore [cannot be assigned to type]
|
||||
|
||||
reload_settings_from_storage()
|
||||
|
||||
if config.is_unlocked():
|
||||
wire.find_handler = workflow_handlers.find_registered_handler
|
||||
wire.common_find_handler.register_find_handler(
|
||||
workflow_handlers.find_registered_handler
|
||||
)
|
||||
else:
|
||||
wire.find_handler = get_pinlocked_handler
|
||||
wire.common_find_handler.register_find_handler(get_pinlocked_handler)
|
||||
|
@ -85,6 +85,7 @@ async def verify_user_pin(
|
||||
allow_cancel: bool = True,
|
||||
retry: bool = True,
|
||||
cache_time_ms: int = 0,
|
||||
skip_button_request: bool = False,
|
||||
) -> None:
|
||||
# _get_last_unlock_time
|
||||
last_unlock = int.from_bytes(
|
||||
@ -102,7 +103,12 @@ async def verify_user_pin(
|
||||
if config.has_pin():
|
||||
from trezor.ui.layouts import request_pin_on_device
|
||||
|
||||
pin = await request_pin_on_device(prompt, config.get_pin_rem(), allow_cancel)
|
||||
pin = await request_pin_on_device(
|
||||
prompt,
|
||||
config.get_pin_rem(),
|
||||
allow_cancel,
|
||||
skip_button_request=skip_button_request,
|
||||
)
|
||||
config.ensure_not_wipe_code(pin)
|
||||
else:
|
||||
pin = ""
|
||||
@ -116,7 +122,11 @@ async def verify_user_pin(
|
||||
|
||||
while retry:
|
||||
pin = await request_pin_on_device( # type: ignore ["request_pin_on_device" is possibly unbound]
|
||||
"Enter PIN", config.get_pin_rem(), allow_cancel, wrong_pin=True
|
||||
"Enter PIN",
|
||||
config.get_pin_rem(),
|
||||
allow_cancel,
|
||||
wrong_pin=True,
|
||||
skip_button_request=skip_button_request,
|
||||
)
|
||||
if config.unlock(pin, salt):
|
||||
_set_last_unlock_time()
|
||||
|
15
core/src/apps/management/ble/comparison_request.py
Normal file
15
core/src/apps/management/ble/comparison_request.py
Normal file
@ -0,0 +1,15 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import ComparisonRequest, Success
|
||||
|
||||
|
||||
async def comparison_request(msg: ComparisonRequest) -> Success:
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.layouts import confirm_action
|
||||
|
||||
await confirm_action(
|
||||
None, "DO THE NUMBERS MATCH?", description=msg.key.decode("utf-8")
|
||||
)
|
||||
|
||||
return Success()
|
16
core/src/apps/management/ble/disconnect.py
Normal file
16
core/src/apps/management/ble/disconnect.py
Normal file
@ -0,0 +1,16 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import Disconnect, Success
|
||||
|
||||
|
||||
async def disconnect(_msg: Disconnect) -> Success:
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.layouts import confirm_action
|
||||
|
||||
await confirm_action(None, "DISCONNECT")
|
||||
|
||||
ble.disconnect()
|
||||
|
||||
return Success()
|
16
core/src/apps/management/ble/erase_bonds.py
Normal file
16
core/src/apps/management/ble/erase_bonds.py
Normal file
@ -0,0 +1,16 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import EraseBonds, Success
|
||||
|
||||
|
||||
async def erase_bonds(_msg: EraseBonds) -> Success:
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.layouts import confirm_action
|
||||
|
||||
await confirm_action(None, "ERASE BONDS")
|
||||
|
||||
ble.erase_bonds()
|
||||
|
||||
return Success()
|
16
core/src/apps/management/ble/pairing_request.py
Normal file
16
core/src/apps/management/ble/pairing_request.py
Normal file
@ -0,0 +1,16 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import AuthKey, PairingRequest
|
||||
|
||||
|
||||
async def pairing_request(_msg: PairingRequest) -> AuthKey:
|
||||
from trezor.messages import AuthKey
|
||||
from trezor.ui.layouts import request_pin_on_device
|
||||
|
||||
pin = await request_pin_on_device("PAIRING", None, True, False, True)
|
||||
|
||||
if len(pin) != 6:
|
||||
pin = "000000"
|
||||
|
||||
return AuthKey(key=pin.encode())
|
13
core/src/apps/management/ble/repair_request.py
Normal file
13
core/src/apps/management/ble/repair_request.py
Normal file
@ -0,0 +1,13 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import RepairRequest, Success
|
||||
|
||||
|
||||
async def repair_request(_msg: RepairRequest) -> Success:
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.layouts import confirm_action
|
||||
|
||||
await confirm_action(None, "RE-PAIR DEVICE")
|
||||
|
||||
return Success()
|
75
core/src/apps/management/ble/upload_ble_firmware_init.py
Normal file
75
core/src/apps/management/ble/upload_ble_firmware_init.py
Normal file
@ -0,0 +1,75 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import Success, UploadBLEFirmwareChunk, UploadBLEFirmwareInit
|
||||
|
||||
|
||||
async def upload_ble_firmware_chunk(msg: UploadBLEFirmwareChunk) -> int:
|
||||
result = ble.update_chunk(msg.data)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
async def upload_ble_firmware_init(msg: UploadBLEFirmwareInit) -> Success:
|
||||
from trezor.enums import ButtonRequestType
|
||||
from trezor.messages import (
|
||||
Success,
|
||||
UploadBLEFirmwareChunk,
|
||||
UploadBLEFirmwareNextChunk,
|
||||
)
|
||||
from trezor.ui.layouts import confirm_action
|
||||
|
||||
await confirm_action(
|
||||
"confirm_upload_ble_firmware",
|
||||
"Upload BLE firmware",
|
||||
"",
|
||||
"Update BLE FW?\n",
|
||||
reverse=True,
|
||||
verb="Confirm",
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
from trezor.enums import MessageType
|
||||
from trezor.ui.layouts import progress
|
||||
from trezor.wire.context import get_context
|
||||
|
||||
ctx = get_context()
|
||||
|
||||
progress_layout = progress.progress("Uploading...")
|
||||
|
||||
upload_progress = 0
|
||||
|
||||
p = int(1000 * upload_progress / msg.binsize)
|
||||
progress_layout.report(p)
|
||||
|
||||
res = ble.update_init(msg.init_data, msg.binsize)
|
||||
|
||||
await ctx.write(UploadBLEFirmwareNextChunk(offset=0))
|
||||
|
||||
if res == 0:
|
||||
|
||||
while True:
|
||||
|
||||
received_msg = await ctx.read(
|
||||
(MessageType.UploadBLEFirmwareChunk,),
|
||||
UploadBLEFirmwareChunk,
|
||||
)
|
||||
|
||||
result = await upload_ble_firmware_chunk(received_msg)
|
||||
|
||||
upload_progress += len(received_msg.data)
|
||||
p = int(1000 * upload_progress / msg.binsize)
|
||||
progress_layout.report(p)
|
||||
|
||||
if result == 0:
|
||||
result_msg = UploadBLEFirmwareNextChunk(offset=0)
|
||||
await ctx.write(result_msg)
|
||||
del (result_msg, received_msg)
|
||||
else:
|
||||
del received_msg
|
||||
break
|
||||
|
||||
progress_layout.report(1000)
|
||||
|
||||
return Success(message="BLE firmware update successful")
|
@ -1,7 +1,7 @@
|
||||
def boot() -> None:
|
||||
def boot(mutex) -> None:
|
||||
import usb
|
||||
from trezor import loop
|
||||
|
||||
from .fido2 import handle_reports
|
||||
|
||||
loop.schedule(handle_reports(usb.iface_webauthn))
|
||||
loop.schedule(handle_reports(usb.iface_webauthn, mutex))
|
||||
|
@ -539,7 +539,7 @@ def send_cmd_sync(cmd: Cmd, iface: HID) -> None:
|
||||
seq += 1
|
||||
|
||||
|
||||
async def handle_reports(iface: HID) -> None:
|
||||
async def handle_reports(iface: HID, mutex) -> None:
|
||||
dialog_mgr = DialogManager(iface)
|
||||
|
||||
while True:
|
||||
@ -547,10 +547,14 @@ async def handle_reports(iface: HID) -> None:
|
||||
req = await _read_cmd(iface)
|
||||
if req is None:
|
||||
continue
|
||||
if not dialog_mgr.allow_cid(req.cid):
|
||||
if mutex is not None and mutex.get_busy(iface.iface_num()):
|
||||
resp: Cmd | None = cmd_error(req.cid, _ERR_CHANNEL_BUSY)
|
||||
else:
|
||||
resp = _dispatch_cmd(req, dialog_mgr)
|
||||
mutex.set_busy(iface.iface_num())
|
||||
if not dialog_mgr.allow_cid(req.cid):
|
||||
resp: Cmd | None = cmd_error(req.cid, _ERR_CHANNEL_BUSY)
|
||||
else:
|
||||
resp = _dispatch_cmd(req, dialog_mgr)
|
||||
if resp is not None:
|
||||
await send_cmd(resp, iface)
|
||||
except Exception as e:
|
||||
|
@ -14,7 +14,7 @@ def register(wire_type: int, handler: Handler[Msg]) -> None:
|
||||
workflow_handlers[wire_type] = handler
|
||||
|
||||
|
||||
def _find_message_handler_module(msg_type: int) -> str:
|
||||
def _find_message_handler_module(msg_type: int, iface: WireInterface) -> str:
|
||||
"""Statically find the appropriate workflow handler.
|
||||
|
||||
For now, new messages must be registered by hand in the if-elif manner below.
|
||||
@ -62,6 +62,14 @@ def _find_message_handler_module(msg_type: int) -> str:
|
||||
if utils.USE_OPTIGA and msg_type == MessageType.AuthenticateDevice:
|
||||
return "apps.management.authenticate_device"
|
||||
|
||||
if utils.USE_BLE:
|
||||
if msg_type == MessageType.UploadBLEFirmwareInit:
|
||||
return "apps.management.ble.upload_ble_firmware_init"
|
||||
if msg_type == MessageType.EraseBonds:
|
||||
return "apps.management.ble.erase_bonds"
|
||||
if msg_type == MessageType.Disconnect:
|
||||
return "apps.management.ble.disconnect"
|
||||
|
||||
# bitcoin
|
||||
if msg_type == MessageType.AuthorizeCoinJoin:
|
||||
return "apps.bitcoin.authorize_coinjoin"
|
||||
@ -199,7 +207,7 @@ def find_registered_handler(iface: WireInterface, msg_type: int) -> Handler | No
|
||||
return workflow_handlers[msg_type]
|
||||
|
||||
try:
|
||||
modname = _find_message_handler_module(msg_type)
|
||||
modname = _find_message_handler_module(msg_type, iface)
|
||||
handler_name = modname[modname.rfind(".") + 1 :]
|
||||
module = __import__(modname, None, None, (handler_name,), 0)
|
||||
return getattr(module, handler_name)
|
||||
|
72
core/src/bluetooth.py
Normal file
72
core/src/bluetooth.py
Normal file
@ -0,0 +1,72 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import config
|
||||
|
||||
from apps.base import unlock_device
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor import protobuf, wire
|
||||
|
||||
|
||||
class BleInterfaceInternal:
|
||||
def iface_num(self) -> int:
|
||||
return 16
|
||||
|
||||
def write(self, msg: bytes) -> int:
|
||||
return ble.write_int(self, msg)
|
||||
|
||||
|
||||
class BleInterfaceExternal:
|
||||
def iface_num(self) -> int:
|
||||
return 17
|
||||
|
||||
def write(self, msg: bytes) -> int:
|
||||
return ble.write_ext(self, msg)
|
||||
|
||||
|
||||
def find_ble_int_handler(iface, msg_type) -> wire.Handler | None:
|
||||
from trezor.enums import MessageType
|
||||
|
||||
modname = None
|
||||
|
||||
if msg_type == MessageType.PairingRequest:
|
||||
modname = "apps.management.ble.pairing_request"
|
||||
if msg_type == MessageType.RepairRequest:
|
||||
modname = "apps.management.ble.repair_request"
|
||||
if msg_type == MessageType.ComparisonRequest:
|
||||
modname = "apps.management.ble.comparison_request"
|
||||
|
||||
if modname is not None:
|
||||
try:
|
||||
handler_name = modname[modname.rfind(".") + 1 :]
|
||||
module = __import__(modname, None, None, (handler_name,), 0)
|
||||
return getattr(module, handler_name)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def int_find_handler(
|
||||
iface: wire.WireInterface, msg_type: int
|
||||
) -> wire.Handler[wire.Msg] | None:
|
||||
|
||||
orig_handler = find_ble_int_handler(iface, msg_type)
|
||||
|
||||
if config.is_unlocked():
|
||||
return orig_handler
|
||||
else:
|
||||
if orig_handler is None:
|
||||
return None
|
||||
|
||||
async def wrapper(msg: wire.Msg) -> protobuf.MessageType:
|
||||
await unlock_device(True)
|
||||
return await orig_handler(msg)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
# interface used for trezor wire protocol
|
||||
iface_ble_int = BleInterfaceInternal()
|
||||
iface_ble_ext = BleInterfaceExternal()
|
23
core/src/mutex.py
Normal file
23
core/src/mutex.py
Normal file
@ -0,0 +1,23 @@
|
||||
class Mutex:
|
||||
def __init__(self):
|
||||
self.ifaces = []
|
||||
self.busy = None
|
||||
|
||||
def add(self, iface_num: int):
|
||||
if iface_num not in self.ifaces:
|
||||
self.ifaces.append(iface_num)
|
||||
|
||||
def set_busy(self, iface_num: int):
|
||||
if iface_num in self.ifaces:
|
||||
self.busy = iface_num
|
||||
|
||||
def get_busy(self, iface_num: int) -> int:
|
||||
return (
|
||||
iface_num in self.ifaces
|
||||
and self.busy is not None
|
||||
and self.busy != iface_num
|
||||
)
|
||||
|
||||
def release(self, iface_num: int):
|
||||
if iface_num == self.busy:
|
||||
self.busy = None
|
@ -1,29 +1,67 @@
|
||||
# isort: skip_file
|
||||
from micropython import const
|
||||
from mutex import Mutex
|
||||
|
||||
from trezor import log, loop, utils, wire, workflow
|
||||
|
||||
import apps.base
|
||||
import usb
|
||||
|
||||
_PROTOBUF_BUFFER_SIZE = const(8192)
|
||||
WIRE_BUFFER = bytearray(_PROTOBUF_BUFFER_SIZE)
|
||||
|
||||
|
||||
apps.base.boot()
|
||||
|
||||
mutex = Mutex()
|
||||
|
||||
if not utils.BITCOIN_ONLY and usb.ENABLE_IFACE_WEBAUTHN:
|
||||
import apps.webauthn
|
||||
|
||||
apps.webauthn.boot()
|
||||
apps.webauthn.boot(mutex)
|
||||
|
||||
if __debug__:
|
||||
import apps.debug
|
||||
|
||||
apps.debug.boot()
|
||||
|
||||
|
||||
# run main event loop and specify which screen is the default
|
||||
apps.base.set_homescreen()
|
||||
workflow.start_default()
|
||||
|
||||
|
||||
mutex.add(usb.iface_wire.iface_num())
|
||||
|
||||
# initialize the wire codec
|
||||
wire.setup(usb.iface_wire)
|
||||
wire.setup(usb.iface_wire, WIRE_BUFFER, wire.common_find_handler, mutex=mutex)
|
||||
|
||||
if __debug__:
|
||||
wire.setup(usb.iface_debug, is_debug_session=True)
|
||||
PROTOBUF_BUFFER_SIZE_DEBUG = 1024
|
||||
WIRE_BUFFER_DEBUG = bytearray(PROTOBUF_BUFFER_SIZE_DEBUG)
|
||||
|
||||
mutex.add(usb.iface_debug.iface_num())
|
||||
wire.setup(
|
||||
usb.iface_debug,
|
||||
WIRE_BUFFER_DEBUG,
|
||||
wire.common_find_handler,
|
||||
is_debug_session=True,
|
||||
)
|
||||
|
||||
if utils.USE_BLE:
|
||||
import bluetooth
|
||||
|
||||
BLE_BUFFER = bytearray(_PROTOBUF_BUFFER_SIZE)
|
||||
ble_find_handler = wire.MessageHandler()
|
||||
ble_find_handler.register_find_handler(bluetooth.int_find_handler)
|
||||
|
||||
mutex.add(bluetooth.iface_ble_int.iface_num())
|
||||
mutex.add(bluetooth.iface_ble_ext.iface_num())
|
||||
wire.setup(bluetooth.iface_ble_int, BLE_BUFFER, ble_find_handler, mutex=mutex)
|
||||
wire.setup(
|
||||
bluetooth.iface_ble_ext, BLE_BUFFER, wire.common_find_handler, mutex=mutex
|
||||
)
|
||||
|
||||
|
||||
loop.run()
|
||||
|
||||
|
@ -16,4 +16,5 @@ NotInitialized = 11
|
||||
PinMismatch = 12
|
||||
WipeCodeMismatch = 13
|
||||
InvalidSession = 14
|
||||
DeviceIsBusy = 15
|
||||
FirmwareError = 99
|
||||
|
@ -54,6 +54,15 @@ FirmwareErase = 6
|
||||
FirmwareUpload = 7
|
||||
FirmwareRequest = 8
|
||||
SelfTest = 32
|
||||
UploadBLEFirmwareInit = 8000
|
||||
UploadBLEFirmwareNextChunk = 8001
|
||||
UploadBLEFirmwareChunk = 8002
|
||||
PairingRequest = 8003
|
||||
AuthKey = 8004
|
||||
RepairRequest = 8005
|
||||
EraseBonds = 8006
|
||||
Disconnect = 8007
|
||||
ComparisonRequest = 8008
|
||||
GetPublicKey = 11
|
||||
PublicKey = 12
|
||||
SignTx = 15
|
||||
|
@ -76,6 +76,15 @@ if TYPE_CHECKING:
|
||||
FirmwareUpload = 7
|
||||
FirmwareRequest = 8
|
||||
SelfTest = 32
|
||||
UploadBLEFirmwareInit = 8000
|
||||
UploadBLEFirmwareNextChunk = 8001
|
||||
UploadBLEFirmwareChunk = 8002
|
||||
PairingRequest = 8003
|
||||
AuthKey = 8004
|
||||
RepairRequest = 8005
|
||||
EraseBonds = 8006
|
||||
Disconnect = 8007
|
||||
ComparisonRequest = 8008
|
||||
GetPublicKey = 11
|
||||
PublicKey = 12
|
||||
SignTx = 15
|
||||
@ -270,6 +279,7 @@ if TYPE_CHECKING:
|
||||
PinMismatch = 12
|
||||
WipeCodeMismatch = 13
|
||||
InvalidSession = 14
|
||||
DeviceIsBusy = 15
|
||||
FirmwareError = 99
|
||||
|
||||
class ButtonRequestType(IntEnum):
|
||||
|
@ -1188,6 +1188,102 @@ if TYPE_CHECKING:
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["TxAckPrevExtraDataWrapper"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class UploadBLEFirmwareInit(protobuf.MessageType):
|
||||
init_data: "bytes"
|
||||
binsize: "int"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
init_data: "bytes",
|
||||
binsize: "int",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareInit"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class UploadBLEFirmwareNextChunk(protobuf.MessageType):
|
||||
offset: "int"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
offset: "int",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareNextChunk"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class UploadBLEFirmwareChunk(protobuf.MessageType):
|
||||
data: "bytes"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
data: "bytes",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["UploadBLEFirmwareChunk"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class EraseBonds(protobuf.MessageType):
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["EraseBonds"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class Disconnect(protobuf.MessageType):
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["Disconnect"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class PairingRequest(protobuf.MessageType):
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["PairingRequest"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class AuthKey(protobuf.MessageType):
|
||||
key: "bytes"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
key: "bytes",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["AuthKey"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class RepairRequest(protobuf.MessageType):
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["RepairRequest"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class ComparisonRequest(protobuf.MessageType):
|
||||
key: "bytes"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
key: "bytes",
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_type_of(cls, msg: Any) -> TypeGuard["ComparisonRequest"]:
|
||||
return isinstance(msg, cls)
|
||||
|
||||
class CardanoBlockchainPointerType(protobuf.MessageType):
|
||||
block_index: "int"
|
||||
tx_index: "int"
|
||||
|
@ -30,12 +30,13 @@ async def button_request(
|
||||
|
||||
async def interact(
|
||||
layout: LayoutType,
|
||||
br_type: str,
|
||||
br_type: str | None,
|
||||
br_code: ButtonRequestType = ButtonRequestType.Other,
|
||||
) -> Any:
|
||||
pages = None
|
||||
if hasattr(layout, "page_count") and layout.page_count() > 1: # type: ignore [Cannot access member "page_count" for type "LayoutType"]
|
||||
# We know for certain how many pages the layout will have
|
||||
pages = layout.page_count() # type: ignore [Cannot access member "page_count" for type "LayoutType"]
|
||||
await button_request(br_type, br_code, pages)
|
||||
if br_type is not None:
|
||||
pages = None
|
||||
if hasattr(layout, "page_count") and layout.page_count() > 1: # type: ignore [Cannot access member "page_count" for type "LayoutType"]
|
||||
# We know for certain how many pages the layout will have
|
||||
pages = layout.page_count() # type: ignore [Cannot access member "page_count" for type "LayoutType"]
|
||||
await button_request(br_type, br_code, pages)
|
||||
return await context.wait(layout)
|
||||
|
@ -243,7 +243,7 @@ async def raise_if_not_confirmed(a: Awaitable[T], exc: Any = ActionCancelled) ->
|
||||
|
||||
|
||||
async def confirm_action(
|
||||
br_type: str,
|
||||
br_type: str | None,
|
||||
title: str,
|
||||
action: str | None = None,
|
||||
description: str | None = None,
|
||||
@ -1237,9 +1237,13 @@ async def request_pin_on_device(
|
||||
attempts_remaining: int | None,
|
||||
allow_cancel: bool,
|
||||
wrong_pin: bool = False,
|
||||
skip_button_request: bool = False,
|
||||
) -> str:
|
||||
from trezor.wire import PinCancelled
|
||||
|
||||
if not skip_button_request:
|
||||
await button_request("pin_device", code=ButtonRequestType.PinEntry)
|
||||
|
||||
if attempts_remaining is None:
|
||||
subprompt = ""
|
||||
elif attempts_remaining == 1:
|
||||
|
@ -9,6 +9,7 @@ from trezorutils import ( # noqa: F401
|
||||
SCM_REVISION,
|
||||
UI_LAYOUT,
|
||||
USE_BACKLIGHT,
|
||||
USE_BLE,
|
||||
USE_OPTIGA,
|
||||
USE_BUTTON,
|
||||
USE_SD_CARD,
|
||||
|
@ -49,7 +49,6 @@ from trezor.wire.errors import ActionCancelled, DataError, Error
|
||||
# other packages.
|
||||
from trezor.wire.errors import * # isort:skip # noqa: F401,F403
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezorio import WireInterface
|
||||
from typing import Any, Callable, Container, Coroutine, TypeVar
|
||||
@ -65,9 +64,35 @@ if TYPE_CHECKING:
|
||||
EXPERIMENTAL_ENABLED = False
|
||||
|
||||
|
||||
def setup(iface: WireInterface, is_debug_session: bool = False) -> None:
|
||||
class MessageHandler:
|
||||
def __init__(self):
|
||||
self._find_handler = None
|
||||
|
||||
def find_handler(self, iface: WireInterface, msg_type: int) -> Handler | None:
|
||||
if self._find_handler is not None:
|
||||
return self._find_handler(iface, msg_type)
|
||||
return None
|
||||
|
||||
def register_find_handler(self, handler):
|
||||
self._find_handler = handler
|
||||
|
||||
|
||||
common_find_handler = MessageHandler()
|
||||
|
||||
|
||||
def setup(
|
||||
iface: WireInterface,
|
||||
buffer: bytearray,
|
||||
handler: MessageHandler,
|
||||
is_debug_session: bool = False,
|
||||
mutex=None,
|
||||
) -> None:
|
||||
"""Initialize the wire stack on passed USB interface."""
|
||||
loop.schedule(handle_session(iface, codec_v1.SESSION_ID, is_debug_session))
|
||||
loop.schedule(
|
||||
handle_session(
|
||||
iface, codec_v1.SESSION_ID, buffer, handler, is_debug_session, mutex
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def wrap_protobuf_load(
|
||||
@ -100,7 +125,10 @@ if __debug__:
|
||||
|
||||
|
||||
async def _handle_single_message(
|
||||
ctx: context.Context, msg: codec_v1.Message, use_workflow: bool
|
||||
ctx: context.Context,
|
||||
msg: codec_v1.Message,
|
||||
find_handler: MessageHandler,
|
||||
use_workflow: bool,
|
||||
) -> codec_v1.Message | None:
|
||||
"""Handle a message that was loaded from USB by the caller.
|
||||
|
||||
@ -132,7 +160,9 @@ async def _handle_single_message(
|
||||
res_msg: protobuf.MessageType | None = None
|
||||
|
||||
# We need to find a handler for this message type. Should not raise.
|
||||
handler = find_handler(ctx.iface, msg.type) # pylint: disable=assignment-from-none
|
||||
handler = find_handler.find_handler(
|
||||
ctx.iface, msg.type
|
||||
) # pylint: disable=assignment-from-none
|
||||
|
||||
if handler is None:
|
||||
# If no handler is found, we can skip decoding and directly
|
||||
@ -205,13 +235,13 @@ async def _handle_single_message(
|
||||
|
||||
|
||||
async def handle_session(
|
||||
iface: WireInterface, session_id: int, is_debug_session: bool = False
|
||||
iface: WireInterface,
|
||||
session_id: int,
|
||||
ctx_buffer: bytearray,
|
||||
message_handler: MessageHandler,
|
||||
is_debug_session: bool = False,
|
||||
mutex=None,
|
||||
) -> None:
|
||||
if __debug__ and is_debug_session:
|
||||
ctx_buffer = WIRE_BUFFER_DEBUG
|
||||
else:
|
||||
ctx_buffer = WIRE_BUFFER
|
||||
|
||||
ctx = context.Context(iface, session_id, ctx_buffer)
|
||||
next_msg: codec_v1.Message | None = None
|
||||
|
||||
@ -230,6 +260,18 @@ async def handle_session(
|
||||
# wait for a new one coming from the wire.
|
||||
try:
|
||||
msg = await ctx.read_from_wire()
|
||||
if mutex is not None:
|
||||
if mutex.get_busy(iface.iface_num()):
|
||||
await ctx.write(
|
||||
Failure(
|
||||
code=FailureType.DeviceIsBusy,
|
||||
message="Device is busy",
|
||||
)
|
||||
)
|
||||
continue
|
||||
else:
|
||||
mutex.set_busy(iface.iface_num())
|
||||
|
||||
except codec_v1.CodecError as exc:
|
||||
if __debug__:
|
||||
log.exception(__name__, exc)
|
||||
@ -243,8 +285,9 @@ async def handle_session(
|
||||
|
||||
try:
|
||||
next_msg = await _handle_single_message(
|
||||
ctx, msg, use_workflow=not is_debug_session
|
||||
ctx, msg, message_handler, not is_debug_session
|
||||
)
|
||||
|
||||
except Exception as exc:
|
||||
# Log and ignore. The session handler can only exit explicitly in the
|
||||
# following finally block.
|
||||
@ -271,12 +314,6 @@ async def handle_session(
|
||||
log.exception(__name__, exc)
|
||||
|
||||
|
||||
def _find_handler_placeholder(iface: WireInterface, msg_type: int) -> Handler | None:
|
||||
"""Placeholder handler lookup before a proper one is registered."""
|
||||
return None
|
||||
|
||||
|
||||
find_handler = _find_handler_placeholder
|
||||
AVOID_RESTARTING_FOR: Container[int] = ()
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user