mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-05 22:32:33 +00:00
feat(core): internal events between nrf and stm
This commit is contained in:
parent
4046a878c3
commit
2c505978a5
@ -191,6 +191,7 @@ CPPPATH_MOD += [
|
||||
'embed/sdk/nrf52/components/nfc/ndef/conn_hand_parser/ac_rec_parser',
|
||||
'embed/sdk/nrf52/components/libraries/stack_guard',
|
||||
'embed/sdk/nrf52/components/libraries/log/src',
|
||||
'embed'
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
]
|
||||
@ -274,6 +275,7 @@ SOURCE_NRFHAL = [
|
||||
SOURCE_BLE_FIRMWARE = [
|
||||
'embed/ble_firmware/main.c',
|
||||
'embed/ble_firmware/ble_nus.c',
|
||||
'embed/ble_firmware/int_comm.c',
|
||||
]
|
||||
|
||||
if MMD:
|
||||
|
@ -382,6 +382,7 @@ SOURCE_TREZORHAL = [
|
||||
'embed/trezorhal/ble/comm.c',
|
||||
'embed/trezorhal/ble/dfu.c',
|
||||
'embed/trezorhal/ble/fwu.c',
|
||||
'embed/trezorhal/ble/state.c',
|
||||
]
|
||||
|
||||
|
||||
|
245
core/embed/ble_firmware/int_comm.c
Normal file
245
core/embed/ble_firmware/int_comm.c
Normal file
@ -0,0 +1,245 @@
|
||||
|
||||
#include "int_comm.h"
|
||||
#include "app_error.h"
|
||||
#include "app_uart.h"
|
||||
#include "ble_nus.h"
|
||||
#include "nrf_log.h"
|
||||
#include "stdint.h"
|
||||
#include "trezorhal/ble/int_comm_defs.h"
|
||||
|
||||
static uint8_t m_uart_rx_data[BLE_NUS_MAX_DATA_LEN];
|
||||
static bool m_uart_rx_data_ready_internal = false;
|
||||
static uint16_t *m_p_conn_handle = NULL;
|
||||
|
||||
BLE_NUS_DEF(m_nus,
|
||||
NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< BLE NUS service instance. */
|
||||
|
||||
void nus_init(uint16_t *p_conn_handle) {
|
||||
m_p_conn_handle = p_conn_handle;
|
||||
uint32_t err_code;
|
||||
|
||||
ble_nus_init_t nus_init;
|
||||
|
||||
memset(&nus_init, 0, sizeof(nus_init));
|
||||
|
||||
nus_init.data_handler = nus_data_handler;
|
||||
|
||||
err_code = ble_nus_init(&m_nus, &nus_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
*p_conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
}
|
||||
|
||||
void process_command(uint8_t *data, uint16_t len) {
|
||||
uint8_t cmd = data[0];
|
||||
switch (cmd) {
|
||||
case INTERNAL_CMD_SEND_STATE:
|
||||
if (*m_p_conn_handle != BLE_CONN_HANDLE_INVALID) {
|
||||
send_connected_event();
|
||||
} else {
|
||||
send_disconnected_event();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Function for handling app_uart events.
|
||||
*
|
||||
* @details This function will receive a single character from the app_uart
|
||||
* module and append it to a string. The string will be be sent over BLE when
|
||||
* the last character received was a 'new line' '\n' (hex 0x0A) or if the string
|
||||
* has reached the maximum data length.
|
||||
*/
|
||||
/**@snippet [Handling the data received over UART] */
|
||||
void uart_event_handle(app_uart_evt_t *p_event) {
|
||||
static uint8_t index = 0;
|
||||
static uint8_t message_type = 0;
|
||||
static uint16_t len = 0;
|
||||
uint32_t err_code;
|
||||
uint8_t rx_byte = 0;
|
||||
|
||||
switch (p_event->evt_type) {
|
||||
case APP_UART_DATA_READY:
|
||||
while (app_uart_get(&rx_byte) == NRF_SUCCESS) {
|
||||
if (index == 0) {
|
||||
if (rx_byte == INTERNAL_MESSAGE || rx_byte == INTERNAL_EVENT ||
|
||||
rx_byte == EXTERNAL_MESSAGE) {
|
||||
message_type = rx_byte;
|
||||
index += 1;
|
||||
continue;
|
||||
} else {
|
||||
// unknown message
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == 1) {
|
||||
// len HI
|
||||
len = rx_byte << 8;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index == 2) {
|
||||
// len LO
|
||||
len |= rx_byte;
|
||||
index += 1;
|
||||
if (len > sizeof(m_uart_rx_data) + OVERHEAD_SIZE) {
|
||||
// message too long
|
||||
index = 0;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index < (len - 1)) {
|
||||
// command
|
||||
m_uart_rx_data[index - COMM_HEADER_SIZE] = rx_byte;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index >= (len - 1)) {
|
||||
if (rx_byte == EOM) {
|
||||
if (message_type == EXTERNAL_MESSAGE) {
|
||||
NRF_LOG_DEBUG("Ready to send data over BLE NUS");
|
||||
NRF_LOG_HEXDUMP_DEBUG(m_uart_rx_data, index);
|
||||
|
||||
do {
|
||||
uint16_t length = (uint16_t)len - OVERHEAD_SIZE;
|
||||
err_code = ble_nus_data_send(&m_nus, m_uart_rx_data, &length,
|
||||
*m_p_conn_handle);
|
||||
if ((err_code != NRF_ERROR_INVALID_STATE) &&
|
||||
(err_code != NRF_ERROR_RESOURCES) &&
|
||||
(err_code != NRF_ERROR_NOT_FOUND)) {
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
} while (err_code == NRF_ERROR_RESOURCES);
|
||||
} else if (message_type == INTERNAL_MESSAGE) {
|
||||
m_uart_rx_data_ready_internal = true;
|
||||
} else if (message_type == INTERNAL_EVENT) {
|
||||
process_command(m_uart_rx_data, len - OVERHEAD_SIZE);
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**@snippet [Handling the data received over UART] */
|
||||
|
||||
void send_byte(uint8_t byte) {
|
||||
uint32_t err_code;
|
||||
|
||||
do {
|
||||
err_code = app_uart_put(byte);
|
||||
if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) {
|
||||
NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
|
||||
}
|
||||
} while (err_code == NRF_ERROR_BUSY);
|
||||
}
|
||||
|
||||
void send_packet(uint8_t message_type, const uint8_t *tx_data, uint16_t len) {
|
||||
uint16_t total_len = len + OVERHEAD_SIZE;
|
||||
send_byte(message_type);
|
||||
send_byte((total_len >> 8) & 0xFF);
|
||||
send_byte(total_len & 0xFF);
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
send_byte(tx_data[i]);
|
||||
}
|
||||
send_byte(EOM);
|
||||
}
|
||||
|
||||
/**@brief Function for handling the data from the Nordic UART Service.
|
||||
*
|
||||
* @details This function will process the data received from the Nordic UART
|
||||
* BLE Service and send it to the UART module.
|
||||
*
|
||||
* @param[in] p_evt Nordic UART Service event.
|
||||
*/
|
||||
/**@snippet [Handling the data received over BLE] */
|
||||
void nus_data_handler(ble_nus_evt_t *p_evt) {
|
||||
if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
|
||||
NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
|
||||
NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data,
|
||||
p_evt->params.rx_data.length);
|
||||
|
||||
if (p_evt->params.rx_data.length != 64) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_packet(EXTERNAL_MESSAGE, p_evt->params.rx_data.p_data,
|
||||
p_evt->params.rx_data.length);
|
||||
}
|
||||
}
|
||||
/**@snippet [Handling the data received over BLE] */
|
||||
|
||||
void send_connected_event(void) {
|
||||
uint8_t tx_data[] = {
|
||||
INTERNAL_EVENT_CONNECTED,
|
||||
};
|
||||
send_packet(INTERNAL_EVENT, tx_data, sizeof(tx_data));
|
||||
}
|
||||
|
||||
void send_disconnected_event(void) {
|
||||
uint8_t tx_data[] = {
|
||||
INTERNAL_EVENT_DISCONNECTED,
|
||||
};
|
||||
send_packet(INTERNAL_EVENT, tx_data, sizeof(tx_data));
|
||||
}
|
||||
|
||||
uint16_t get_message_type(const uint8_t *rx_data) {
|
||||
return (rx_data[3] << 8) | rx_data[4];
|
||||
}
|
||||
|
||||
bool send_auth_key_request(uint8_t *p_key, uint8_t p_key_len) {
|
||||
uint8_t tx_data[] = {
|
||||
0x3F, 0x23, 0x23, 0x1F, 0x43, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
send_packet(INTERNAL_MESSAGE, tx_data, sizeof(tx_data));
|
||||
|
||||
while (!m_uart_rx_data_ready_internal)
|
||||
;
|
||||
|
||||
if (get_message_type(m_uart_rx_data) != 8004) {
|
||||
m_uart_rx_data_ready_internal = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
p_key[i] = m_uart_rx_data[i + 11];
|
||||
}
|
||||
m_uart_rx_data_ready_internal = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool send_repair_request(void) {
|
||||
uint8_t tx_data[] = {
|
||||
0x3F, 0x23, 0x23, 0x1F, 0x45, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
send_packet(INTERNAL_MESSAGE, tx_data, sizeof(tx_data));
|
||||
|
||||
while (!m_uart_rx_data_ready_internal)
|
||||
;
|
||||
|
||||
m_uart_rx_data_ready_internal = false;
|
||||
|
||||
if (get_message_type(m_uart_rx_data) != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void send_initialized(void) {
|
||||
uint8_t tx_data[] = {
|
||||
INTERNAL_EVENT_INITIALIZED,
|
||||
};
|
||||
send_packet(INTERNAL_EVENT, tx_data, sizeof(tx_data));
|
||||
}
|
25
core/embed/ble_firmware/int_comm.h
Normal file
25
core/embed/ble_firmware/int_comm.h
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
#ifndef __INT_COMM__
|
||||
#define __INT_COMM__
|
||||
|
||||
#include "app_uart.h"
|
||||
#include "ble_nus.h"
|
||||
#include "stdint.h"
|
||||
|
||||
void nus_init(uint16_t *p_conn_handle);
|
||||
|
||||
void nus_data_handler(ble_nus_evt_t *p_evt);
|
||||
|
||||
void uart_event_handle(app_uart_evt_t *p_event);
|
||||
|
||||
void send_connected_event(void);
|
||||
|
||||
void send_disconnected_event(void);
|
||||
|
||||
bool send_auth_key_request(uint8_t *p_key, uint8_t p_key_len);
|
||||
|
||||
bool send_repair_request(void);
|
||||
|
||||
void send_initialized(void);
|
||||
|
||||
#endif
|
@ -84,6 +84,8 @@
|
||||
#include "nrf_log_ctrl.h"
|
||||
#include "nrf_log_default_backends.h"
|
||||
|
||||
#include "int_comm.h"
|
||||
|
||||
#define APP_BLE_CONN_CFG_TAG \
|
||||
1 /**< A tag identifying the SoftDevice BLE configuration. */
|
||||
|
||||
@ -139,12 +141,31 @@
|
||||
#define UART_TX_BUF_SIZE 256 /**< UART TX buffer size. */
|
||||
#define UART_RX_BUF_SIZE 256 /**< UART RX buffer size. */
|
||||
|
||||
BLE_NUS_DEF(m_nus,
|
||||
NRF_SDH_BLE_TOTAL_LINK_COUNT); /**< BLE NUS service instance. */
|
||||
NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */
|
||||
NRF_BLE_GATT_DEF(m_gatt); /**< GATT module instance. */
|
||||
NRF_BLE_QWR_DEF(m_qwr); /**< Context for the Queued Write module.*/
|
||||
BLE_ADVERTISING_DEF(m_advertising); /**< Advertising module instance. */
|
||||
|
||||
#define SEC_PARAM_BOND 1 /**< Perform bonding. */
|
||||
#define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */
|
||||
#define SEC_PARAM_LESC 0 /**< LE Secure Connections not enabled. */
|
||||
#define SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */
|
||||
#define SEC_PARAM_IO_CAPABILITIES \
|
||||
BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY /**< No I/O capabilities. */
|
||||
#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */
|
||||
#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */
|
||||
#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */
|
||||
|
||||
#define SCHED_MAX_EVENT_DATA_SIZE \
|
||||
APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */
|
||||
#ifdef SVCALL_AS_NORMAL_FUNCTION
|
||||
#define SCHED_QUEUE_SIZE \
|
||||
20 /**< Maximum number of events in the scheduler queue. More is needed in \
|
||||
case of Serialization. */
|
||||
#else
|
||||
#define SCHED_QUEUE_SIZE \
|
||||
10 /**< Maximum number of events in the scheduler queue. */
|
||||
#endif
|
||||
|
||||
static pm_peer_id_t
|
||||
m_peer_id; /**< Device reference handle to the current bonded central. */
|
||||
static uint16_t m_conn_handle =
|
||||
@ -156,9 +177,6 @@ static uint16_t m_ble_nus_max_data_len =
|
||||
static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifier. */
|
||||
{{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}};
|
||||
|
||||
static uint8_t m_uart_rx_data[BLE_NUS_MAX_DATA_LEN];
|
||||
static bool m_uart_rx_data_ready_internal = false;
|
||||
|
||||
/**@brief Function for assert macro callback.
|
||||
*
|
||||
* @details This function will be called in case of an assert in the SoftDevice.
|
||||
@ -224,51 +242,11 @@ static void nrf_qwr_error_handler(uint32_t nrf_error) {
|
||||
APP_ERROR_HANDLER(nrf_error);
|
||||
}
|
||||
|
||||
/**@brief Function for handling the data from the Nordic UART Service.
|
||||
*
|
||||
* @details This function will process the data received from the Nordic UART
|
||||
* BLE Service and send it to the UART module.
|
||||
*
|
||||
* @param[in] p_evt Nordic UART Service event.
|
||||
*/
|
||||
/**@snippet [Handling the data received over BLE] */
|
||||
static void nus_data_handler(ble_nus_evt_t *p_evt) {
|
||||
if (p_evt->type == BLE_NUS_EVT_RX_DATA) {
|
||||
uint32_t err_code;
|
||||
|
||||
NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
|
||||
NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data,
|
||||
p_evt->params.rx_data.length);
|
||||
|
||||
if (p_evt->params.rx_data.length != 64) {
|
||||
return;
|
||||
}
|
||||
|
||||
app_uart_put(0xA1); // external message
|
||||
app_uart_put(0x00); // len - HI
|
||||
app_uart_put(0x44); // len - LO
|
||||
|
||||
for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++) {
|
||||
do {
|
||||
err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
|
||||
if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) {
|
||||
NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
} while (err_code == NRF_ERROR_BUSY);
|
||||
}
|
||||
|
||||
app_uart_put(0x55); // EOM
|
||||
}
|
||||
}
|
||||
/**@snippet [Handling the data received over BLE] */
|
||||
|
||||
/**@brief Function for initializing services that will be used by the
|
||||
* application.
|
||||
*/
|
||||
static void services_init(void) {
|
||||
uint32_t err_code;
|
||||
ble_nus_init_t nus_init;
|
||||
nrf_ble_qwr_init_t qwr_init = {0};
|
||||
|
||||
// Initialize Queued Write Module.
|
||||
@ -277,13 +255,7 @@ static void services_init(void) {
|
||||
err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
// Initialize NUS.
|
||||
memset(&nus_init, 0, sizeof(nus_init));
|
||||
|
||||
nus_init.data_handler = nus_data_handler;
|
||||
|
||||
err_code = ble_nus_init(&m_nus, &nus_init);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
nus_init(&m_conn_handle);
|
||||
}
|
||||
|
||||
/**@brief Function for handling errors from the Connection Parameters module.
|
||||
@ -451,20 +423,6 @@ static void on_adv_evt(ble_adv_evt_t ble_adv_evt) {
|
||||
}
|
||||
}
|
||||
|
||||
static void int_comm_send(uint8_t *tx_data, uint16_t len) {
|
||||
uint32_t err_code;
|
||||
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
do {
|
||||
err_code = app_uart_put(tx_data[i]);
|
||||
if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY)) {
|
||||
NRF_LOG_ERROR("Failed sending data to STM. Error 0x%x. ", err_code);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
} while (err_code == NRF_ERROR_BUSY);
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Function for handling BLE events.
|
||||
*
|
||||
* @param[in] p_ble_evt Bluetooth stack event.
|
||||
@ -478,6 +436,8 @@ static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context) {
|
||||
NRF_LOG_INFO("Connected");
|
||||
err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
send_connected_event();
|
||||
m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
|
||||
err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
@ -486,6 +446,7 @@ static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context) {
|
||||
case BLE_GAP_EVT_DISCONNECTED:
|
||||
NRF_LOG_INFO("Disconnected");
|
||||
// LED indication will be changed when advertising starts.
|
||||
send_disconnected_event();
|
||||
m_conn_handle = BLE_CONN_HANDLE_INVALID;
|
||||
break;
|
||||
|
||||
@ -503,37 +464,19 @@ static void ble_evt_handler(ble_evt_t const *p_ble_evt, void *p_context) {
|
||||
case BLE_GAP_EVT_AUTH_KEY_REQUEST: {
|
||||
NRF_LOG_INFO("Key requested.");
|
||||
|
||||
uint8_t tx_data[] = {
|
||||
0xA0, // internal message
|
||||
0x00, // length - HI
|
||||
0x0D, // length - LO
|
||||
0x3F, 0x23, 0x23, 0x1F, 0x43, 0x00, 0x00, 0x00, 0x00,
|
||||
0x55, // EOM
|
||||
};
|
||||
|
||||
int_comm_send(tx_data, sizeof(tx_data));
|
||||
|
||||
uint8_t p_key[6] = {0};
|
||||
|
||||
while (!m_uart_rx_data_ready_internal)
|
||||
;
|
||||
bool ok = send_auth_key_request(p_key, sizeof(p_key));
|
||||
|
||||
uint16_t message_type = (m_uart_rx_data[3] << 8) | m_uart_rx_data[4];
|
||||
|
||||
if (message_type != 8004) {
|
||||
break;
|
||||
if (ok) {
|
||||
NRF_LOG_INFO("Received data: %c", p_key);
|
||||
err_code =
|
||||
sd_ble_gap_auth_key_reply(p_ble_evt->evt.gap_evt.conn_handle,
|
||||
BLE_GAP_AUTH_KEY_TYPE_PASSKEY, p_key);
|
||||
} else {
|
||||
NRF_LOG_INFO("Auth key request failed.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
p_key[i] = m_uart_rx_data[i + 11];
|
||||
}
|
||||
m_uart_rx_data_ready_internal = false;
|
||||
|
||||
NRF_LOG_INFO("Received data: %c", p_key);
|
||||
|
||||
err_code =
|
||||
sd_ble_gap_auth_key_reply(p_ble_evt->evt.gap_evt.conn_handle,
|
||||
BLE_GAP_AUTH_KEY_TYPE_PASSKEY, p_key);
|
||||
// APP_ERROR_CHECK(err_code);
|
||||
break;
|
||||
}
|
||||
@ -644,105 +587,6 @@ void bsp_event_handler(bsp_event_t event) {
|
||||
}
|
||||
}
|
||||
|
||||
/**@brief Function for handling app_uart events.
|
||||
*
|
||||
* @details This function will receive a single character from the app_uart
|
||||
* module and append it to a string. The string will be be sent over BLE when
|
||||
* the last character received was a 'new line' '\n' (hex 0x0A) or if the string
|
||||
* has reached the maximum data length.
|
||||
*/
|
||||
/**@snippet [Handling the data received over UART] */
|
||||
void uart_event_handle(app_uart_evt_t *p_event) {
|
||||
static uint8_t index = 0;
|
||||
static uint8_t external = 0;
|
||||
static uint16_t len = 0;
|
||||
uint32_t err_code;
|
||||
uint8_t rx_byte = 0;
|
||||
|
||||
switch (p_event->evt_type) {
|
||||
case APP_UART_DATA_READY:
|
||||
while (app_uart_get(&rx_byte) == NRF_SUCCESS) {
|
||||
if (index == 0) {
|
||||
// decide destination
|
||||
if (rx_byte == 0xA0) {
|
||||
// internal message
|
||||
external = 0;
|
||||
index += 1;
|
||||
continue;
|
||||
} else if (rx_byte == 0xA1) {
|
||||
// external message
|
||||
external = 1;
|
||||
index += 1;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
// unknown message
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == 1) {
|
||||
// len HI
|
||||
len = rx_byte << 8;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index == 2) {
|
||||
// len LO
|
||||
len |= rx_byte;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index < (len - 1)) {
|
||||
// command
|
||||
m_uart_rx_data[index - 3] = rx_byte;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index >= (len - 1)) {
|
||||
{
|
||||
if (rx_byte == 0x55) {
|
||||
if (external) {
|
||||
NRF_LOG_DEBUG("Ready to send data over BLE NUS");
|
||||
NRF_LOG_HEXDUMP_DEBUG(m_uart_rx_data, index);
|
||||
|
||||
do {
|
||||
uint16_t length = (uint16_t)len - 4;
|
||||
err_code = ble_nus_data_send(&m_nus, m_uart_rx_data, &length,
|
||||
m_conn_handle);
|
||||
if ((err_code != NRF_ERROR_INVALID_STATE) &&
|
||||
(err_code != NRF_ERROR_RESOURCES) &&
|
||||
(err_code != NRF_ERROR_NOT_FOUND)) {
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
} while (err_code == NRF_ERROR_RESOURCES);
|
||||
} else {
|
||||
m_uart_rx_data_ready_internal = true;
|
||||
}
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// case APP_UART_COMMUNICATION_ERROR:
|
||||
// APP_ERROR_HANDLER(p_event->data.error_communication);
|
||||
// break;
|
||||
//
|
||||
// case APP_UART_FIFO_ERROR:
|
||||
// APP_ERROR_HANDLER(p_event->data.error_code);
|
||||
// break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**@snippet [Handling the data received over UART] */
|
||||
|
||||
/**@brief Function for initializing the UART module.
|
||||
*/
|
||||
/**@snippet [UART Initialization] */
|
||||
@ -888,27 +732,6 @@ static void advertising_start(bool erase_bonds) {
|
||||
}
|
||||
}
|
||||
|
||||
#define SEC_PARAM_BOND 1 /**< Perform bonding. */
|
||||
#define SEC_PARAM_MITM 0 /**< Man In The Middle protection not required. */
|
||||
#define SEC_PARAM_LESC 0 /**< LE Secure Connections not enabled. */
|
||||
#define SEC_PARAM_KEYPRESS 0 /**< Keypress notifications not enabled. */
|
||||
#define SEC_PARAM_IO_CAPABILITIES \
|
||||
BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY /**< No I/O capabilities. */
|
||||
#define SEC_PARAM_OOB 0 /**< Out Of Band data not available. */
|
||||
#define SEC_PARAM_MIN_KEY_SIZE 7 /**< Minimum encryption key size. */
|
||||
#define SEC_PARAM_MAX_KEY_SIZE 16 /**< Maximum encryption key size. */
|
||||
|
||||
#define SCHED_MAX_EVENT_DATA_SIZE \
|
||||
APP_TIMER_SCHED_EVENT_DATA_SIZE /**< Maximum size of scheduler events. */
|
||||
#ifdef SVCALL_AS_NORMAL_FUNCTION
|
||||
#define SCHED_QUEUE_SIZE \
|
||||
20 /**< Maximum number of events in the scheduler queue. More is needed in \
|
||||
case of Serialization. */
|
||||
#else
|
||||
#define SCHED_QUEUE_SIZE \
|
||||
10 /**< Maximum number of events in the scheduler queue. */
|
||||
#endif
|
||||
|
||||
/**@brief Function for handling Peer Manager events.
|
||||
*
|
||||
* @param[in] p_evt Peer Manager event.
|
||||
@ -939,29 +762,14 @@ static void pm_evt_handler(pm_evt_t const *p_evt) {
|
||||
}
|
||||
break;
|
||||
case PM_EVT_CONN_SEC_CONFIG_REQ: {
|
||||
uint8_t tx_data[] = {
|
||||
0xA0, // internal message
|
||||
0x00, // length - HI
|
||||
0x0D, // length - LO
|
||||
0x3F, 0x23, 0x23, 0x1F, 0x45, 0x00, 0x00, 0x00, 0x00,
|
||||
0x55, // EOM
|
||||
};
|
||||
bool ok = send_repair_request();
|
||||
|
||||
int_comm_send(tx_data, sizeof(tx_data));
|
||||
|
||||
while (!m_uart_rx_data_ready_internal)
|
||||
;
|
||||
|
||||
uint16_t message_type = (m_uart_rx_data[3] << 8) | m_uart_rx_data[4];
|
||||
|
||||
m_uart_rx_data_ready_internal = false;
|
||||
|
||||
if (message_type == 2) {
|
||||
// Allow or reject pairing request from an already bonded peer.
|
||||
if (ok) {
|
||||
// Allow pairing request from an already bonded peer.
|
||||
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = true};
|
||||
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
|
||||
} else {
|
||||
// Allow or reject pairing request from an already bonded peer.
|
||||
// Reject pairing request from an already bonded peer.
|
||||
pm_conn_sec_config_t conn_sec_config = {.allow_repairing = false};
|
||||
pm_conn_sec_config_reply(p_evt->conn_handle, &conn_sec_config);
|
||||
}
|
||||
@ -1031,7 +839,9 @@ int main(void) {
|
||||
peer_manager_init();
|
||||
|
||||
// Start execution.
|
||||
advertising_start(true);
|
||||
advertising_start(erase_bonds);
|
||||
|
||||
send_initialized();
|
||||
|
||||
// Enter main loop.
|
||||
for (;;) {
|
||||
|
@ -72,7 +72,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_BLE_update_chunk_obj,
|
||||
STATIC mp_obj_t mod_trezorio_BLE_write(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, ble_last_internal);
|
||||
ble_int_comm_send(buf.buf, buf.len,
|
||||
ble_last_internal ? INTERNAL_MESSAGE : EXTERNAL_MESSAGE);
|
||||
return MP_OBJ_NEW_SMALL_INT(buf.len);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_BLE_write_obj,
|
||||
|
@ -20,11 +20,13 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "ble/comm.h"
|
||||
#include "ble/state.h"
|
||||
#include "button.h"
|
||||
#include "common.h"
|
||||
#include "display.h"
|
||||
#include "embed/extmod/trezorobj.h"
|
||||
|
||||
#define BLE_EVENTS_IFACE (252)
|
||||
#define USB_DATA_IFACE (253)
|
||||
#define BUTTON_IFACE (254)
|
||||
#define TOUCH_IFACE (255)
|
||||
@ -128,6 +130,15 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
|
||||
ret->items[1] = usb_connected ? mp_const_true : mp_const_false;
|
||||
return mp_const_true;
|
||||
}
|
||||
} else if (iface == BLE_EVENTS_IFACE) {
|
||||
ble_int_comm_poll();
|
||||
uint8_t connected = ble_connected();
|
||||
if (connected != ble_connected_previously) {
|
||||
ble_connected_previously = connected;
|
||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||
ret->items[1] = connected ? mp_const_true : mp_const_false;
|
||||
return mp_const_true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if USE_BUTTON
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
// Whether USB data pins were connected on last check (USB configured)
|
||||
bool usb_connected_previously = true;
|
||||
uint8_t ble_connected_previously = false;
|
||||
bool ble_last_internal = false;
|
||||
|
||||
#define CHECK_PARAM_RANGE(value, minimum, maximum) \
|
||||
@ -120,6 +121,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_POLL_WRITE), MP_ROM_INT(POLL_WRITE)},
|
||||
|
||||
{MP_ROM_QSTR(MP_QSTR_USB_CHECK), MP_ROM_INT(USB_DATA_IFACE)},
|
||||
{MP_ROM_QSTR(MP_QSTR_BLE_CHECK), MP_ROM_INT(BLE_EVENTS_IFACE)},
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals,
|
||||
|
@ -23,6 +23,7 @@ clippy = []
|
||||
jpeg = []
|
||||
disp_i8080_8bit_dw = [] # write pixels directly to peripheral
|
||||
disp_i8080_16bit_dw = [] # write pixels directly to peripheral
|
||||
ble = []
|
||||
debug = ["ui_debug"]
|
||||
sbu = []
|
||||
sd_card = []
|
||||
|
@ -347,6 +347,8 @@ fn generate_trezorhal_bindings() {
|
||||
.no_copy("buffer_blurring_t")
|
||||
.allowlist_var("text_buffer_height")
|
||||
.allowlist_var("buffer_width")
|
||||
// ble
|
||||
.allowlist_function("ble_connected")
|
||||
//usb
|
||||
.allowlist_function("usb_configured")
|
||||
// touch
|
||||
|
5
core/embed/rust/src/trezorhal/ble.rs
Normal file
5
core/embed/rust/src/trezorhal/ble.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use super::ffi;
|
||||
|
||||
pub fn ble_connected() -> bool {
|
||||
unsafe { ffi::ble_connected() }
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
pub mod bip39;
|
||||
#[cfg(feature = "ble")]
|
||||
pub mod ble;
|
||||
#[macro_use]
|
||||
#[allow(unused_macros)]
|
||||
pub mod fatal_error;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include TREZOR_BOARD
|
||||
#include "ble/state.h"
|
||||
#include "buffers.h"
|
||||
#include "button.h"
|
||||
#include "common.h"
|
||||
|
@ -21,8 +21,11 @@
|
||||
#include TREZOR_BOARD
|
||||
|
||||
#include "comm.h"
|
||||
#include "int_comm_defs.h"
|
||||
#include "state.h"
|
||||
|
||||
static UART_HandleTypeDef urt;
|
||||
static uint8_t last_init_byte = 0;
|
||||
|
||||
void ble_comm_init(void) {
|
||||
GPIO_InitTypeDef GPIO_InitStructure;
|
||||
@ -46,6 +49,8 @@ void ble_comm_init(void) {
|
||||
urt.Instance = USART1;
|
||||
|
||||
HAL_UART_Init(&urt);
|
||||
|
||||
set_initialized(false);
|
||||
}
|
||||
|
||||
void ble_comm_send(uint8_t *data, uint32_t len) {
|
||||
@ -68,20 +73,13 @@ uint32_t ble_comm_receive(uint8_t *data, uint32_t len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ble_int_comm_send(uint8_t *data, uint32_t len, bool internal) {
|
||||
uint16_t msg_len = len + 4;
|
||||
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 = 0x55;
|
||||
uint8_t eom = EOM;
|
||||
|
||||
uint8_t init_byte = 0;
|
||||
if (internal) {
|
||||
init_byte = 0xA0;
|
||||
} else {
|
||||
init_byte = 0xA1;
|
||||
}
|
||||
|
||||
HAL_UART_Transmit(&urt, &init_byte, 1, 1);
|
||||
HAL_UART_Transmit(&urt, &message_type, 1, 1);
|
||||
HAL_UART_Transmit(&urt, &len_hi, 1, 1);
|
||||
HAL_UART_Transmit(&urt, &len_lo, 1, 1);
|
||||
|
||||
@ -89,13 +87,52 @@ void ble_int_comm_send(uint8_t *data, uint32_t len, bool internal) {
|
||||
HAL_UART_Transmit(&urt, &eom, 1, 1);
|
||||
}
|
||||
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len, bool *internal) {
|
||||
data[0] = 0;
|
||||
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_CONNECTED: {
|
||||
set_connected(true);
|
||||
set_initialized(true);
|
||||
break;
|
||||
}
|
||||
case INTERNAL_EVENT_DISCONNECTED: {
|
||||
set_connected(false);
|
||||
set_initialized(true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void flush_line(void) {
|
||||
while (urt.Instance->SR & USART_SR_RXNE) {
|
||||
(void)urt.Instance->DR;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ble_int_comm_poll(void) {
|
||||
uint8_t data[64] = {0};
|
||||
if (urt.Instance->SR & USART_SR_RXNE) {
|
||||
uint8_t init_byte = 0;
|
||||
HAL_UART_Receive(&urt, &init_byte, 1, 1);
|
||||
|
||||
if (init_byte == 0xA0 || init_byte == 0xA1) {
|
||||
if (last_init_byte != 0) {
|
||||
if (last_init_byte == INTERNAL_EVENT) {
|
||||
init_byte = last_init_byte;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
HAL_UART_Receive(&urt, &init_byte, 1, 1);
|
||||
}
|
||||
|
||||
if (init_byte == INTERNAL_EVENT) {
|
||||
uint8_t len_hi = 0;
|
||||
uint8_t len_lo = 0;
|
||||
HAL_UART_Receive(&urt, &len_hi, 1, 1);
|
||||
@ -103,28 +140,104 @@ uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len, bool *internal) {
|
||||
|
||||
uint16_t act_len = (len_hi << 8) | len_lo;
|
||||
|
||||
HAL_StatusTypeDef result = HAL_UART_Receive(&urt, data, act_len - 4, 5);
|
||||
if (act_len > sizeof(data) + OVERHEAD_SIZE) {
|
||||
last_init_byte = 0;
|
||||
flush_line();
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef result =
|
||||
HAL_UART_Receive(&urt, data, act_len - OVERHEAD_SIZE, 5);
|
||||
|
||||
if (result != HAL_OK) {
|
||||
last_init_byte = 0;
|
||||
flush_line();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t eom = 0;
|
||||
HAL_UART_Receive(&urt, &eom, 1, 1);
|
||||
|
||||
if (eom == 0x55) {
|
||||
if (init_byte == 0xA0) {
|
||||
if (eom == EOM) {
|
||||
process_poll(data, act_len - OVERHEAD_SIZE);
|
||||
last_init_byte = 0;
|
||||
return act_len - OVERHEAD_SIZE;
|
||||
}
|
||||
return 0;
|
||||
|
||||
} else if (init_byte == INTERNAL_MESSAGE || init_byte == EXTERNAL_MESSAGE) {
|
||||
last_init_byte = init_byte;
|
||||
} else {
|
||||
flush_line();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ble_initialized()) {
|
||||
uint8_t cmd = INTERNAL_CMD_SEND_STATE;
|
||||
ble_int_comm_send(&cmd, sizeof(cmd), INTERNAL_EVENT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len, bool *internal) {
|
||||
if (urt.Instance->SR & USART_SR_RXNE) {
|
||||
uint8_t init_byte = 0;
|
||||
|
||||
if (last_init_byte != 0) {
|
||||
if (last_init_byte == INTERNAL_MESSAGE ||
|
||||
last_init_byte == EXTERNAL_MESSAGE) {
|
||||
init_byte = last_init_byte;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
HAL_UART_Receive(&urt, &init_byte, 1, 1);
|
||||
}
|
||||
|
||||
if (init_byte == INTERNAL_MESSAGE || init_byte == EXTERNAL_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 > len + OVERHEAD_SIZE) {
|
||||
last_init_byte = 0;
|
||||
flush_line();
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef result =
|
||||
HAL_UART_Receive(&urt, data, act_len - OVERHEAD_SIZE, 5);
|
||||
|
||||
if (result != HAL_OK) {
|
||||
last_init_byte = 0;
|
||||
flush_line();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t eom = 0;
|
||||
HAL_UART_Receive(&urt, &eom, 1, 1);
|
||||
|
||||
if (eom == EOM) {
|
||||
if (init_byte == INTERNAL_MESSAGE) {
|
||||
*internal = true;
|
||||
} else {
|
||||
*internal = false;
|
||||
}
|
||||
return act_len - 4;
|
||||
last_init_byte = 0;
|
||||
return act_len - OVERHEAD_SIZE;
|
||||
}
|
||||
return 0;
|
||||
|
||||
} else if (init_byte == INTERNAL_EVENT) {
|
||||
last_init_byte = init_byte;
|
||||
} else {
|
||||
// disregard byte.
|
||||
// todo: flush everything on the line, also for other errors
|
||||
flush_line();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,18 @@
|
||||
|
||||
#ifndef __BLE_COMM_H__
|
||||
#define __BLE_COMM_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "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, bool internal);
|
||||
void ble_int_comm_send(uint8_t *data, uint32_t len, uint8_t message_type);
|
||||
uint32_t ble_int_comm_receive(uint8_t *data, uint32_t len, bool *internal);
|
||||
|
||||
uint32_t ble_int_comm_poll(void);
|
||||
|
||||
#endif
|
||||
|
20
core/embed/trezorhal/ble/int_comm_defs.h
Normal file
20
core/embed/trezorhal/ble/int_comm_defs.h
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
#ifndef __INT_COMM_DEFS__
|
||||
#define __INT_COMM_DEFS__
|
||||
|
||||
#define COMM_HEADER_SIZE (3)
|
||||
#define COMM_FOOTER_SIZE (1)
|
||||
#define OVERHEAD_SIZE (COMM_HEADER_SIZE + COMM_FOOTER_SIZE)
|
||||
|
||||
#define EOM (0x55)
|
||||
#define INTERNAL_EVENT (0xA2)
|
||||
#define EXTERNAL_MESSAGE (0xA1)
|
||||
#define INTERNAL_MESSAGE (0xA0)
|
||||
|
||||
#define INTERNAL_EVENT_INITIALIZED (0x00)
|
||||
#define INTERNAL_EVENT_CONNECTED (0x01)
|
||||
#define INTERNAL_EVENT_DISCONNECTED (0x02)
|
||||
|
||||
#define INTERNAL_CMD_SEND_STATE (0x00)
|
||||
|
||||
#endif
|
14
core/embed/trezorhal/ble/state.c
Normal file
14
core/embed/trezorhal/ble/state.c
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
#include "ble/state.h"
|
||||
|
||||
static bool ble_state_connected = false;
|
||||
static bool ble_state_initialized = false;
|
||||
|
||||
bool ble_connected(void) { return ble_state_connected; }
|
||||
|
||||
void set_connected(bool connected) { ble_state_connected = connected; }
|
||||
|
||||
void set_initialized(bool initialized) { ble_state_initialized = initialized; }
|
||||
|
||||
bool ble_initialized(void) { return ble_state_initialized; }
|
16
core/embed/trezorhal/ble/state.h
Normal file
16
core/embed/trezorhal/ble/state.h
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
#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_connected(bool connected);
|
||||
|
||||
#endif
|
14
core/embed/unix/ble/state.c
Normal file
14
core/embed/unix/ble/state.c
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
#include "ble/state.h"
|
||||
|
||||
static bool ble_state_connected = false;
|
||||
static bool ble_state_initialized = false;
|
||||
|
||||
bool ble_connected(void) { return ble_state_connected; }
|
||||
|
||||
void set_connected(bool connected) { ble_state_connected = connected; }
|
||||
|
||||
void set_initialized(bool initialized) { ble_state_initialized = initialized; }
|
||||
|
||||
bool ble_initialized(void) { return ble_state_initialized; }
|
16
core/embed/unix/ble/state.h
Normal file
16
core/embed/unix/ble/state.h
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
#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_connected(bool connected);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user