mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-08 14:31:06 +00:00
feat(core:): introduce BLE driver
[no changelog]
This commit is contained in:
parent
f5a0f84201
commit
0e4565ac2f
@ -29,7 +29,7 @@ FEATURE_FLAGS = {
|
|||||||
"AES_GCM": BENCHMARK or THP,
|
"AES_GCM": BENCHMARK or THP,
|
||||||
}
|
}
|
||||||
|
|
||||||
FEATURES_WANTED = ["input", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic"]
|
FEATURES_WANTED = ["input", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic", "ble"]
|
||||||
if DISABLE_OPTIGA and PYOPT == '0':
|
if DISABLE_OPTIGA and PYOPT == '0':
|
||||||
FEATURES_WANTED.remove("optiga")
|
FEATURES_WANTED.remove("optiga")
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ FEATURE_FLAGS = {
|
|||||||
"AES_GCM": False,
|
"AES_GCM": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
FEATURES_WANTED = ["input", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic"]
|
FEATURES_WANTED = ["input", "sd_card", "rgb_led", "dma2d", "consumption_mask", "usb" ,"optiga", "haptic", "ble"]
|
||||||
if DISABLE_OPTIGA and PYOPT == '0':
|
if DISABLE_OPTIGA and PYOPT == '0':
|
||||||
FEATURES_WANTED.remove("optiga")
|
FEATURES_WANTED.remove("optiga")
|
||||||
|
|
||||||
|
125
core/embed/io/ble/inc/io/ble.h
Normal file
125
core/embed/io/ble/inc/io/ble.h
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREZORHAL_BLE_H
|
||||||
|
#define TREZORHAL_BLE_H
|
||||||
|
|
||||||
|
// This module provides interface to BLE (Bluetooth Low Energy) functionality.
|
||||||
|
// It allows the device to advertise itself, connect to other devices, and
|
||||||
|
// exchange data over BLE.
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
#define BLE_RX_PACKET_SIZE 244
|
||||||
|
#define BLE_TX_PACKET_SIZE 64
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BLE_SWITCH_OFF = 0, // Turn off BLE advertising, disconnect
|
||||||
|
BLE_SWITCH_ON = 1, // Turn on BLE advertising
|
||||||
|
BLE_PAIRING_MODE = 2, // Enter pairing mode
|
||||||
|
BLE_DISCONNECT = 3, // Disconnect from the connected device
|
||||||
|
BLE_ERASE_BONDS = 4, // Erase all bonding information
|
||||||
|
BLE_ALLOW_PAIRING = 5, // Accept pairing request
|
||||||
|
BLE_REJECT_PAIRING = 6, // Reject pairing request
|
||||||
|
} ble_command_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BLE_NONE = 0, // No event
|
||||||
|
BLE_CONNECTED = 1, // Connected to a device
|
||||||
|
BLE_DISCONNECTED = 2, // Disconnected from a device
|
||||||
|
BLE_PAIRING_REQUEST = 3, // Pairing request received
|
||||||
|
BLE_PAIRING_CANCELLED = 4, // Pairing was cancelled by host
|
||||||
|
} ble_event_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ble_event_type_t type;
|
||||||
|
int connection_id;
|
||||||
|
uint8_t data_len;
|
||||||
|
uint8_t data[6];
|
||||||
|
} ble_event_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool connected;
|
||||||
|
uint8_t peer_count;
|
||||||
|
} ble_state_t;
|
||||||
|
|
||||||
|
// Initializes the BLE module
|
||||||
|
//
|
||||||
|
// Sets up the BLE hardware and software resources,
|
||||||
|
// preparing the module for operation.
|
||||||
|
// The function has no effect if the module was already initialized.
|
||||||
|
void ble_init(void);
|
||||||
|
|
||||||
|
// Deinitializes the BLE module
|
||||||
|
//
|
||||||
|
// Releases resources allocated during initialization
|
||||||
|
// and shuts down the BLE module.
|
||||||
|
void ble_deinit(void);
|
||||||
|
|
||||||
|
// Starts BLE operations
|
||||||
|
//
|
||||||
|
// Enables reception of messages over BLE
|
||||||
|
void ble_start(void);
|
||||||
|
|
||||||
|
// Stops BLE operations
|
||||||
|
//
|
||||||
|
// Disables reception of messages over BLE
|
||||||
|
// Flushes any queued messages
|
||||||
|
void ble_stop(void);
|
||||||
|
|
||||||
|
// Issues a command to the BLE module
|
||||||
|
//
|
||||||
|
// Sends a specific command to the BLE module for execution.
|
||||||
|
//
|
||||||
|
// Returns `true` if the command was successfully issued.
|
||||||
|
bool ble_issue_command(ble_command_t command);
|
||||||
|
|
||||||
|
// Reads an event from the BLE module
|
||||||
|
//
|
||||||
|
// Retrieves the next event from the BLE module's event queue.
|
||||||
|
//
|
||||||
|
// Returns `true` if an event was successfully read, `false` if no event is
|
||||||
|
// available.
|
||||||
|
bool ble_get_event(ble_event_t *event);
|
||||||
|
|
||||||
|
// Retrieves the current state of the BLE module
|
||||||
|
//
|
||||||
|
// Obtains the current operational state of the BLE module.
|
||||||
|
void ble_get_state(ble_state_t *state);
|
||||||
|
|
||||||
|
// Check if write is possible
|
||||||
|
bool ble_can_write(void);
|
||||||
|
|
||||||
|
// Writes data to a connected BLE device
|
||||||
|
//
|
||||||
|
// Sends data over an established BLE connection.
|
||||||
|
bool ble_write(const uint8_t *data, uint16_t len);
|
||||||
|
|
||||||
|
// Check if read is possible
|
||||||
|
bool ble_can_read(void);
|
||||||
|
|
||||||
|
// Reads data from a connected BLE device
|
||||||
|
//
|
||||||
|
// max_len indicates the maximum number of bytes to read. Rest of the data
|
||||||
|
// will be discarded.
|
||||||
|
//
|
||||||
|
// Returns the number of bytes actually read.
|
||||||
|
uint32_t ble_read(uint8_t *data, uint16_t max_len);
|
||||||
|
|
||||||
|
#endif
|
486
core/embed/io/ble/stm32/ble.c
Normal file
486
core/embed/io/ble/stm32/ble.c
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <io/ble.h>
|
||||||
|
#include <io/nrf.h>
|
||||||
|
#include <sys/systimer.h>
|
||||||
|
#include <util/tsqueue.h>
|
||||||
|
|
||||||
|
#include "ble_comm_defs.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BLE_MODE_OFF,
|
||||||
|
BLE_MODE_CONNECTABLE,
|
||||||
|
BLE_MODE_PAIRING,
|
||||||
|
BLE_MODE_DFU,
|
||||||
|
} ble_mode_t;
|
||||||
|
|
||||||
|
// changing value of TX_QUEUE_LEN is not allowed
|
||||||
|
// as it might result in order of messages being changed
|
||||||
|
#define TX_QUEUE_LEN 1
|
||||||
|
#define EVENT_QUEUE_LEN 4
|
||||||
|
#define RX_QUEUE_LEN 16
|
||||||
|
#define LOOP_PERIOD_MS 20
|
||||||
|
#define PING_PERIOD 100
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ble_mode_t mode_requested;
|
||||||
|
ble_mode_t mode_current;
|
||||||
|
bool connected;
|
||||||
|
uint8_t peer_count;
|
||||||
|
bool initialized;
|
||||||
|
bool status_valid;
|
||||||
|
bool accept_msgs;
|
||||||
|
ble_event_t event_queue_buffers[EVENT_QUEUE_LEN];
|
||||||
|
tsqueue_entry_t event_queue_entries[EVENT_QUEUE_LEN];
|
||||||
|
tsqueue_t event_queue;
|
||||||
|
|
||||||
|
uint8_t rx_queue_buffers[RX_QUEUE_LEN][BLE_RX_PACKET_SIZE];
|
||||||
|
tsqueue_entry_t rx_queue_entries[RX_QUEUE_LEN];
|
||||||
|
tsqueue_t rx_queue;
|
||||||
|
|
||||||
|
uint8_t tx_queue_buffers[TX_QUEUE_LEN][NRF_MAX_TX_DATA_SIZE];
|
||||||
|
tsqueue_entry_t ts_queue_entries[TX_QUEUE_LEN];
|
||||||
|
tsqueue_t tx_queue;
|
||||||
|
|
||||||
|
systimer_t *timer;
|
||||||
|
uint16_t ping_cntr;
|
||||||
|
} ble_driver_t;
|
||||||
|
|
||||||
|
static ble_driver_t g_ble_driver = {0};
|
||||||
|
|
||||||
|
static void ble_send_state_request(void) {
|
||||||
|
uint8_t cmd = INTERNAL_CMD_PING;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_send_advertising_on(bool whitelist) {
|
||||||
|
uint8_t data[2];
|
||||||
|
data[0] = INTERNAL_CMD_ADVERTISING_ON;
|
||||||
|
data[1] = whitelist ? 1 : 0;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, data, sizeof(data), NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_send_advertising_off(void) {
|
||||||
|
uint8_t cmd = INTERNAL_CMD_ADVERTISING_OFF;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ble_send_erase_bonds(void) {
|
||||||
|
uint8_t cmd = INTERNAL_CMD_ERASE_BONDS;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ble_send_disconnect(void) {
|
||||||
|
uint8_t cmd = INTERNAL_CMD_DISCONNECT;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_send_pairing_reject(void) {
|
||||||
|
uint8_t cmd = INTERNAL_CMD_REJECT_PAIRING;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_send_pairing_accept(void) {
|
||||||
|
uint8_t cmd = INTERNAL_CMD_ALLOW_PAIRING;
|
||||||
|
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len < sizeof(event_status_msg_t)) {
|
||||||
|
// insufficient data length
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_status_msg_t msg;
|
||||||
|
memcpy(&msg, data, sizeof(event_status_msg_t));
|
||||||
|
|
||||||
|
if (!drv->status_valid) {
|
||||||
|
if (msg.peer_count > 0) {
|
||||||
|
drv->mode_requested = BLE_MODE_CONNECTABLE;
|
||||||
|
} else {
|
||||||
|
drv->mode_requested = BLE_MODE_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drv->connected != msg.connected) {
|
||||||
|
if (msg.connected) {
|
||||||
|
// new connection
|
||||||
|
|
||||||
|
ble_event_t event = {.type = BLE_CONNECTED};
|
||||||
|
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// connection lost
|
||||||
|
ble_event_t event = {.type = BLE_DISCONNECTED};
|
||||||
|
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (drv->mode_current == BLE_MODE_PAIRING) {
|
||||||
|
drv->mode_requested = BLE_MODE_CONNECTABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->connected = msg.connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.advertising && !msg.advertising_whitelist) {
|
||||||
|
drv->mode_current = BLE_MODE_PAIRING;
|
||||||
|
} else if (msg.advertising) {
|
||||||
|
drv->mode_current = BLE_MODE_CONNECTABLE;
|
||||||
|
} else {
|
||||||
|
drv->mode_current = BLE_MODE_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->peer_count = msg.peer_count;
|
||||||
|
|
||||||
|
drv->status_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_process_rx_msg_pairing_request(const uint8_t *data,
|
||||||
|
uint32_t len) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < 7) {
|
||||||
|
// insufficient data length
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drv->mode_requested != BLE_MODE_PAIRING ||
|
||||||
|
drv->mode_current != BLE_MODE_PAIRING) {
|
||||||
|
ble_send_pairing_reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_event_t event = {.type = BLE_PAIRING_REQUEST, .data_len = 6};
|
||||||
|
memcpy(event.data, &data[1], 6);
|
||||||
|
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_process_rx_msg_pairing_cancelled(const uint8_t *data,
|
||||||
|
uint32_t len) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_event_t event = {.type = BLE_PAIRING_CANCELLED, .data_len = 0};
|
||||||
|
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_process_rx_msg(const uint8_t *data, uint32_t len) {
|
||||||
|
if (len < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data[0]) {
|
||||||
|
case INTERNAL_EVENT_STATUS:
|
||||||
|
ble_process_rx_msg_status(data, len);
|
||||||
|
break;
|
||||||
|
case INTERNAL_EVENT_PAIRING_REQUEST:
|
||||||
|
ble_process_rx_msg_pairing_request(data, len);
|
||||||
|
break;
|
||||||
|
case INTERNAL_EVENT_PAIRING_CANCELLED:
|
||||||
|
ble_process_rx_msg_pairing_cancelled(data, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_process_data(const uint8_t *data, uint32_t len) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != BLE_RX_PACKET_SIZE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tsqueue_enqueue(&drv->rx_queue, data, len, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// background loop, called from systimer every 10ms
|
||||||
|
static void ble_loop(void *context) {
|
||||||
|
(void)context;
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nrf_is_running()) {
|
||||||
|
if (drv->ping_cntr == 0) {
|
||||||
|
ble_send_state_request();
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->ping_cntr++;
|
||||||
|
if (drv->ping_cntr >= PING_PERIOD / LOOP_PERIOD_MS) {
|
||||||
|
drv->ping_cntr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t data[NRF_MAX_TX_DATA_SIZE] = {0};
|
||||||
|
if (tsqueue_dequeue(&drv->tx_queue, data, NRF_MAX_TX_DATA_SIZE, NULL,
|
||||||
|
NULL)) {
|
||||||
|
if (!nrf_send_msg(NRF_SERVICE_BLE, data, NRF_MAX_TX_DATA_SIZE, NULL,
|
||||||
|
NULL)) {
|
||||||
|
tsqueue_enqueue(&drv->tx_queue, data, NRF_MAX_TX_DATA_SIZE, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drv->mode_current != drv->mode_requested) {
|
||||||
|
if (drv->mode_requested == BLE_MODE_OFF) {
|
||||||
|
ble_send_advertising_off();
|
||||||
|
// if (drv->connected) {
|
||||||
|
// nrf_send_disconnect();
|
||||||
|
// }
|
||||||
|
} else if (drv->mode_requested == BLE_MODE_CONNECTABLE) {
|
||||||
|
ble_send_advertising_on(true);
|
||||||
|
} else if (drv->mode_requested == BLE_MODE_PAIRING) {
|
||||||
|
ble_send_advertising_on(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drv->status_valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_init(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(drv, 0, sizeof(ble_driver_t));
|
||||||
|
|
||||||
|
tsqueue_init(&drv->event_queue, drv->event_queue_entries,
|
||||||
|
(uint8_t *)drv->event_queue_buffers, sizeof(ble_event_t),
|
||||||
|
EVENT_QUEUE_LEN);
|
||||||
|
|
||||||
|
tsqueue_init(&drv->rx_queue, drv->rx_queue_entries,
|
||||||
|
(uint8_t *)drv->rx_queue_buffers, BLE_RX_PACKET_SIZE,
|
||||||
|
RX_QUEUE_LEN);
|
||||||
|
|
||||||
|
tsqueue_init(&drv->tx_queue, drv->ts_queue_entries,
|
||||||
|
(uint8_t *)drv->tx_queue_buffers, NRF_MAX_TX_DATA_SIZE,
|
||||||
|
TX_QUEUE_LEN);
|
||||||
|
|
||||||
|
drv->timer = systimer_create(ble_loop, NULL);
|
||||||
|
|
||||||
|
systimer_set_periodic(drv->timer, LOOP_PERIOD_MS);
|
||||||
|
|
||||||
|
nrf_init();
|
||||||
|
nrf_register_listener(NRF_SERVICE_BLE_MANAGER, ble_process_rx_msg);
|
||||||
|
nrf_register_listener(NRF_SERVICE_BLE, ble_process_data);
|
||||||
|
|
||||||
|
drv->initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_deinit(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
systimer_delete(drv->timer);
|
||||||
|
|
||||||
|
tsqueue_reset(&drv->event_queue);
|
||||||
|
tsqueue_reset(&drv->rx_queue);
|
||||||
|
tsqueue_reset(&drv->tx_queue);
|
||||||
|
|
||||||
|
nrf_unregister_listener(NRF_SERVICE_BLE);
|
||||||
|
nrf_unregister_listener(NRF_SERVICE_BLE_MANAGER);
|
||||||
|
|
||||||
|
drv->initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_connected(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drv->connected && nrf_is_running();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_start(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->accept_msgs = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_stop(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->accept_msgs = false;
|
||||||
|
tsqueue_reset(&drv->rx_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_can_write(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drv->connected || !drv->accept_msgs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !tsqueue_full(&drv->tx_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_write(const uint8_t *data, uint16_t len) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drv->connected || !drv->accept_msgs) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sent = nrf_send_msg(NRF_SERVICE_BLE, data, len, NULL, NULL);
|
||||||
|
|
||||||
|
if (!sent) {
|
||||||
|
bool queued = tsqueue_enqueue(&drv->tx_queue, data, len, NULL);
|
||||||
|
return queued;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_can_read(void) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !tsqueue_empty(&drv->rx_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ble_read(uint8_t *data, uint16_t max_len) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tsqueue_t *queue = &drv->rx_queue;
|
||||||
|
|
||||||
|
uint16_t read_len = 0;
|
||||||
|
|
||||||
|
tsqueue_dequeue(queue, data, max_len, &read_len, NULL);
|
||||||
|
|
||||||
|
return read_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_issue_command(ble_command_t command) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case BLE_SWITCH_OFF:
|
||||||
|
drv->mode_requested = BLE_MODE_OFF;
|
||||||
|
break;
|
||||||
|
case BLE_SWITCH_ON:
|
||||||
|
drv->mode_requested = BLE_MODE_CONNECTABLE;
|
||||||
|
break;
|
||||||
|
case BLE_PAIRING_MODE:
|
||||||
|
drv->mode_requested = BLE_MODE_PAIRING;
|
||||||
|
break;
|
||||||
|
case BLE_DISCONNECT:
|
||||||
|
ble_send_disconnect();
|
||||||
|
break;
|
||||||
|
case BLE_ERASE_BONDS:
|
||||||
|
ble_send_erase_bonds();
|
||||||
|
break;
|
||||||
|
case BLE_ALLOW_PAIRING:
|
||||||
|
ble_send_pairing_accept();
|
||||||
|
break;
|
||||||
|
case BLE_REJECT_PAIRING:
|
||||||
|
ble_send_pairing_reject();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// unknown command
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_get_event(ble_event_t *event) {
|
||||||
|
ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tsqueue_dequeue(&drv->event_queue, (uint8_t *)event, sizeof(*event),
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_get_state(ble_state_t *state) {
|
||||||
|
const ble_driver_t *drv = &g_ble_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
memset(state, 0, sizeof(ble_state_t));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->connected = drv->connected;
|
||||||
|
state->peer_count = drv->peer_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
59
core/embed/io/ble/stm32/ble_comm_defs.h
Normal file
59
core/embed/io/ble/stm32/ble_comm_defs.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREZORHAL_BLE_COMM_DEFS_H_
|
||||||
|
#define TREZORHAL_BLE_COMM_DEFS_H_
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t msg_id;
|
||||||
|
uint8_t connected;
|
||||||
|
uint8_t advertising;
|
||||||
|
uint8_t advertising_whitelist;
|
||||||
|
|
||||||
|
uint8_t peer_count;
|
||||||
|
uint8_t reserved[2];
|
||||||
|
uint8_t sd_version_number;
|
||||||
|
|
||||||
|
uint16_t sd_company_id;
|
||||||
|
uint16_t sd_subversion_number;
|
||||||
|
|
||||||
|
uint32_t app_version;
|
||||||
|
uint32_t bld_version;
|
||||||
|
|
||||||
|
} event_status_msg_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INTERNAL_EVENT_STATUS = 0x01,
|
||||||
|
INTERNAL_EVENT_PAIRING_REQUEST = 0x04,
|
||||||
|
INTERNAL_EVENT_PAIRING_CANCELLED = 0x05,
|
||||||
|
} internal_event_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INTERNAL_CMD_PING = 0x00,
|
||||||
|
INTERNAL_CMD_ADVERTISING_ON = 0x01,
|
||||||
|
INTERNAL_CMD_ADVERTISING_OFF = 0x02,
|
||||||
|
INTERNAL_CMD_ERASE_BONDS = 0x03,
|
||||||
|
INTERNAL_CMD_DISCONNECT = 0x04,
|
||||||
|
INTERNAL_CMD_ACK = 0x05,
|
||||||
|
INTERNAL_CMD_ALLOW_PAIRING = 0x06,
|
||||||
|
INTERNAL_CMD_REJECT_PAIRING = 0x07,
|
||||||
|
} internal_cmd_t;
|
||||||
|
#endif
|
53
core/embed/io/nrf/crc8.c
Normal file
53
core/embed/io/nrf/crc8.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Intel Corporation
|
||||||
|
* Copyright (c) 2017 Nordic Semiconductor ASA
|
||||||
|
* Copyright (c) 2015 Runtime Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "crc8.h"
|
||||||
|
|
||||||
|
static const uint8_t crc8_ccitt_small_table[16] = {
|
||||||
|
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
|
||||||
|
0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d};
|
||||||
|
|
||||||
|
uint8_t crc8_ccitt(uint8_t val, const void *buf, size_t cnt) {
|
||||||
|
size_t i;
|
||||||
|
const uint8_t *p = buf;
|
||||||
|
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
val ^= p[i];
|
||||||
|
val = (val << 4) ^ crc8_ccitt_small_table[val >> 4];
|
||||||
|
val = (val << 4) ^ crc8_ccitt_small_table[val >> 4];
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t crc8(const uint8_t *src, size_t len, uint8_t polynomial,
|
||||||
|
uint8_t initial_value, bool reversed) {
|
||||||
|
uint8_t crc = initial_value;
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
crc ^= src[i];
|
||||||
|
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if (reversed) {
|
||||||
|
if (crc & 0x01) {
|
||||||
|
crc = (crc >> 1) ^ polynomial;
|
||||||
|
} else {
|
||||||
|
crc >>= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (crc & 0x80) {
|
||||||
|
crc = (crc << 1) ^ polynomial;
|
||||||
|
} else {
|
||||||
|
crc <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return crc;
|
||||||
|
}
|
28
core/embed/io/nrf/crc8.h
Normal file
28
core/embed/io/nrf/crc8.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREZORHAL_NRF_CRC8_H
|
||||||
|
#define TREZORHAL_NRF_CRC8_H
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
uint8_t crc8(const uint8_t *src, size_t len, uint8_t polynomial,
|
||||||
|
uint8_t initial_value, bool reversed);
|
||||||
|
|
||||||
|
#endif
|
77
core/embed/io/nrf/inc/io/nrf.h
Normal file
77
core/embed/io/nrf/inc/io/nrf.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREZORHAL_NRF_H
|
||||||
|
#define TREZORHAL_NRF_H
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
// maximum data size allowed to be sent
|
||||||
|
#define NRF_MAX_TX_DATA_SIZE (64)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NRF_SERVICE_BLE = 0,
|
||||||
|
NRF_SERVICE_BLE_MANAGER = 1,
|
||||||
|
|
||||||
|
NRF_SERVICE_CNT // Number of services
|
||||||
|
} nrf_service_id_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NRF_STATUS_OK = 0, // Packet completed successfully
|
||||||
|
NRF_STATUS_TIMEOUT = 1, // Timeout occurred
|
||||||
|
NRF_STATUS_ERROR = 2, // General error
|
||||||
|
NRF_STATUS_ABORTED = 3, // Packet was aborted
|
||||||
|
} nrf_status_t;
|
||||||
|
|
||||||
|
typedef void (*nrf_rx_callback_t)(const uint8_t *data, uint32_t len);
|
||||||
|
typedef void (*nrf_tx_callback_t)(nrf_status_t status, void *context);
|
||||||
|
|
||||||
|
// Initialize the NRF driver
|
||||||
|
void nrf_init(void);
|
||||||
|
|
||||||
|
// Deinitialize the NRF driver
|
||||||
|
void nrf_deinit(void);
|
||||||
|
|
||||||
|
// Check that NRF is running
|
||||||
|
bool nrf_is_running(void);
|
||||||
|
|
||||||
|
// Register listener for a service
|
||||||
|
// The listener will be called when a message is received for the service
|
||||||
|
// The listener will be called from an interrupt context
|
||||||
|
// Returns false if a listener for the service is already registered
|
||||||
|
bool nrf_register_listener(nrf_service_id_t service,
|
||||||
|
nrf_rx_callback_t callback);
|
||||||
|
|
||||||
|
// Unregister listener for a service
|
||||||
|
void nrf_unregister_listener(nrf_service_id_t service);
|
||||||
|
|
||||||
|
// Send a message to a service
|
||||||
|
// The message will be queued and sent as soon as possible
|
||||||
|
// If the queue is full, the message will be dropped
|
||||||
|
// returns ID of the message if it was successfully queued, otherwise -1
|
||||||
|
int32_t nrf_send_msg(nrf_service_id_t service, const uint8_t *data,
|
||||||
|
uint32_t len, nrf_tx_callback_t callback, void *context);
|
||||||
|
|
||||||
|
// Abort a message by ID
|
||||||
|
// If the message is already sent or the id is not found, it does nothing and
|
||||||
|
// returns false If the message is queued, it will be removed from the queue If
|
||||||
|
// the message is being sent, it will be sent. The callback will not be called.
|
||||||
|
bool nrf_abort_msg(int32_t id);
|
||||||
|
|
||||||
|
#endif
|
39
core/embed/io/nrf/nrf_internal.h
Normal file
39
core/embed/io/nrf/nrf_internal.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREZORHAL_NRF_INTERNAL_H
|
||||||
|
#define TREZORHAL_NRF_INTERNAL_H
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
void nrf_dfu_comm_send(const uint8_t *data, uint32_t len);
|
||||||
|
uint32_t nrf_dfu_comm_receive(uint8_t *data, uint32_t len);
|
||||||
|
|
||||||
|
void nrf_int_send(const uint8_t *data, uint32_t len);
|
||||||
|
uint32_t nrf_int_receive(uint8_t *data, uint32_t len);
|
||||||
|
|
||||||
|
bool nrf_firmware_running(void);
|
||||||
|
|
||||||
|
bool nrf_reboot(void);
|
||||||
|
bool nrf_reboot_to_bootloader(void);
|
||||||
|
|
||||||
|
void nrf_signal_running(void);
|
||||||
|
void nrf_signal_off(void);
|
||||||
|
|
||||||
|
#endif
|
766
core/embed/io/nrf/stm32u5/nrf.c
Normal file
766
core/embed/io/nrf/stm32u5/nrf.c
Normal file
@ -0,0 +1,766 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
|
#include <trezor_bsp.h>
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <io/nrf.h>
|
||||||
|
#include <sys/irq.h>
|
||||||
|
#include <sys/mpu.h>
|
||||||
|
#include <sys/systick.h>
|
||||||
|
#include <util/tsqueue.h>
|
||||||
|
|
||||||
|
#include "../crc8.h"
|
||||||
|
#include "../nrf_internal.h"
|
||||||
|
|
||||||
|
#define MAX_SPI_DATA_SIZE (244)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t service_id;
|
||||||
|
uint8_t data[MAX_SPI_DATA_SIZE];
|
||||||
|
uint8_t crc;
|
||||||
|
} spi_packet_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t service_id;
|
||||||
|
uint8_t msg_len;
|
||||||
|
uint8_t data[NRF_MAX_TX_DATA_SIZE + 1];
|
||||||
|
// uint8_t crc; part of data, as it has variable position
|
||||||
|
} uart_packet_t;
|
||||||
|
|
||||||
|
#define UART_OVERHEAD_SIZE (sizeof(uart_packet_t) - NRF_MAX_TX_DATA_SIZE)
|
||||||
|
#define UART_HEADER_SIZE (UART_OVERHEAD_SIZE - 1)
|
||||||
|
|
||||||
|
#define TX_QUEUE_SIZE (8)
|
||||||
|
|
||||||
|
#define START_BYTE (0xA0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uart_packet_t packet;
|
||||||
|
nrf_tx_callback_t callback;
|
||||||
|
void *context;
|
||||||
|
} nrf_tx_request_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UART_HandleTypeDef urt;
|
||||||
|
DMA_HandleTypeDef urt_tx_dma;
|
||||||
|
|
||||||
|
uint8_t tx_buffers[TX_QUEUE_SIZE][sizeof(nrf_tx_request_t)];
|
||||||
|
tsqueue_entry_t tx_queue_entries[TX_QUEUE_SIZE];
|
||||||
|
tsqueue_t tx_queue;
|
||||||
|
nrf_tx_request_t tx_request;
|
||||||
|
int32_t tx_request_id;
|
||||||
|
|
||||||
|
uart_packet_t rx_buffer;
|
||||||
|
uint8_t rx_len;
|
||||||
|
uint8_t rx_byte;
|
||||||
|
uint16_t rx_idx;
|
||||||
|
|
||||||
|
SPI_HandleTypeDef spi;
|
||||||
|
DMA_HandleTypeDef spi_dma;
|
||||||
|
spi_packet_t long_rx_buffer;
|
||||||
|
|
||||||
|
bool comm_running;
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
|
nrf_rx_callback_t service_listeners[NRF_SERVICE_CNT];
|
||||||
|
|
||||||
|
} nrf_driver_t;
|
||||||
|
|
||||||
|
static nrf_driver_t g_nrf_driver = {0};
|
||||||
|
|
||||||
|
static void nrf_start(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_SPI_Receive_DMA(&drv->spi, (uint8_t *)&drv->long_rx_buffer,
|
||||||
|
sizeof(spi_packet_t));
|
||||||
|
|
||||||
|
tsqueue_reset(&drv->tx_queue);
|
||||||
|
HAL_UART_Receive_IT(&drv->urt, &drv->rx_byte, 1);
|
||||||
|
|
||||||
|
drv->comm_running = true;
|
||||||
|
|
||||||
|
nrf_signal_running();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nrf_abort_urt_comm(nrf_driver_t *drv) {
|
||||||
|
HAL_UART_AbortReceive(&drv->urt);
|
||||||
|
HAL_UART_AbortTransmit(&drv->urt);
|
||||||
|
|
||||||
|
if (drv->tx_request.callback != NULL) {
|
||||||
|
drv->tx_request.callback(NRF_STATUS_ERROR, drv->tx_request.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->rx_idx = 0;
|
||||||
|
drv->rx_len = 0;
|
||||||
|
drv->tx_request_id = -1;
|
||||||
|
|
||||||
|
while (tsqueue_dequeue(&drv->tx_queue, (uint8_t *)&drv->tx_request,
|
||||||
|
sizeof(nrf_tx_request_t), NULL, NULL)) {
|
||||||
|
if (drv->tx_request.callback != NULL) {
|
||||||
|
drv->tx_request.callback(NRF_STATUS_ERROR, drv->tx_request.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&drv->tx_request, 0, sizeof(nrf_tx_request_t));
|
||||||
|
|
||||||
|
tsqueue_reset(&drv->tx_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nrf_stop(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_signal_off();
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
drv->comm_running = false;
|
||||||
|
HAL_SPI_DMAStop(&drv->spi);
|
||||||
|
nrf_abort_urt_comm(drv);
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_init(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
|
||||||
|
if (drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__HAL_RCC_USART3_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPDMA1_CLK_ENABLE();
|
||||||
|
__HAL_RCC_SPI1_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOB_CLK_ENABLE();
|
||||||
|
__HAL_RCC_GPIOD_CLK_ENABLE();
|
||||||
|
|
||||||
|
memset(drv, 0, sizeof(*drv));
|
||||||
|
tsqueue_init(&drv->tx_queue, drv->tx_queue_entries,
|
||||||
|
(uint8_t *)drv->tx_buffers, sizeof(nrf_tx_request_t),
|
||||||
|
TX_QUEUE_SIZE);
|
||||||
|
|
||||||
|
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||||
|
|
||||||
|
// synchronization signals
|
||||||
|
NRF_OUT_RESET_CLK_ENA();
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_RESET_PORT, NRF_OUT_RESET_PIN, GPIO_PIN_SET);
|
||||||
|
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
|
||||||
|
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
|
||||||
|
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
GPIO_InitStructure.Pin = NRF_OUT_RESET_PIN;
|
||||||
|
HAL_GPIO_Init(NRF_OUT_RESET_PORT, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
NRF_IN_GPIO0_CLK_ENA();
|
||||||
|
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
|
||||||
|
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
|
||||||
|
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
GPIO_InitStructure.Pin = NRF_IN_GPIO0_PIN;
|
||||||
|
HAL_GPIO_Init(NRF_IN_GPIO0_PORT, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
NRF_IN_FW_RUNNING_CLK_ENA();
|
||||||
|
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
|
||||||
|
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
|
||||||
|
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
GPIO_InitStructure.Pin = NRF_IN_FW_RUNNING_PIN;
|
||||||
|
HAL_GPIO_Init(NRF_IN_FW_RUNNING_PORT, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
NRF_OUT_STAY_IN_BLD_CLK_ENA();
|
||||||
|
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
|
||||||
|
GPIO_InitStructure.Pull = GPIO_NOPULL;
|
||||||
|
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
GPIO_InitStructure.Pin = NRF_OUT_STAY_IN_BLD_PIN;
|
||||||
|
HAL_GPIO_Init(NRF_OUT_STAY_IN_BLD_PORT, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
NRF_OUT_FW_RUNNING_CLK_ENA();
|
||||||
|
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
|
||||||
|
GPIO_InitStructure.Pull = GPIO_NOPULL;
|
||||||
|
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
GPIO_InitStructure.Pin = NRF_OUT_FW_RUNNING_PIN;
|
||||||
|
HAL_GPIO_Init(NRF_OUT_FW_RUNNING_PORT, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
// UART PINS
|
||||||
|
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
|
||||||
|
GPIO_InitStructure.Pull = GPIO_NOPULL;
|
||||||
|
GPIO_InitStructure.Alternate = GPIO_AF7_USART3;
|
||||||
|
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
|
||||||
|
|
||||||
|
GPIO_InitStructure.Pin = GPIO_PIN_5;
|
||||||
|
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||||
|
GPIO_InitStructure.Pin = GPIO_PIN_10 | GPIO_PIN_1;
|
||||||
|
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
|
||||||
|
GPIO_InitStructure.Pin = GPIO_PIN_11;
|
||||||
|
HAL_GPIO_Init(GPIOD, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
drv->urt.Init.Mode = UART_MODE_TX_RX;
|
||||||
|
drv->urt.Init.BaudRate = 1000000;
|
||||||
|
drv->urt.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
|
||||||
|
drv->urt.Init.OverSampling = UART_OVERSAMPLING_16;
|
||||||
|
drv->urt.Init.Parity = UART_PARITY_NONE;
|
||||||
|
drv->urt.Init.StopBits = UART_STOPBITS_1;
|
||||||
|
drv->urt.Init.WordLength = UART_WORDLENGTH_8B;
|
||||||
|
drv->urt.Instance = USART3;
|
||||||
|
drv->urt.hdmatx = &drv->urt_tx_dma;
|
||||||
|
|
||||||
|
drv->urt_tx_dma.Init.Direction = DMA_MEMORY_TO_PERIPH;
|
||||||
|
drv->urt_tx_dma.Init.Mode = DMA_NORMAL;
|
||||||
|
drv->urt_tx_dma.Instance = GPDMA1_Channel1;
|
||||||
|
drv->urt_tx_dma.Init.Request = GPDMA1_REQUEST_USART3_TX;
|
||||||
|
drv->urt_tx_dma.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||||
|
drv->urt_tx_dma.Init.SrcInc = DMA_SINC_INCREMENTED;
|
||||||
|
drv->urt_tx_dma.Init.DestInc = DMA_DINC_FIXED;
|
||||||
|
drv->urt_tx_dma.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
|
||||||
|
drv->urt_tx_dma.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
|
||||||
|
drv->urt_tx_dma.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
|
||||||
|
drv->urt_tx_dma.Init.SrcBurstLength = 1;
|
||||||
|
drv->urt_tx_dma.Init.DestBurstLength = 1;
|
||||||
|
drv->urt_tx_dma.Init.TransferAllocatedPort =
|
||||||
|
DMA_SRC_ALLOCATED_PORT1 | DMA_DEST_ALLOCATED_PORT0;
|
||||||
|
drv->urt_tx_dma.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||||
|
|
||||||
|
drv->urt_tx_dma.Parent = &drv->urt;
|
||||||
|
HAL_DMA_Init(&drv->urt_tx_dma);
|
||||||
|
HAL_DMA_ConfigChannelAttributes(
|
||||||
|
&drv->urt_tx_dma, DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC |
|
||||||
|
DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC);
|
||||||
|
|
||||||
|
HAL_UART_Init(&drv->urt);
|
||||||
|
|
||||||
|
NVIC_SetPriority(GPDMA1_Channel1_IRQn, IRQ_PRI_NORMAL);
|
||||||
|
NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
|
||||||
|
NVIC_SetPriority(USART3_IRQn, IRQ_PRI_NORMAL);
|
||||||
|
NVIC_EnableIRQ(USART3_IRQn);
|
||||||
|
|
||||||
|
// SPI pins
|
||||||
|
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_1 | GPIO_PIN_4 | GPIO_PIN_6 | GPIO_PIN_7;
|
||||||
|
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
|
||||||
|
|
||||||
|
drv->spi_dma.Instance = GPDMA1_Channel2;
|
||||||
|
drv->spi_dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
|
||||||
|
drv->spi_dma.Init.Mode = DMA_NORMAL;
|
||||||
|
drv->spi_dma.Init.Request = GPDMA1_REQUEST_SPI1_RX;
|
||||||
|
drv->spi_dma.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
|
||||||
|
drv->spi_dma.Init.SrcInc = DMA_SINC_FIXED;
|
||||||
|
drv->spi_dma.Init.DestInc = DMA_DINC_INCREMENTED;
|
||||||
|
drv->spi_dma.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
|
||||||
|
drv->spi_dma.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
|
||||||
|
drv->spi_dma.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT;
|
||||||
|
drv->spi_dma.Init.SrcBurstLength = 1;
|
||||||
|
drv->spi_dma.Init.DestBurstLength = 1;
|
||||||
|
drv->spi_dma.Init.TransferAllocatedPort =
|
||||||
|
DMA_SRC_ALLOCATED_PORT1 | DMA_DEST_ALLOCATED_PORT0;
|
||||||
|
drv->spi_dma.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
|
||||||
|
|
||||||
|
HAL_DMA_Init(&drv->spi_dma);
|
||||||
|
HAL_DMA_ConfigChannelAttributes(
|
||||||
|
&drv->spi_dma, DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC |
|
||||||
|
DMA_CHANNEL_DEST_SEC);
|
||||||
|
|
||||||
|
drv->spi.Instance = SPI1;
|
||||||
|
drv->spi.Init.Mode = SPI_MODE_SLAVE;
|
||||||
|
drv->spi.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
|
||||||
|
drv->spi.Init.DataSize = SPI_DATASIZE_8BIT;
|
||||||
|
drv->spi.Init.CLKPolarity = SPI_POLARITY_LOW;
|
||||||
|
drv->spi.Init.CLKPhase = SPI_PHASE_1EDGE;
|
||||||
|
drv->spi.Init.NSS = SPI_NSS_HARD_INPUT;
|
||||||
|
drv->spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
|
||||||
|
drv->spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
||||||
|
drv->spi.Init.TIMode = SPI_TIMODE_DISABLE;
|
||||||
|
drv->spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
||||||
|
drv->spi.Init.CRCPolynomial = 0;
|
||||||
|
drv->spi.hdmarx = &drv->spi_dma;
|
||||||
|
|
||||||
|
drv->spi_dma.Parent = &drv->spi;
|
||||||
|
|
||||||
|
HAL_SPI_Init(&drv->spi);
|
||||||
|
|
||||||
|
NVIC_SetPriority(GPDMA1_Channel2_IRQn, IRQ_PRI_NORMAL);
|
||||||
|
NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);
|
||||||
|
NVIC_SetPriority(SPI1_IRQn, IRQ_PRI_NORMAL);
|
||||||
|
NVIC_EnableIRQ(SPI1_IRQn);
|
||||||
|
|
||||||
|
drv->tx_request_id = -1;
|
||||||
|
drv->initialized = true;
|
||||||
|
|
||||||
|
nrf_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_deinit(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_stop();
|
||||||
|
|
||||||
|
NVIC_DisableIRQ(GPDMA1_Channel2_IRQn);
|
||||||
|
NVIC_DisableIRQ(SPI1_IRQn);
|
||||||
|
|
||||||
|
__HAL_RCC_SPI1_FORCE_RESET();
|
||||||
|
__HAL_RCC_SPI1_RELEASE_RESET();
|
||||||
|
|
||||||
|
__HAL_RCC_USART1_FORCE_RESET();
|
||||||
|
__HAL_RCC_USART1_RELEASE_RESET();
|
||||||
|
|
||||||
|
drv->initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_register_listener(nrf_service_id_t service,
|
||||||
|
nrf_rx_callback_t callback) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service >= NRF_SERVICE_CNT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drv->service_listeners[service] != NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
drv->service_listeners[service] = callback;
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_unregister_listener(nrf_service_id_t service) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service >= NRF_SERVICE_CNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
drv->service_listeners[service] = NULL;
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nrf_process_msg(nrf_driver_t *drv, const uint8_t *data,
|
||||||
|
uint32_t len, nrf_service_id_t service) {
|
||||||
|
if (drv->service_listeners[service] != NULL) {
|
||||||
|
drv->service_listeners[service](data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DFU communication
|
||||||
|
/// ----------------------------------------------------------
|
||||||
|
|
||||||
|
void nrf_dfu_comm_send(const uint8_t *data, uint32_t len) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAL_UART_Transmit(&drv->urt, (uint8_t *)data, len, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t nrf_dfu_comm_receive(uint8_t *data, uint32_t len) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__HAL_UART_GET_FLAG(&drv->urt, UART_FLAG_RXNE)) {
|
||||||
|
HAL_StatusTypeDef result = HAL_UART_Receive(&drv->urt, data, len, 30);
|
||||||
|
|
||||||
|
if (result == HAL_OK) {
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drv->urt.RxXferCount == len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len - drv->urt.RxXferCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// UART communication
|
||||||
|
/// ---------------------------------------------------------
|
||||||
|
|
||||||
|
int32_t nrf_send_msg(nrf_service_id_t service, const uint8_t *data,
|
||||||
|
uint32_t len, nrf_tx_callback_t callback, void *context) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > NRF_MAX_TX_DATA_SIZE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (service > NRF_SERVICE_CNT) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nrf_is_running()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t id = 0;
|
||||||
|
|
||||||
|
nrf_tx_request_t tx_request = {0};
|
||||||
|
|
||||||
|
tx_request.callback = callback;
|
||||||
|
tx_request.context = context;
|
||||||
|
tx_request.packet.service_id = 0xA0 | (uint8_t)service;
|
||||||
|
tx_request.packet.msg_len = len + UART_OVERHEAD_SIZE;
|
||||||
|
memcpy(&tx_request.packet.data, data, len);
|
||||||
|
tx_request.packet.data[len] =
|
||||||
|
crc8((uint8_t *)&tx_request.packet, len + UART_OVERHEAD_SIZE - 1, 0x07,
|
||||||
|
0x00, false);
|
||||||
|
|
||||||
|
if (!tsqueue_enqueue(&drv->tx_queue, (uint8_t *)&tx_request,
|
||||||
|
sizeof(nrf_tx_request_t), &id)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
if (drv->tx_request_id <= 0) {
|
||||||
|
int32_t tx_id = 0;
|
||||||
|
if (tsqueue_dequeue(&drv->tx_queue, (uint8_t *)&drv->tx_request,
|
||||||
|
sizeof(nrf_tx_request_t), NULL, &tx_id)) {
|
||||||
|
HAL_UART_Transmit_DMA(&drv->urt, (uint8_t *)&drv->tx_request.packet,
|
||||||
|
drv->tx_request.packet.msg_len);
|
||||||
|
drv->tx_request_id = tx_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_abort_msg(int32_t id) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool aborted = tsqueue_abort(&drv->tx_queue, id, NULL, 0, NULL);
|
||||||
|
|
||||||
|
if (aborted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
if (drv->tx_request_id == id) {
|
||||||
|
drv->tx_request_id = -1;
|
||||||
|
irq_unlock(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nrf_is_valid_startbyte(uint8_t val) {
|
||||||
|
if ((val & 0xF0) != 0xA0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((val & 0x0F) >= NRF_SERVICE_CNT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *urt) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized && urt == &drv->urt) {
|
||||||
|
if (drv->rx_idx == 0) {
|
||||||
|
// received first byte: START BYTE
|
||||||
|
if (nrf_is_valid_startbyte(drv->rx_byte)) {
|
||||||
|
drv->rx_buffer.service_id = drv->rx_byte;
|
||||||
|
drv->rx_idx++;
|
||||||
|
} else {
|
||||||
|
// bad message, flush the line
|
||||||
|
drv->rx_idx = 0;
|
||||||
|
}
|
||||||
|
} else if (drv->rx_idx == 1) {
|
||||||
|
// received second byte: LEN
|
||||||
|
|
||||||
|
drv->rx_buffer.msg_len = drv->rx_byte;
|
||||||
|
drv->rx_len = drv->rx_byte;
|
||||||
|
|
||||||
|
if (drv->rx_len > sizeof(uart_packet_t)) {
|
||||||
|
drv->rx_len = 0;
|
||||||
|
} else {
|
||||||
|
drv->rx_idx++;
|
||||||
|
}
|
||||||
|
} else if (drv->rx_idx >= UART_HEADER_SIZE &&
|
||||||
|
drv->rx_idx < (drv->rx_len - 1)) {
|
||||||
|
// receive the rest of the message
|
||||||
|
|
||||||
|
drv->rx_buffer.data[drv->rx_idx - UART_HEADER_SIZE] = drv->rx_byte;
|
||||||
|
drv->rx_idx++;
|
||||||
|
|
||||||
|
if (drv->rx_idx >= NRF_MAX_TX_DATA_SIZE) {
|
||||||
|
// message is too long, flush the line
|
||||||
|
drv->rx_idx = 0;
|
||||||
|
drv->rx_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (drv->rx_idx == (drv->rx_len - 1)) {
|
||||||
|
// received last byte: CRC
|
||||||
|
|
||||||
|
uint8_t crc =
|
||||||
|
crc8((uint8_t *)&drv->rx_buffer, drv->rx_len - 1, 0x07, 0x00, false);
|
||||||
|
|
||||||
|
if (drv->rx_byte == crc) {
|
||||||
|
uart_packet_t *packet = &drv->rx_buffer;
|
||||||
|
nrf_process_msg(drv, drv->rx_buffer.data,
|
||||||
|
drv->rx_len - UART_OVERHEAD_SIZE,
|
||||||
|
packet->service_id & 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
drv->rx_idx = 0;
|
||||||
|
drv->rx_len = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// bad message, flush the line
|
||||||
|
drv->rx_idx = 0;
|
||||||
|
drv->rx_len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// receive the rest of the message, or new message in any case.
|
||||||
|
HAL_UART_Receive_IT(&drv->urt, &drv->rx_byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_UART_ErrorCallback(UART_HandleTypeDef *urt) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized && urt == &drv->urt) {
|
||||||
|
nrf_abort_urt_comm(drv);
|
||||||
|
|
||||||
|
HAL_UART_Receive_IT(&drv->urt, &drv->rx_byte, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *urt) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized && urt == &drv->urt) {
|
||||||
|
if (drv->tx_request.callback != NULL) {
|
||||||
|
drv->tx_request.callback(NRF_STATUS_OK, drv->tx_request.context);
|
||||||
|
}
|
||||||
|
drv->tx_request_id = -1;
|
||||||
|
memset(&drv->tx_request, 0, sizeof(nrf_tx_request_t));
|
||||||
|
|
||||||
|
bool msg =
|
||||||
|
tsqueue_dequeue(&drv->tx_queue, (uint8_t *)&drv->tx_request,
|
||||||
|
sizeof(nrf_tx_request_t), NULL, &drv->tx_request_id);
|
||||||
|
if (msg) {
|
||||||
|
HAL_UART_Transmit_DMA(&drv->urt, (uint8_t *)&drv->tx_request.packet,
|
||||||
|
drv->tx_request.packet.msg_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USART3_IRQHandler(void) {
|
||||||
|
IRQ_LOG_ENTER();
|
||||||
|
|
||||||
|
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||||
|
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized) {
|
||||||
|
HAL_UART_IRQHandler(&drv->urt);
|
||||||
|
}
|
||||||
|
|
||||||
|
mpu_restore(mpu_mode);
|
||||||
|
|
||||||
|
IRQ_LOG_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPDMA1_Channel1_IRQHandler(void) {
|
||||||
|
IRQ_LOG_ENTER();
|
||||||
|
|
||||||
|
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||||
|
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized) {
|
||||||
|
HAL_DMA_IRQHandler(&drv->urt_tx_dma);
|
||||||
|
}
|
||||||
|
|
||||||
|
mpu_restore(mpu_mode);
|
||||||
|
|
||||||
|
IRQ_LOG_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SPI communication
|
||||||
|
/// ----------------------------------------------------------
|
||||||
|
|
||||||
|
void GPDMA1_Channel2_IRQHandler(void) {
|
||||||
|
IRQ_LOG_ENTER();
|
||||||
|
|
||||||
|
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||||
|
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized) {
|
||||||
|
HAL_DMA_IRQHandler(&drv->spi_dma);
|
||||||
|
}
|
||||||
|
|
||||||
|
mpu_restore(mpu_mode);
|
||||||
|
|
||||||
|
IRQ_LOG_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPI1_IRQHandler(void) {
|
||||||
|
IRQ_LOG_ENTER();
|
||||||
|
|
||||||
|
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||||
|
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (drv->initialized) {
|
||||||
|
HAL_SPI_IRQHandler(&drv->spi);
|
||||||
|
}
|
||||||
|
|
||||||
|
mpu_restore(mpu_mode);
|
||||||
|
|
||||||
|
IRQ_LOG_EXIT();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hspi != &drv->spi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_packet_t *packet = &drv->long_rx_buffer;
|
||||||
|
|
||||||
|
uint8_t crc = crc8((uint8_t *)&drv->long_rx_buffer, sizeof(spi_packet_t) - 1,
|
||||||
|
0x07, 0x00, false);
|
||||||
|
|
||||||
|
if ((packet->service_id & 0xF0) != START_BYTE || packet->crc != crc) {
|
||||||
|
HAL_SPI_Abort(&drv->spi);
|
||||||
|
HAL_SPI_Receive_DMA(&drv->spi, (uint8_t *)&drv->long_rx_buffer,
|
||||||
|
sizeof(spi_packet_t));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nrf_process_msg(drv, drv->long_rx_buffer.data, sizeof(packet->data),
|
||||||
|
packet->service_id & 0x0F);
|
||||||
|
|
||||||
|
HAL_SPI_Receive_DMA(&drv->spi, (uint8_t *)&drv->long_rx_buffer,
|
||||||
|
sizeof(spi_packet_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GPIO communication
|
||||||
|
/// ---------------------------------------------------------
|
||||||
|
|
||||||
|
bool nrf_reboot_to_bootloader(void) {
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_RESET_PORT, NRF_OUT_RESET_PIN, GPIO_PIN_RESET);
|
||||||
|
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_STAY_IN_BLD_PORT, NRF_OUT_STAY_IN_BLD_PIN,
|
||||||
|
GPIO_PIN_SET);
|
||||||
|
|
||||||
|
systick_delay_ms(50);
|
||||||
|
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_RESET_PORT, NRF_OUT_RESET_PIN, GPIO_PIN_SET);
|
||||||
|
|
||||||
|
systick_delay_ms(1000);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_reboot(void) {
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_RESET_PORT, NRF_OUT_RESET_PIN, GPIO_PIN_SET);
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_STAY_IN_BLD_PORT, NRF_OUT_STAY_IN_BLD_PIN,
|
||||||
|
GPIO_PIN_RESET);
|
||||||
|
systick_delay_ms(50);
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_RESET_PORT, NRF_OUT_RESET_PIN, GPIO_PIN_RESET);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_signal_running(void) {
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_FW_RUNNING_PORT, NRF_OUT_FW_RUNNING_PIN,
|
||||||
|
GPIO_PIN_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_signal_off(void) {
|
||||||
|
HAL_GPIO_WritePin(NRF_OUT_FW_RUNNING_PORT, NRF_OUT_FW_RUNNING_PIN,
|
||||||
|
GPIO_PIN_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_firmware_running(void) {
|
||||||
|
return HAL_GPIO_ReadPin(NRF_IN_FW_RUNNING_PORT, NRF_IN_FW_RUNNING_PIN) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_is_running(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nrf_firmware_running()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drv->comm_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nrf_set_dfu_mode(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// if (nrf_reboot_to_bootloader()) {
|
||||||
|
// drv->mode_current = BLE_MODE_DFU;
|
||||||
|
// } else {
|
||||||
|
// drv->status_valid = false;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nrf_is_dfu_mode(void) {
|
||||||
|
nrf_driver_t *drv = &g_nrf_driver;
|
||||||
|
|
||||||
|
if (!drv->initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -42,6 +42,10 @@
|
|||||||
#include <io/button.h>
|
#include <io/button.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
#include <io/ble.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_CONSUMPTION_MASK
|
#ifdef USE_CONSUMPTION_MASK
|
||||||
#include <sec/consumption_mask.h>
|
#include <sec/consumption_mask.h>
|
||||||
#endif
|
#endif
|
||||||
@ -177,6 +181,10 @@ void drivers_init() {
|
|||||||
haptic_init();
|
haptic_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
ble_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_OPTIGA
|
#ifdef USE_OPTIGA
|
||||||
|
|
||||||
#if !PYOPT
|
#if !PYOPT
|
||||||
|
@ -39,6 +39,10 @@
|
|||||||
#include <util/translations.h>
|
#include <util/translations.h>
|
||||||
#include <util/unit_properties.h>
|
#include <util/unit_properties.h>
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
#include <io/ble.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
#include <io/button.h>
|
#include <io/button.h>
|
||||||
#endif
|
#endif
|
||||||
@ -673,6 +677,47 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
|
|||||||
firmware_hash_callback_wrapper, callback_context);
|
firmware_hash_callback_wrapper, callback_context);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
case SYSCALL_BLE_START: {
|
||||||
|
ble_start();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_ISSUE_COMMAND: {
|
||||||
|
ble_command_t command = args[0];
|
||||||
|
ble_issue_command(command);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_GET_STATE: {
|
||||||
|
ble_state_t *state = (ble_state_t *)args[0];
|
||||||
|
ble_get_state__verified(state);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_GET_EVENT: {
|
||||||
|
ble_event_t *event = (ble_event_t *)args[0];
|
||||||
|
args[0] = ble_get_event__verified(event);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_CAN_WRITE: {
|
||||||
|
args[0] = ble_can_write();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_WRITE: {
|
||||||
|
uint8_t *data = (uint8_t *)args[0];
|
||||||
|
size_t len = args[1];
|
||||||
|
args[0] = ble_write__verified(data, len);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_CAN_READ: {
|
||||||
|
args[0] = ble_can_read();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case SYSCALL_BLE_READ: {
|
||||||
|
uint8_t *data = (uint8_t *)args[0];
|
||||||
|
size_t len = args[1];
|
||||||
|
args[0] = ble_read__verified(data, len);
|
||||||
|
} break;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
args[0] = 0xffffffff;
|
args[0] = 0xffffffff;
|
||||||
break;
|
break;
|
||||||
|
@ -139,6 +139,14 @@ typedef enum {
|
|||||||
SYSCALL_FIRMWARE_GET_VENDOR,
|
SYSCALL_FIRMWARE_GET_VENDOR,
|
||||||
SYSCALL_FIRMWARE_CALC_HASH,
|
SYSCALL_FIRMWARE_CALC_HASH,
|
||||||
|
|
||||||
|
SYSCALL_BLE_START,
|
||||||
|
SYSCALL_BLE_ISSUE_COMMAND,
|
||||||
|
SYSCALL_BLE_GET_EVENT,
|
||||||
|
SYSCALL_BLE_GET_STATE,
|
||||||
|
SYSCALL_BLE_CAN_WRITE,
|
||||||
|
SYSCALL_BLE_WRITE,
|
||||||
|
SYSCALL_BLE_CAN_READ,
|
||||||
|
SYSCALL_BLE_READ,
|
||||||
} syscall_number_t;
|
} syscall_number_t;
|
||||||
|
|
||||||
#endif // SYSCALL_NUMBERS_H
|
#endif // SYSCALL_NUMBERS_H
|
||||||
|
@ -645,4 +645,40 @@ secbool firmware_calc_hash(const uint8_t *challenge, size_t challenge_len,
|
|||||||
SYSCALL_FIRMWARE_CALC_HASH);
|
SYSCALL_FIRMWARE_CALC_HASH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// ble.h
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#include <io/ble.h>
|
||||||
|
|
||||||
|
void ble_start(void) { syscall_invoke0(SYSCALL_BLE_START); }
|
||||||
|
|
||||||
|
bool ble_issue_command(ble_command_t command) {
|
||||||
|
return (bool)syscall_invoke1((uint32_t)command, SYSCALL_BLE_ISSUE_COMMAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_get_event(ble_event_t *event) {
|
||||||
|
return (bool)syscall_invoke1((uint32_t)event, SYSCALL_BLE_GET_EVENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ble_get_state(ble_state_t *state) {
|
||||||
|
syscall_invoke1((uint32_t)state, SYSCALL_BLE_GET_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_can_write(void) { return syscall_invoke0(SYSCALL_BLE_CAN_WRITE); }
|
||||||
|
|
||||||
|
bool ble_write(const uint8_t *data, uint16_t len) {
|
||||||
|
return syscall_invoke2((uint32_t)data, len, SYSCALL_BLE_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_can_read(void) { return syscall_invoke0(SYSCALL_BLE_CAN_READ); }
|
||||||
|
|
||||||
|
uint32_t ble_read(uint8_t *data, uint16_t len) {
|
||||||
|
return (uint32_t)syscall_invoke2((uint32_t)data, len, SYSCALL_BLE_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // KERNEL_MODE
|
#endif // KERNEL_MODE
|
||||||
|
@ -715,4 +715,58 @@ access_violation:
|
|||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef USE_BLE
|
||||||
|
void ble_get_state__verified(ble_state_t *state) {
|
||||||
|
if (!probe_write_access(state, sizeof(*state))) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_state_t state_copy = {0};
|
||||||
|
ble_get_state(&state_copy);
|
||||||
|
*state = state_copy;
|
||||||
|
return;
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_get_event__verified(ble_event_t *event) {
|
||||||
|
if (!probe_write_access(event, sizeof(*event))) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ble_get_event(event);
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ble_write__verified(const uint8_t *data, size_t len) {
|
||||||
|
if (!probe_read_access(data, len)) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ble_write(data, len);
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ble_read__verified(uint8_t *data, size_t len) {
|
||||||
|
if (!probe_write_access(data, len)) {
|
||||||
|
goto access_violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ble_read(data, len);
|
||||||
|
|
||||||
|
access_violation:
|
||||||
|
apptask_access_violation();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // SYSCALL_DISPATCH
|
#endif // SYSCALL_DISPATCH
|
||||||
|
@ -186,6 +186,21 @@ secbool firmware_calc_hash__verified(const uint8_t *challenge,
|
|||||||
|
|
||||||
secbool firmware_get_vendor__verified(char *buff, size_t buff_size);
|
secbool firmware_get_vendor__verified(char *buff, size_t buff_size);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
#ifdef USE_BLE
|
||||||
|
|
||||||
|
#include <io/ble.h>
|
||||||
|
|
||||||
|
void ble_get_state__verified(ble_state_t *state);
|
||||||
|
|
||||||
|
bool ble_get_event__verified(ble_event_t *event);
|
||||||
|
|
||||||
|
bool ble_write__verified(const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
secbool ble_read__verified(uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // SYSCALL_DISPATCH
|
#endif // SYSCALL_DISPATCH
|
||||||
|
|
||||||
#endif // TREZORHAL_SYSCALL_VERIFIERS_H
|
#endif // TREZORHAL_SYSCALL_VERIFIERS_H
|
||||||
|
67
core/embed/util/tsqueue/inc/util/tsqueue.h
Normal file
67
core/embed/util/tsqueue/inc/util/tsqueue.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TREZORHAL_TSQUEUE_H
|
||||||
|
#define TREZORHAL_TSQUEUE_H
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *buffer; // Pointer to the data buffer
|
||||||
|
uint16_t len; // Length of data in the buffer
|
||||||
|
int32_t id; // ID of the entry
|
||||||
|
bool used; // Used flag
|
||||||
|
bool aborted; // Aborted flag
|
||||||
|
} tsqueue_entry_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
tsqueue_entry_t *entries; // Array of queue entries
|
||||||
|
int rix; // Read index
|
||||||
|
int wix; // Write index
|
||||||
|
int qlen; // Queue length
|
||||||
|
uint16_t size; // Size of each buffer
|
||||||
|
int32_t next_id; // ID of the next item
|
||||||
|
} tsqueue_t;
|
||||||
|
|
||||||
|
// Initialize the queue
|
||||||
|
void tsqueue_init(tsqueue_t *queue, tsqueue_entry_t *entries,
|
||||||
|
uint8_t *buffer_mem, uint16_t size, int qlen);
|
||||||
|
|
||||||
|
void tsqueue_reset(tsqueue_t *queue);
|
||||||
|
|
||||||
|
// Insert data into the queue
|
||||||
|
bool tsqueue_enqueue(tsqueue_t *queue, const uint8_t *data, uint16_t len,
|
||||||
|
int32_t *id);
|
||||||
|
|
||||||
|
// Read data from the queue
|
||||||
|
bool tsqueue_dequeue(tsqueue_t *queue, uint8_t *data, uint16_t max_len,
|
||||||
|
uint16_t *len, int32_t *id);
|
||||||
|
|
||||||
|
// Checks if the queue is full
|
||||||
|
bool tsqueue_full(tsqueue_t *queue);
|
||||||
|
|
||||||
|
// Checks if the queue is empty
|
||||||
|
bool tsqueue_empty(tsqueue_t *queue);
|
||||||
|
|
||||||
|
// Aborts item in the queue
|
||||||
|
// The space in the queue is not freed until the item is attempted to be read
|
||||||
|
bool tsqueue_abort(tsqueue_t *queue, int32_t id, uint8_t *data,
|
||||||
|
uint16_t max_len, uint16_t *len);
|
||||||
|
|
||||||
|
#endif
|
192
core/embed/util/tsqueue/tsqueue.c
Normal file
192
core/embed/util/tsqueue/tsqueue.c
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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 <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <sys/irq.h>
|
||||||
|
#include <util/tsqueue.h>
|
||||||
|
|
||||||
|
// Initialize the queue
|
||||||
|
void tsqueue_init(tsqueue_t *queue, tsqueue_entry_t *entries,
|
||||||
|
uint8_t *buffer_mem, uint16_t size, int qlen) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
queue->entries = entries;
|
||||||
|
queue->rix = 0;
|
||||||
|
queue->wix = 0;
|
||||||
|
queue->qlen = qlen;
|
||||||
|
queue->size = size;
|
||||||
|
queue->next_id = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < qlen; i++) {
|
||||||
|
if (buffer_mem != NULL) {
|
||||||
|
queue->entries[i].buffer = buffer_mem + i * size;
|
||||||
|
memset(queue->entries[i].buffer, 0, size);
|
||||||
|
}
|
||||||
|
queue->entries[i].len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tsqueue_entry_reset(tsqueue_entry_t *entry, uint32_t data_size) {
|
||||||
|
entry->len = 0;
|
||||||
|
entry->used = 0;
|
||||||
|
entry->aborted = false;
|
||||||
|
entry->id = 0;
|
||||||
|
memset(entry->buffer, 0, data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tsqueue_reset(tsqueue_t *queue) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
queue->rix = 0;
|
||||||
|
queue->wix = 0;
|
||||||
|
queue->next_id = 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < queue->qlen; i++) {
|
||||||
|
tsqueue_entry_reset(&queue->entries[i], queue->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t get_next_id(tsqueue_t *queue) {
|
||||||
|
int val = 1;
|
||||||
|
if (queue->next_id < INT32_MAX) {
|
||||||
|
val = queue->next_id;
|
||||||
|
queue->next_id++;
|
||||||
|
} else {
|
||||||
|
queue->next_id = 2;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tsqueue_enqueue(tsqueue_t *queue, const uint8_t *data, uint16_t len,
|
||||||
|
int32_t *id) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
|
||||||
|
if (queue->entries[queue->wix].used) {
|
||||||
|
// Full queue
|
||||||
|
irq_unlock(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > queue->size) {
|
||||||
|
irq_unlock(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(queue->entries[queue->wix].buffer, data, len);
|
||||||
|
queue->entries[queue->wix].id = get_next_id(queue);
|
||||||
|
queue->entries[queue->wix].len = len;
|
||||||
|
queue->entries[queue->wix].used = true;
|
||||||
|
|
||||||
|
if (id != NULL) {
|
||||||
|
*id = queue->entries[queue->wix].id;
|
||||||
|
}
|
||||||
|
queue->wix = (queue->wix + 1) % queue->qlen;
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tsqueue_discard_aborted(tsqueue_t *queue) {
|
||||||
|
while (queue->entries[queue->rix].aborted) {
|
||||||
|
tsqueue_entry_reset(&queue->entries[queue->rix], queue->size);
|
||||||
|
queue->rix = (queue->rix + 1) % queue->qlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tsqueue_dequeue(tsqueue_t *queue, uint8_t *data, uint16_t max_len,
|
||||||
|
uint16_t *len, int32_t *id) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
|
||||||
|
tsqueue_discard_aborted(queue);
|
||||||
|
|
||||||
|
if (!queue->entries[queue->rix].used) {
|
||||||
|
irq_unlock(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != NULL) {
|
||||||
|
*len = queue->entries[queue->rix].len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != NULL) {
|
||||||
|
*id = queue->entries[queue->rix].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data, queue->entries[queue->rix].buffer,
|
||||||
|
MIN(queue->entries[queue->rix].len, max_len));
|
||||||
|
|
||||||
|
tsqueue_entry_reset(queue->entries + queue->rix, queue->size);
|
||||||
|
queue->rix = (queue->rix + 1) % queue->qlen;
|
||||||
|
|
||||||
|
tsqueue_discard_aborted(queue);
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the queue is full
|
||||||
|
bool tsqueue_full(tsqueue_t *queue) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
|
||||||
|
tsqueue_discard_aborted(queue);
|
||||||
|
|
||||||
|
bool full = queue->entries[queue->wix].used;
|
||||||
|
irq_unlock(key);
|
||||||
|
return full;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tsqueue_empty(tsqueue_t *queue) {
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
|
||||||
|
tsqueue_discard_aborted(queue);
|
||||||
|
|
||||||
|
bool empty = !queue->entries[queue->rix].used;
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tsqueue_abort(tsqueue_t *queue, int32_t id, uint8_t *data,
|
||||||
|
uint16_t max_len, uint16_t *len) {
|
||||||
|
bool found = false;
|
||||||
|
irq_key_t key = irq_lock();
|
||||||
|
|
||||||
|
for (int i = 0; i < queue->qlen; i++) {
|
||||||
|
if (queue->entries[i].used && queue->entries[i].id == id) {
|
||||||
|
queue->entries[i].aborted = true;
|
||||||
|
if (len != NULL) {
|
||||||
|
*len = queue->entries[i].len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != NULL) {
|
||||||
|
memcpy(data, queue->entries[i].buffer,
|
||||||
|
MIN(queue->entries[i].len, max_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
irq_unlock(key);
|
||||||
|
return found;
|
||||||
|
}
|
@ -29,6 +29,7 @@ def stm32f4_common_files(env, defines, sources, paths):
|
|||||||
"embed/util/flash/inc",
|
"embed/util/flash/inc",
|
||||||
"embed/util/fwutils/inc",
|
"embed/util/fwutils/inc",
|
||||||
"embed/util/option_bytes/inc",
|
"embed/util/option_bytes/inc",
|
||||||
|
"embed/util/tsqueue/inc",
|
||||||
"embed/util/unit_properties/inc",
|
"embed/util/unit_properties/inc",
|
||||||
"vendor/micropython/lib/cmsis/inc",
|
"vendor/micropython/lib/cmsis/inc",
|
||||||
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc",
|
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc",
|
||||||
@ -87,6 +88,7 @@ def stm32f4_common_files(env, defines, sources, paths):
|
|||||||
"embed/util/flash/stm32f4/flash_otp.c",
|
"embed/util/flash/stm32f4/flash_otp.c",
|
||||||
"embed/util/fwutils/fwutils.c",
|
"embed/util/fwutils/fwutils.c",
|
||||||
"embed/util/option_bytes/stm32f4/option_bytes.c",
|
"embed/util/option_bytes/stm32f4/option_bytes.c",
|
||||||
|
"embed/util/tsqueue/tsqueue.c",
|
||||||
"embed/util/unit_properties/stm32/unit_properties.c",
|
"embed/util/unit_properties/stm32/unit_properties.c",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ def stm32u5_common_files(env, defines, sources, paths):
|
|||||||
"embed/util/flash/inc",
|
"embed/util/flash/inc",
|
||||||
"embed/util/fwutils/inc",
|
"embed/util/fwutils/inc",
|
||||||
"embed/util/option_bytes/inc",
|
"embed/util/option_bytes/inc",
|
||||||
|
"embed/util/tsqueue/inc",
|
||||||
"embed/util/unit_properties/inc",
|
"embed/util/unit_properties/inc",
|
||||||
"vendor/stm32u5xx_hal_driver/Inc",
|
"vendor/stm32u5xx_hal_driver/Inc",
|
||||||
"vendor/cmsis_device_u5/Include",
|
"vendor/cmsis_device_u5/Include",
|
||||||
@ -106,6 +107,7 @@ def stm32u5_common_files(env, defines, sources, paths):
|
|||||||
"embed/util/flash/stm32u5/flash_otp.c",
|
"embed/util/flash/stm32u5/flash_otp.c",
|
||||||
"embed/util/fwutils/fwutils.c",
|
"embed/util/fwutils/fwutils.c",
|
||||||
"embed/util/option_bytes/stm32u5/option_bytes.c",
|
"embed/util/option_bytes/stm32u5/option_bytes.c",
|
||||||
|
"embed/util/tsqueue/tsqueue.c",
|
||||||
"embed/util/unit_properties/stm32/unit_properties.c",
|
"embed/util/unit_properties/stm32/unit_properties.c",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user