parent
d13fe73164
commit
d85bbf65aa
@ -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;
|
||||
}
|
@ -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};
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
@ -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__
|
@ -0,0 +1,31 @@
|
||||
|
||||
#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 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
|
@ -0,0 +1,80 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ble.h"
|
||||
#include "common.h"
|
||||
#include "int_comm_defs.h"
|
||||
#include "messages.h"
|
||||
|
||||
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;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef __BLE_MESSAGES__
|
||||
#define __BLE_MESSAGES__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
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
|
@ -0,0 +1,72 @@
|
||||
|
||||
|
||||
#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_connected(bool connected) {}
|
||||
|
||||
void set_status(bool connected, bool advertising, bool whitelist,
|
||||
uint8_t count) {
|
||||
if (ble_state_connected != connected) {
|
||||
ble_advertising_wanted = count > 0;
|
||||
ble_advertising_wl_wanted = true;
|
||||
}
|
||||
ble_state_connected = connected;
|
||||
|
||||
ble_peer_count = count;
|
||||
if (count > 0 && !ble_initialized()) {
|
||||
ble_advertising_wanted = true;
|
||||
ble_advertising_wl_wanted = true;
|
||||
}
|
||||
|
||||
if (ble_advertising_wanted != advertising ||
|
||||
(ble_advertising_wl_wanted != whitelist)) {
|
||||
if (ble_advertising_wanted) {
|
||||
send_advertising_on(ble_advertising_wl_wanted);
|
||||
}
|
||||
if (!ble_advertising_wanted && ble_advertising) {
|
||||
send_advertising_off();
|
||||
}
|
||||
}
|
||||
ble_advertising = advertising;
|
||||
ble_advertising_wl = 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);
|
||||
}
|
||||
}
|
||||
|
||||
void stop_advertising(void) {
|
||||
ble_advertising_wanted = false;
|
||||
if (ble_advertising) {
|
||||
send_advertising_off();
|
||||
}
|
||||
}
|
||||
|
||||
void ble_set_dfu_mode(bool dfu) { ble_dfu_mode = dfu; }
|
||||
|
||||
bool is_ble_dfu_mode(void) { return ble_dfu_mode; }
|
@ -0,0 +1,25 @@
|
||||
|
||||
#ifndef __BLE_STATE__
|
||||
#define __BLE_STATE__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool ble_initialized(void);
|
||||
|
||||
void set_initialized(bool initialized);
|
||||
|
||||
bool ble_connected(void);
|
||||
|
||||
void set_status(bool connected, bool advertising, bool whitelist,
|
||||
uint8_t count);
|
||||
|
||||
void start_advertising(bool whitelist);
|
||||
|
||||
void stop_advertising(void);
|
||||
|
||||
void ble_set_dfu_mode(bool dfu);
|
||||
|
||||
bool is_ble_dfu_mode(void);
|
||||
|
||||
#endif
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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) }
|
||||
}
|
@ -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 *************************************************/
|
@ -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 *************************************************/
|
@ -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 *************************************************/
|
@ -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 *************************************************/
|
@ -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 ****************************/
|
@ -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
|
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#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_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 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: {
|
||||
set_status(data[1], data[2], data[3], data[4]);
|
||||
set_initialized(true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
#include "supervise.h"
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
|
||||
#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_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
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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
|
||||
"""
|
@ -0,0 +1,20 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import (
|
||||
Success,
|
||||
ComparisonRequest,
|
||||
)
|
||||
|
||||
|
||||
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()
|
@ -0,0 +1,21 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import (
|
||||
Success,
|
||||
Disconnect,
|
||||
)
|
||||
|
||||
|
||||
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()
|
@ -0,0 +1,21 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import (
|
||||
Success,
|
||||
EraseBonds,
|
||||
)
|
||||
|
||||
|
||||
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()
|
@ -0,0 +1,21 @@
|
||||
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())
|
@ -0,0 +1,18 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import (
|
||||
Success,
|
||||
RepairRequest,
|
||||
)
|
||||
|
||||
|
||||
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()
|
@ -0,0 +1,79 @@
|
||||
from trezorio import ble
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import (
|
||||
UploadBLEFirmwareInit,
|
||||
UploadBLEFirmwareChunk,
|
||||
Success,
|
||||
)
|
||||
|
||||
|
||||
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 (
|
||||
UploadBLEFirmwareNextChunk,
|
||||
UploadBLEFirmwareChunk,
|
||||
Success,
|
||||
)
|
||||
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.ui.layouts import progress
|
||||
from trezor.wire.context import get_context
|
||||
from trezor.enums import MessageType
|
||||
|
||||
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))
|
||||
|
@ -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()
|
@ -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
|
Loading…
Reference in new issue