mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-01 15:40:49 +00:00
Put a bit of order in the src directory, group and name things appropriately
This commit is contained in:
160
src/platform/esp32/BluetoothSoftwareUpdate.cpp
Normal file
160
src/platform/esp32/BluetoothSoftwareUpdate.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
#ifndef USE_NEW_ESP32_BLUETOOTH
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "../concurrency/LockGuard.h"
|
||||
#include "../graphics/Screen.h"
|
||||
#include "../main.h"
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RadioLibInterface.h"
|
||||
#include "configuration.h"
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
|
||||
#include <CRC32.h>
|
||||
#include <Update.h>
|
||||
|
||||
int16_t updateResultHandle = -1;
|
||||
|
||||
static CRC32 crc;
|
||||
|
||||
static uint32_t updateExpectedSize, updateActualSize;
|
||||
static uint8_t update_result;
|
||||
static uint8_t update_region;
|
||||
|
||||
static concurrency::Lock *updateLock;
|
||||
|
||||
/// Handle writes & reads to total size
|
||||
int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
concurrency::LockGuard g(updateLock);
|
||||
|
||||
// Check if there is enough to OTA Update
|
||||
chr_readwrite32le(&updateExpectedSize, ctxt);
|
||||
|
||||
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR && updateExpectedSize != 0) {
|
||||
updateActualSize = 0;
|
||||
crc.reset();
|
||||
if (Update.isRunning())
|
||||
Update.abort();
|
||||
bool canBegin = Update.begin(updateExpectedSize, update_region);
|
||||
DEBUG_MSG("Setting region %d update size %u, result %d\n", update_region, updateExpectedSize, canBegin);
|
||||
if (!canBegin) {
|
||||
// Indicate failure by forcing the size to 0 (client will read it back)
|
||||
updateExpectedSize = 0;
|
||||
} else {
|
||||
// This totally breaks abstraction to up up into the app layer for this, but quick hack to make sure we only
|
||||
// talk to one service during the sw update.
|
||||
// DEBUG_MSG("FIXME, crufty shutdown of mesh bluetooth for sw update.");
|
||||
// void stopMeshBluetoothService();
|
||||
// stopMeshBluetoothService();
|
||||
|
||||
screen->startFirmwareUpdateScreen();
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->disable(); // FIXME, nasty hack - the RF95 ISR/SPI code on ESP32 can fail while we
|
||||
// are writing flash - shut the radio off during updates
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_BLOCKSIZE_FOR_BT 512
|
||||
|
||||
/// Handle writes to data
|
||||
int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
concurrency::LockGuard g(updateLock);
|
||||
|
||||
static uint8_t
|
||||
data[MAX_BLOCKSIZE_FOR_BT]; // we temporarily copy here because I'm worried that a fast sender might be able overwrite srcbuf
|
||||
|
||||
uint16_t len = 0;
|
||||
|
||||
auto rc = ble_hs_mbuf_to_flat(ctxt->om, data, sizeof(data), &len);
|
||||
assert(rc == 0);
|
||||
|
||||
// DEBUG_MSG("Writing %u\n", len);
|
||||
crc.update(data, len);
|
||||
Update.write(data, len);
|
||||
updateActualSize += len;
|
||||
powerFSM.trigger(EVENT_FIRMWARE_UPDATE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Handle writes to crc32
|
||||
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
concurrency::LockGuard g(updateLock);
|
||||
uint32_t expectedCRC = 0;
|
||||
chr_readwrite32le(&expectedCRC, ctxt);
|
||||
|
||||
uint32_t actualCRC = crc.finalize();
|
||||
DEBUG_MSG("expected CRC %u\n", expectedCRC);
|
||||
|
||||
uint8_t result = 0xff;
|
||||
|
||||
if (updateActualSize != updateExpectedSize) {
|
||||
DEBUG_MSG("Expected %u bytes, but received %u bytes!\n", updateExpectedSize, updateActualSize);
|
||||
result = 0xe1; // FIXME, use real error codes
|
||||
} else if (actualCRC != expectedCRC) // Check the CRC before asking the update to happen.
|
||||
{
|
||||
DEBUG_MSG("Invalid CRC! expected=%u, actual=%u\n", expectedCRC, actualCRC);
|
||||
result = 0xe0; // FIXME, use real error codes
|
||||
} else {
|
||||
if (Update.end()) {
|
||||
if (update_region == U_SPIFFS) {
|
||||
DEBUG_MSG("Filesystem updated!\n");
|
||||
nodeDB.saveToDisk(); // Since we just wiped the filesystem, we need to save our current state
|
||||
} else {
|
||||
DEBUG_MSG("Appload updated, rebooting in 5 seconds!\n");
|
||||
rebootAtMsec = millis() + 5000;
|
||||
}
|
||||
} else {
|
||||
DEBUG_MSG("Error Occurred. Error #: %d\n", Update.getError());
|
||||
}
|
||||
result = Update.getError();
|
||||
}
|
||||
|
||||
if (RadioLibInterface::instance)
|
||||
RadioLibInterface::instance->startReceive(); // Resume radio
|
||||
|
||||
assert(updateResultHandle >= 0);
|
||||
update_result = result;
|
||||
DEBUG_MSG("BLE notify update result\n");
|
||||
auto res = ble_gattc_notify(curConnectionHandle, updateResultHandle);
|
||||
assert(res == 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
return chr_readwrite8(&update_result, sizeof(update_result), ctxt);
|
||||
}
|
||||
|
||||
int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
|
||||
{
|
||||
return chr_readwrite8(&update_region, sizeof(update_region), ctxt);
|
||||
}
|
||||
|
||||
/*
|
||||
See bluetooth-api.md
|
||||
|
||||
*/
|
||||
void reinitUpdateService()
|
||||
{
|
||||
if (!updateLock)
|
||||
updateLock = new concurrency::Lock();
|
||||
|
||||
auto res = ble_gatts_count_cfg(gatt_update_svcs); // assigns handles? see docstring for note about clearing the handle list
|
||||
// before calling SLEEP SUPPORT
|
||||
assert(res == 0);
|
||||
|
||||
res = ble_gatts_add_svcs(gatt_update_svcs);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
#endif //#ifndef USE_NEW_ESP32_BLUETOOTH
|
||||
29
src/platform/esp32/BluetoothSoftwareUpdate.h
Normal file
29
src/platform/esp32/BluetoothSoftwareUpdate.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef USE_NEW_ESP32_BLUETOOTH
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nimble/NimbleDefs.h"
|
||||
|
||||
void reinitUpdateService();
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int update_size_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_result_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_crc32_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
int update_region_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg);
|
||||
|
||||
extern const struct ble_gatt_svc_def gatt_update_svcs[];
|
||||
|
||||
extern const ble_uuid128_t update_result_uuid, update_region_uuid;
|
||||
|
||||
extern int16_t updateResultHandle;
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif //#ifndef USE_NEW_ESP32_BLUETOOTH
|
||||
12
src/platform/esp32/CallbackCharacteristic.h
Normal file
12
src/platform/esp32/CallbackCharacteristic.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "BLECharacteristic.h"
|
||||
#include "PowerFSM.h" // FIXME - someday I want to make this OTA thing a separate lb at at that point it can't touch this
|
||||
|
||||
/**
|
||||
* A characterstic with a set of overridable callbacks
|
||||
*/
|
||||
class CallbackCharacteristic : public BLECharacteristic, public BLECharacteristicCallbacks
|
||||
{
|
||||
public:
|
||||
CallbackCharacteristic(const char *uuid, uint32_t btprops) : BLECharacteristic(uuid, btprops) { setCallbacks(this); }
|
||||
};
|
||||
259
src/platform/esp32/ESP32Bluetooth.cpp
Normal file
259
src/platform/esp32/ESP32Bluetooth.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#ifdef USE_NEW_ESP32_BLUETOOTH
|
||||
|
||||
#include "configuration.h"
|
||||
#include "ESP32Bluetooth.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "sleep.h"
|
||||
#include "main.h"
|
||||
#include "mesh/PhoneAPI.h"
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
//static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
|
||||
//static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
|
||||
//static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
|
||||
//static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
|
||||
|
||||
//static BLEDis bledis; // DIS (Device Information Service) helper class instance
|
||||
//static BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
//static BLEDfu bledfu; // DFU software update helper service
|
||||
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
// proccess at once
|
||||
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
|
||||
static uint8_t fromRadioBytes[FromRadio_size];
|
||||
|
||||
NimBLECharacteristic *FromNumCharacteristic;
|
||||
NimBLEServer *bleServer;
|
||||
|
||||
static bool passkeyShowing;
|
||||
static uint32_t doublepressed;
|
||||
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
void BluetoothPhoneAPI::onNowHasData(uint32_t fromRadioNum)
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
DEBUG_MSG("BLE notify fromNum\n");
|
||||
//fromNum.notify32(fromRadioNum);
|
||||
|
||||
uint8_t val[4];
|
||||
put_le32(val, fromRadioNum);
|
||||
|
||||
std::string fromNumByteString(&val[0], &val[0] + sizeof(val));
|
||||
|
||||
FromNumCharacteristic->setValue(fromNumByteString);
|
||||
FromNumCharacteristic->notify();
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
bool BluetoothPhoneAPI::checkIsConnected() {
|
||||
if (bleServer && bleServer->getConnectedCount() > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
class ESP32BluetoothToRadioCallback : public NimBLECharacteristicCallbacks {
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic) {
|
||||
DEBUG_MSG("To Radio onwrite\n");
|
||||
auto val = pCharacteristic->getValue();
|
||||
|
||||
bluetoothPhoneAPI->handleToRadio(val.data(), val.length());
|
||||
}
|
||||
};
|
||||
|
||||
class ESP32BluetoothFromRadioCallback : public NimBLECharacteristicCallbacks {
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic) {
|
||||
DEBUG_MSG("From Radio onread\n");
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
|
||||
std::string fromRadioByteString(fromRadioBytes, fromRadioBytes + numBytes);
|
||||
|
||||
pCharacteristic->setValue(fromRadioByteString);
|
||||
}
|
||||
};
|
||||
|
||||
class ESP32BluetoothServerCallback : public NimBLEServerCallbacks {
|
||||
virtual uint32_t onPassKeyRequest() {
|
||||
|
||||
uint32_t passkey = 0;
|
||||
|
||||
if (doublepressed > 0 && (doublepressed + (30 * 1000)) > millis()) {
|
||||
DEBUG_MSG("User has overridden passkey\n");
|
||||
passkey = defaultBLEPin;
|
||||
} else {
|
||||
DEBUG_MSG("Using random passkey\n");
|
||||
passkey = random(
|
||||
100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
}
|
||||
DEBUG_MSG("*** Enter passkey %d on the peer side ***\n", passkey);
|
||||
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
screen->startBluetoothPinScreen(passkey);
|
||||
passkeyShowing = true;
|
||||
|
||||
return passkey;
|
||||
}
|
||||
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc) {
|
||||
DEBUG_MSG("BLE authentication complete\n");
|
||||
|
||||
if (passkeyShowing) {
|
||||
passkeyShowing = false;
|
||||
screen->stopBluetoothPinScreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static ESP32BluetoothToRadioCallback *toRadioCallbacks;
|
||||
static ESP32BluetoothFromRadioCallback *fromRadioCallbacks;
|
||||
|
||||
void ESP32Bluetooth::shutdown()
|
||||
{
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
DEBUG_MSG("Disable bluetooth\n");
|
||||
//Bluefruit.Advertising.stop();
|
||||
}
|
||||
|
||||
void ESP32Bluetooth::setup()
|
||||
{
|
||||
// Initialise the Bluefruit module
|
||||
DEBUG_MSG("Initialise the ESP32 bluetooth module\n");
|
||||
//Bluefruit.autoConnLed(false);
|
||||
//Bluefruit.begin();
|
||||
|
||||
// Set the advertised device name (keep it short!)
|
||||
//Bluefruit.setName(getDeviceName());
|
||||
|
||||
// Set the connect/disconnect callback handlers
|
||||
//Bluefruit.Periph.setConnectCallback(connect_callback);
|
||||
//Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
|
||||
|
||||
// Configure and Start the Device Information Service
|
||||
DEBUG_MSG("Configuring the Device Information Service\n");
|
||||
// FIXME, we should set a mfg string based on our HW_VENDOR enum
|
||||
// bledis.setManufacturer(HW_VENDOR);
|
||||
//bledis.setModel(optstr(HW_VERSION));
|
||||
//bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
//bledis.begin();
|
||||
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
//DEBUG_MSG("Configuring the Battery Service\n");
|
||||
//blebas.begin();
|
||||
//blebas.write(0); // Unknown battery level for now
|
||||
|
||||
//bledfu.begin(); // Install the DFU helper
|
||||
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
DEBUG_MSG("Configuring the Mesh bluetooth service\n");
|
||||
//setupMeshService();
|
||||
|
||||
// Supposedly debugging works with soft device if you disable advertising
|
||||
//if (isSoftDeviceAllowed) {
|
||||
// Setup the advertising packet(s)
|
||||
// DEBUG_MSG("Setting up the advertising payload(s)\n");
|
||||
// startAdv();
|
||||
|
||||
// DEBUG_MSG("Advertising\n");
|
||||
//}
|
||||
|
||||
//NimBLEDevice::deleteAllBonds();
|
||||
|
||||
NimBLEDevice::init(getDeviceName());
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
|
||||
NimBLEDevice::setSecurityAuth(true, true, true);
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
bleServer = NimBLEDevice::createServer();
|
||||
|
||||
ESP32BluetoothServerCallback *serverCallbacks = new ESP32BluetoothServerCallback();
|
||||
bleServer->setCallbacks(serverCallbacks);
|
||||
|
||||
NimBLEService *bleService = bleServer->createService(MESH_SERVICE_UUID);
|
||||
//NimBLECharacteristic *pNonSecureCharacteristic = bleService->createCharacteristic("1234", NIMBLE_PROPERTY::READ );
|
||||
//NimBLECharacteristic *pSecureCharacteristic = bleService->createCharacteristic("1235", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::READ_AUTHEN);
|
||||
|
||||
//define the characteristics that the app is looking for
|
||||
NimBLECharacteristic *ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
NimBLECharacteristic *FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
FromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
toRadioCallbacks = new ESP32BluetoothToRadioCallback();
|
||||
ToRadioCharacteristic->setCallbacks(toRadioCallbacks);
|
||||
|
||||
fromRadioCallbacks = new ESP32BluetoothFromRadioCallback();
|
||||
FromRadioCharacteristic->setCallbacks(fromRadioCallbacks);
|
||||
|
||||
//uint8_t val[4];
|
||||
//uint32_t zero = 0;
|
||||
//put_le32(val, zero);
|
||||
//std::string fromNumByteString(&val[0], &val[0] + sizeof(val));
|
||||
//FromNumCharacteristic->setValue(fromNumByteString);
|
||||
|
||||
bleService->start();
|
||||
//pNonSecureCharacteristic->setValue("Hello Non Secure BLE");
|
||||
//pSecureCharacteristic->setValue("Hello Secure BLE");
|
||||
|
||||
//FromRadioCharacteristic->setValue("FromRadioString");
|
||||
//ToRadioCharacteristic->setCallbacks()
|
||||
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
pAdvertising->start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
//blebas.write(level);
|
||||
}
|
||||
|
||||
void ESP32Bluetooth::clearBonds()
|
||||
{
|
||||
DEBUG_MSG("Clearing bluetooth bonds!\n");
|
||||
//bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
//bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
|
||||
//Bluefruit.Periph.clearBonds();
|
||||
//Bluefruit.Central.clearBonds();
|
||||
|
||||
}
|
||||
|
||||
void clearNVS() {
|
||||
NimBLEDevice::deleteAllBonds();
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void disablePin() {
|
||||
DEBUG_MSG("User Override, disabling bluetooth pin requirement\n");
|
||||
// keep track of when it was pressed, so we know it was within X seconds
|
||||
|
||||
// Flash the LED
|
||||
setLed(true);
|
||||
delay(100);
|
||||
setLed(false);
|
||||
delay(100);
|
||||
setLed(true);
|
||||
delay(100);
|
||||
setLed(false);
|
||||
delay(100);
|
||||
setLed(true);
|
||||
delay(100);
|
||||
setLed(false);
|
||||
|
||||
doublepressed = millis();
|
||||
}
|
||||
|
||||
#endif
|
||||
33
src/platform/esp32/ESP32Bluetooth.h
Normal file
33
src/platform/esp32/ESP32Bluetooth.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifdef USE_NEW_ESP32_BLUETOOTH
|
||||
|
||||
#pragma once
|
||||
|
||||
extern uint16_t fromNumValHandle;
|
||||
|
||||
class BluetoothPhoneAPI : public PhoneAPI
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
virtual void onNowHasData(uint32_t fromRadioNum) override;
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override;
|
||||
};
|
||||
|
||||
extern PhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
class ESP32Bluetooth
|
||||
{
|
||||
public:
|
||||
void setup();
|
||||
void shutdown();
|
||||
void clearBonds();
|
||||
};
|
||||
|
||||
void setBluetoothEnable(bool on);
|
||||
void clearNVS();
|
||||
void disablePin();
|
||||
|
||||
#endif
|
||||
78
src/platform/esp32/ESP32CryptoEngine.cpp
Normal file
78
src/platform/esp32/ESP32CryptoEngine.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "CryptoEngine.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include "crypto/includes.h"
|
||||
|
||||
#include "crypto/common.h"
|
||||
|
||||
// #include "esp_system.h"
|
||||
|
||||
#include "crypto/aes.h"
|
||||
#include "crypto/aes_wrap.h"
|
||||
#include "mbedtls/aes.h"
|
||||
|
||||
|
||||
|
||||
class ESP32CryptoEngine : public CryptoEngine
|
||||
{
|
||||
|
||||
mbedtls_aes_context aes;
|
||||
|
||||
public:
|
||||
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
|
||||
|
||||
~ESP32CryptoEngine() { mbedtls_aes_free(&aes); }
|
||||
|
||||
/**
|
||||
* Set the key used for encrypt, decrypt.
|
||||
*
|
||||
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
|
||||
*
|
||||
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(const CryptoKey &k) override
|
||||
{
|
||||
CryptoEngine::setKey(k);
|
||||
|
||||
if (key.length != 0) {
|
||||
auto res = mbedtls_aes_setkey_enc(&aes, key.bytes, key.length * 8);
|
||||
assert(!res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a packet
|
||||
*
|
||||
* @param bytes is updated in place
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
if (key.length > 0) {
|
||||
uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
size_t nc_off = 0;
|
||||
|
||||
DEBUG_MSG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetId, numBytes);
|
||||
initNonce(fromNode, packetId);
|
||||
assert(numBytes <= MAX_BLOCKSIZE);
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
memset(scratch + numBytes, 0,
|
||||
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
|
||||
|
||||
auto res = mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, nonce, stream_block, scratch, bytes);
|
||||
assert(!res);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
// For CTR, the implementation is the same
|
||||
encrypt(fromNode, packetId, numBytes, bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
CryptoEngine *crypto = new ESP32CryptoEngine();
|
||||
73
src/platform/esp32/NimbleSoftwareUpdate.c
Normal file
73
src/platform/esp32/NimbleSoftwareUpdate.c
Normal file
@@ -0,0 +1,73 @@
|
||||
#ifndef USE_NEW_ESP32_BLUETOOTH
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
|
||||
// NRF52 wants these constants as byte arrays
|
||||
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
|
||||
|
||||
// "cb0b9a0b-a84c-4c0d-bdbb-442e3144ee30"
|
||||
const ble_uuid128_t update_service_uuid =
|
||||
BLE_UUID128_INIT(0x30, 0xee, 0x44, 0x31, 0x2e, 0x44, 0xbb, 0xbd, 0x0d, 0x4c, 0x4c, 0xa8, 0x0b, 0x9a, 0x0b, 0xcb);
|
||||
|
||||
// "e74dd9c0-a301-4a6f-95a1-f0e1dbea8e1e" write|read
|
||||
const ble_uuid128_t update_size_uuid =
|
||||
BLE_UUID128_INIT(0x1e, 0x8e, 0xea, 0xdb, 0xe1, 0xf0, 0xa1, 0x95, 0x6f, 0x4a, 0x01, 0xa3, 0xc0, 0xd9, 0x4d, 0xe7);
|
||||
|
||||
// "e272ebac-d463-4b98-bc84-5cc1a39ee517" write
|
||||
const ble_uuid128_t update_data_uuid =
|
||||
BLE_UUID128_INIT(0x17, 0xe5, 0x9e, 0xa3, 0xc1, 0x5c, 0x84, 0xbc, 0x98, 0x4b, 0x63, 0xd4, 0xac, 0xeb, 0x72, 0xe2);
|
||||
|
||||
// "4826129c-c22a-43a3-b066-ce8f0d5bacc6" write
|
||||
const ble_uuid128_t update_crc32_uuid =
|
||||
BLE_UUID128_INIT(0xc6, 0xac, 0x5b, 0x0d, 0x8f, 0xce, 0x66, 0xb0, 0xa3, 0x43, 0x2a, 0xc2, 0x9c, 0x12, 0x26, 0x48);
|
||||
|
||||
// "5e134862-7411-4424-ac4a-210937432c77" read|notify
|
||||
const ble_uuid128_t update_result_uuid =
|
||||
BLE_UUID128_INIT(0x77, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e);
|
||||
|
||||
// "5e134862-7411-4424-ac4a-210937432c67" write
|
||||
const ble_uuid128_t update_region_uuid =
|
||||
BLE_UUID128_INIT(0x67, 0x2c, 0x43, 0x37, 0x09, 0x21, 0x4a, 0xac, 0x24, 0x44, 0x11, 0x74, 0x62, 0x48, 0x13, 0x5e);
|
||||
|
||||
const struct ble_gatt_svc_def gatt_update_svcs[] = {
|
||||
{
|
||||
/*** Service: Security test. */
|
||||
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
||||
.uuid = &update_service_uuid.u,
|
||||
.characteristics =
|
||||
(struct ble_gatt_chr_def[]){{
|
||||
.uuid = &update_size_uuid.u,
|
||||
.access_cb = update_size_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN | BLE_GATT_CHR_F_READ |
|
||||
BLE_GATT_CHR_F_READ_AUTHEN,
|
||||
},
|
||||
{
|
||||
.uuid = &update_data_uuid.u,
|
||||
.access_cb = update_data_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
|
||||
},
|
||||
{
|
||||
.uuid = &update_crc32_uuid.u,
|
||||
.access_cb = update_crc32_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
|
||||
},
|
||||
{
|
||||
.uuid = &update_result_uuid.u,
|
||||
.access_cb = update_result_callback,
|
||||
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY,
|
||||
},
|
||||
{
|
||||
.uuid = &update_region_uuid.u,
|
||||
.access_cb = update_region_callback,
|
||||
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_AUTHEN,
|
||||
},
|
||||
{
|
||||
0, /* No more characteristics in this service. */
|
||||
}},
|
||||
},
|
||||
|
||||
{
|
||||
0, /* No more services. */
|
||||
},
|
||||
};
|
||||
|
||||
#endif //#ifndef USE_NEW_ESP32_BLUETOOTH
|
||||
60
src/platform/esp32/SimpleAllocator.cpp
Normal file
60
src/platform/esp32/SimpleAllocator.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "SimpleAllocator.h"
|
||||
#include "assert.h"
|
||||
|
||||
|
||||
SimpleAllocator::SimpleAllocator() { reset(); }
|
||||
|
||||
void *SimpleAllocator::alloc(size_t size)
|
||||
{
|
||||
assert(nextFree + size <= sizeof(bytes));
|
||||
void *res = &bytes[nextFree];
|
||||
nextFree += size;
|
||||
Serial.printf("Total simple allocs %u\n", nextFree);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void SimpleAllocator::reset() { nextFree = 0; }
|
||||
|
||||
void *operator new(size_t size, SimpleAllocator &p)
|
||||
{
|
||||
return p.alloc(size);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This was a dumb idea, turn off for now
|
||||
|
||||
SimpleAllocator *activeAllocator;
|
||||
|
||||
AllocatorScope::AllocatorScope(SimpleAllocator &a)
|
||||
{
|
||||
assert(!activeAllocator);
|
||||
activeAllocator = &a;
|
||||
}
|
||||
|
||||
AllocatorScope::~AllocatorScope()
|
||||
{
|
||||
assert(activeAllocator);
|
||||
activeAllocator = NULL;
|
||||
}
|
||||
|
||||
/// Global new/delete, uses a simple allocator if it is in scope
|
||||
|
||||
void *operator new(size_t sz) throw(std::bad_alloc)
|
||||
{
|
||||
void *mem = activeAllocator ? activeAllocator->alloc(sz) : malloc(sz);
|
||||
if (mem)
|
||||
return mem;
|
||||
else
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
void operator delete(void *ptr) throw()
|
||||
{
|
||||
if (activeAllocator)
|
||||
Serial.println("Warning: leaking an active allocator object"); // We don't properly handle this yet
|
||||
else
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
42
src/platform/esp32/SimpleAllocator.h
Normal file
42
src/platform/esp32/SimpleAllocator.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
|
||||
#define POOL_SIZE 16384
|
||||
|
||||
/**
|
||||
* An allocator (and placement new operator) that allocates storage from a fixed sized buffer.
|
||||
* It will panic if that buffer fills up.
|
||||
* If you are _sure_ no outstanding references to blocks in this buffer still exist, you can call
|
||||
* reset() to start from scratch.
|
||||
*
|
||||
* Currently the only usecase for this class is the ESP32 bluetooth stack, where once we've called deinit(false)
|
||||
* we are sure all those bluetooth objects no longer exist, and we'll need to recreate them when we restart bluetooth
|
||||
*/
|
||||
class SimpleAllocator
|
||||
{
|
||||
uint8_t bytes[POOL_SIZE] = {};
|
||||
|
||||
uint32_t nextFree = 0;
|
||||
|
||||
public:
|
||||
SimpleAllocator();
|
||||
|
||||
void *alloc(size_t size);
|
||||
|
||||
/** If you are _sure_ no outstanding references to blocks in this buffer still exist, you can call
|
||||
* reset() to start from scratch.
|
||||
* */
|
||||
void reset();
|
||||
};
|
||||
|
||||
void *operator new(size_t size, SimpleAllocator &p);
|
||||
|
||||
/**
|
||||
* Temporarily makes the specified Allocator be used for _all_ allocations. Useful when calling library routines
|
||||
* that don't know about pools
|
||||
*/
|
||||
class AllocatorScope {
|
||||
public:
|
||||
explicit AllocatorScope(SimpleAllocator &a);
|
||||
~AllocatorScope();
|
||||
};
|
||||
106
src/platform/esp32/architecture.h
Normal file
106
src/platform/esp32/architecture.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#define ARCH_ESP32
|
||||
|
||||
//
|
||||
// defaults for ESP32 architecture
|
||||
//
|
||||
|
||||
#ifndef HAS_WIFI
|
||||
#define HAS_WIFI 1
|
||||
#endif
|
||||
#ifndef HAS_SCREEN
|
||||
#define HAS_SCREEN 1
|
||||
#endif
|
||||
#ifndef HAS_WIRE
|
||||
#define HAS_WIRE 1
|
||||
#endif
|
||||
#ifndef HAS_GPS
|
||||
#define HAS_GPS 1
|
||||
#endif
|
||||
#ifndef HAS_BUTTON
|
||||
#define HAS_BUTTON 1
|
||||
#endif
|
||||
#ifndef HAS_TELEMETRY
|
||||
#define HAS_TELEMETRY 1
|
||||
#endif
|
||||
#ifndef HAS_RADIO
|
||||
#define HAS_RADIO 1
|
||||
#endif
|
||||
#ifndef HAS_RTC
|
||||
#define HAS_RTC 1
|
||||
#endif
|
||||
|
||||
//
|
||||
// set HW_VENDOR
|
||||
//
|
||||
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
|
||||
#if defined(TBEAM_V10)
|
||||
#define HW_VENDOR HardwareModel_TBEAM
|
||||
#elif defined(TBEAM_V07)
|
||||
#define HW_VENDOR HardwareModel_TBEAM0p7
|
||||
#elif defined(DIY_V1)
|
||||
#define HW_VENDOR HardwareModel_DIY_V1
|
||||
#elif defined(RAK_11200)
|
||||
#define HW_VENDOR HardwareModel_RAK11200
|
||||
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2)
|
||||
#ifdef HELTEC_V2_0
|
||||
#define HW_VENDOR HardwareModel_HELTEC_V2_0
|
||||
#endif
|
||||
#ifdef HELTEC_V2_1
|
||||
#define HW_VENDOR HardwareModel_HELTEC_V2_1
|
||||
#endif
|
||||
#elif defined(ARDUINO_HELTEC_WIFI_LORA_32)
|
||||
#define HW_VENDOR HardwareModel_HELTEC_V1
|
||||
#elif defined(TLORA_V1)
|
||||
#define HW_VENDOR HardwareModel_TLORA_V1
|
||||
#elif defined(TLORA_V2)
|
||||
#define HW_VENDOR HardwareModel_TLORA_V2
|
||||
#elif defined(TLORA_V1_3)
|
||||
#define HW_VENDOR HardwareModel_TLORA_V1_1p3
|
||||
#elif defined(TLORA_V2_1_16)
|
||||
#define HW_VENDOR HardwareModel_TLORA_V2_1_1p6
|
||||
#elif defined(GENIEBLOCKS)
|
||||
#define HW_VENDOR HardwareModel_GENIEBLOCKS
|
||||
#elif defined(PRIVATE_HW)
|
||||
#define HW_VENDOR HardwareModel_PRIVATE_HW
|
||||
#elif defined(NANO_G1)
|
||||
#define HW_VENDOR HardwareModel_NANO_G1
|
||||
#elif defined(M5STACK)
|
||||
#define HW_VENDOR HardwareModel_M5STACK
|
||||
#elif defined(STATION_G1)
|
||||
#define HW_VENDOR HardwareModel_STATION_G1
|
||||
#endif
|
||||
|
||||
//
|
||||
// Standard definitions for ESP32 targets
|
||||
//
|
||||
|
||||
#define GPS_SERIAL_NUM 1
|
||||
#ifndef GPS_RX_PIN
|
||||
#define GPS_RX_PIN 34
|
||||
#endif
|
||||
#ifndef GPS_TX_PIN
|
||||
#ifdef USE_JTAG
|
||||
#define GPS_TX_PIN -1
|
||||
#else
|
||||
#define GPS_TX_PIN 12
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LoRa SPI
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// NRF52 boards will define this in variant.h
|
||||
#ifndef RF95_SCK
|
||||
#define RF95_SCK 5
|
||||
#define RF95_MISO 19
|
||||
#define RF95_MOSI 27
|
||||
#define RF95_NSS 18
|
||||
#endif
|
||||
|
||||
#define SERIAL0_RX_GPIO 3 // Always GPIO3 on ESP32
|
||||
|
||||
184
src/platform/esp32/main-esp32.cpp
Normal file
184
src/platform/esp32/main-esp32.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "BluetoothSoftwareUpdate.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "main.h"
|
||||
|
||||
#ifdef USE_NEW_ESP32_BLUETOOTH
|
||||
#include "ESP32Bluetooth.h"
|
||||
#include "mesh/http/WiFiAPClient.h"
|
||||
#else
|
||||
#include "nimble/BluetoothUtil.h"
|
||||
#endif
|
||||
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
#include <Preferences.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <nvs.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#ifdef USE_NEW_ESP32_BLUETOOTH
|
||||
ESP32Bluetooth *esp32Bluetooth;
|
||||
#endif
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
static void printBLEinfo() {
|
||||
int dev_num = esp_ble_get_bond_device_num();
|
||||
|
||||
esp_ble_bond_dev_t *dev_list = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num);
|
||||
esp_ble_get_bond_device_list(&dev_num, dev_list);
|
||||
for (int i = 0; i < dev_num; i++) {
|
||||
// esp_ble_remove_bond_device(dev_list[i].bd_addr);
|
||||
}
|
||||
|
||||
} */
|
||||
#ifdef USE_NEW_ESP32_BLUETOOTH
|
||||
void setBluetoothEnable(bool on) {
|
||||
|
||||
if (!isWifiAvailable()) {
|
||||
if (!esp32Bluetooth) {
|
||||
esp32Bluetooth = new ESP32Bluetooth();
|
||||
}
|
||||
if (on) {
|
||||
esp32Bluetooth->setup();
|
||||
} else {
|
||||
esp32Bluetooth->shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void esp32Setup()
|
||||
{
|
||||
uint32_t seed = esp_random();
|
||||
DEBUG_MSG("Setting random seed %u\n", seed);
|
||||
randomSeed(seed); // ESP docs say this is fairly random
|
||||
|
||||
DEBUG_MSG("Total heap: %d\n", ESP.getHeapSize());
|
||||
DEBUG_MSG("Free heap: %d\n", ESP.getFreeHeap());
|
||||
DEBUG_MSG("Total PSRAM: %d\n", ESP.getPsramSize());
|
||||
DEBUG_MSG("Free PSRAM: %d\n", ESP.getFreePsram());
|
||||
|
||||
nvs_stats_t nvs_stats;
|
||||
auto res = nvs_get_stats(NULL, &nvs_stats);
|
||||
assert(res == ESP_OK);
|
||||
DEBUG_MSG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d, NameSpaces %d\n", nvs_stats.used_entries, nvs_stats.free_entries,
|
||||
nvs_stats.total_entries, nvs_stats.namespace_count);
|
||||
|
||||
DEBUG_MSG("Setup Preferences in Flash Storage\n");
|
||||
|
||||
// Create object to store our persistant data
|
||||
Preferences preferences;
|
||||
preferences.begin("meshtastic", false);
|
||||
|
||||
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
|
||||
rebootCounter++;
|
||||
preferences.putUInt("rebootCounter", rebootCounter);
|
||||
preferences.end();
|
||||
DEBUG_MSG("Number of Device Reboots: %d\n", rebootCounter);
|
||||
|
||||
// enableModemSleep();
|
||||
|
||||
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
||||
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
|
||||
// #define APP_WATCHDOG_SECS 45
|
||||
#define APP_WATCHDOG_SECS 90
|
||||
|
||||
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
||||
assert(res == ESP_OK);
|
||||
|
||||
res = esp_task_wdt_add(NULL);
|
||||
assert(res == ESP_OK);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Turn off for now
|
||||
|
||||
uint32_t axpDebugRead()
|
||||
{
|
||||
axp.debugCharging();
|
||||
DEBUG_MSG("vbus current %f\n", axp.getVbusCurrent());
|
||||
DEBUG_MSG("charge current %f\n", axp.getBattChargeCurrent());
|
||||
DEBUG_MSG("bat voltage %f\n", axp.getBattVoltage());
|
||||
DEBUG_MSG("batt pct %d\n", axp.getBattPercentage());
|
||||
DEBUG_MSG("is battery connected %d\n", axp.isBatteryConnect());
|
||||
DEBUG_MSG("is USB connected %d\n", axp.isVBUSPlug());
|
||||
DEBUG_MSG("is charging %d\n", axp.isChargeing());
|
||||
|
||||
return 30 * 1000;
|
||||
}
|
||||
|
||||
Periodic axpDebugOutput(axpDebugRead);
|
||||
#endif
|
||||
|
||||
|
||||
/// loop code specific to ESP32 targets
|
||||
void esp32Loop()
|
||||
{
|
||||
esp_task_wdt_reset(); // service our app level watchdog
|
||||
//loopBLE();
|
||||
|
||||
// for debug printing
|
||||
// radio.radioIf.canSleep();
|
||||
}
|
||||
|
||||
void cpuDeepSleep(uint64_t msecToWake)
|
||||
{
|
||||
/*
|
||||
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
|
||||
If an external circuit drives this pin in deep sleep mode, current consumption may
|
||||
increase due to current flowing through these pullups and pulldowns.
|
||||
|
||||
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
|
||||
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
|
||||
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
|
||||
some current will flow through these external and internal resistors, increasing deep
|
||||
sleep current above the minimal possible value.
|
||||
|
||||
Note: we don't isolate pins that are used for the LORA, LED, i2c, spi or the wake button
|
||||
*/
|
||||
static const uint8_t rtcGpios[] = {/* 0, */ 2,
|
||||
/* 4, */
|
||||
#ifndef USE_JTAG
|
||||
13,
|
||||
/* 14, */ /* 15, */
|
||||
#endif
|
||||
/* 25, */ 26, /* 27, */
|
||||
32, 33, 34, 35,
|
||||
36, 37
|
||||
/* 38, 39 */};
|
||||
|
||||
for (int i = 0; i < sizeof(rtcGpios); i++)
|
||||
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
|
||||
|
||||
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
|
||||
// to detect wake and in normal operation the external part drives them hard.
|
||||
|
||||
// We want RTC peripherals to stay on
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
||||
|
||||
#ifdef BUTTON_PIN
|
||||
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
|
||||
uint64_t gpioMask = (1ULL << BUTTON_PIN);
|
||||
|
||||
#ifdef BUTTON_NEED_PULLUP
|
||||
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
#endif
|
||||
|
||||
// Not needed because both of the current boards have external pullups
|
||||
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead of
|
||||
// just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
||||
|
||||
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
|
||||
#endif
|
||||
|
||||
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
|
||||
esp_deep_sleep_start(); // TBD mA sleep current (battery)
|
||||
}
|
||||
120
src/platform/nrf52/JLINK_MONITOR.c
Normal file
120
src/platform/nrf52/JLINK_MONITOR.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH & Co. KG *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 1995 - 2015 SEGGER Microcontroller GmbH & Co. KG *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
|
||||
----------------------------------------------------------------------
|
||||
File : JLINK_MONITOR.c
|
||||
Purpose : Implementation of debug monitor for J-Link monitor mode debug on Cortex-M devices.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
#include "JLINK_MONITOR.h"
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Configuration
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Types
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Static data
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
volatile int MAIN_MonCnt; // Incremented in JLINK_MONITOR_OnPoll() while CPU is in debug mode
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Local functions
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Global functions
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* JLINK_MONITOR_OnExit()
|
||||
*
|
||||
* Function description
|
||||
* Called from DebugMon_Handler(), once per debug exit.
|
||||
* May perform some target specific operations to be done on debug mode exit.
|
||||
*
|
||||
* Notes
|
||||
* (1) Must not keep the CPU busy for more than 100 ms
|
||||
*/
|
||||
void JLINK_MONITOR_OnExit(void) {
|
||||
//
|
||||
// Add custom code here
|
||||
//
|
||||
// BSP_ClrLED(0);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* JLINK_MONITOR_OnEnter()
|
||||
*
|
||||
* Function description
|
||||
* Called from DebugMon_Handler(), once per debug entry.
|
||||
* May perform some target specific operations to be done on debug mode entry
|
||||
*
|
||||
* Notes
|
||||
* (1) Must not keep the CPU busy for more than 100 ms
|
||||
*/
|
||||
void JLINK_MONITOR_OnEnter(void) {
|
||||
//
|
||||
// Add custom code here
|
||||
//
|
||||
// BSP_SetLED(0);
|
||||
// BSP_ClrLED(1);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* JLINK_MONITOR_OnPoll()
|
||||
*
|
||||
* Function description
|
||||
* Called periodically from DebugMon_Handler(), to perform some actions that need to be performed periodically during debug mode.
|
||||
*
|
||||
* Notes
|
||||
* (1) Must not keep the CPU busy for more than 100 ms
|
||||
*/
|
||||
void JLINK_MONITOR_OnPoll(void) {
|
||||
//
|
||||
// Add custom code here
|
||||
//
|
||||
MAIN_MonCnt++;
|
||||
// BSP_ToggleLED(0);
|
||||
// _Delay(500000);
|
||||
}
|
||||
|
||||
/****** End Of File *************************************************/
|
||||
27
src/platform/nrf52/JLINK_MONITOR.h
Normal file
27
src/platform/nrf52/JLINK_MONITOR.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH & Co. KG *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 1995 - 2015 SEGGER Microcontroller GmbH & Co. KG *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
|
||||
----------------------------------------------------------------------
|
||||
File : JLINK_MONITOR.h
|
||||
Purpose : Header file of debug monitor for J-Link monitor mode debug on Cortex-M devices.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef JLINK_MONITOR_H
|
||||
#define JLINK_MONITOR_H
|
||||
|
||||
void JLINK_MONITOR_OnExit (void);
|
||||
void JLINK_MONITOR_OnEnter (void);
|
||||
void JLINK_MONITOR_OnPoll (void);
|
||||
|
||||
#endif
|
||||
|
||||
/****** End Of File *************************************************/
|
||||
888
src/platform/nrf52/JLINK_MONITOR_ISR_SES.S
Normal file
888
src/platform/nrf52/JLINK_MONITOR_ISR_SES.S
Normal file
@@ -0,0 +1,888 @@
|
||||
/*********************************************************************
|
||||
* SEGGER Microcontroller GmbH & Co. KG *
|
||||
* The Embedded Experts *
|
||||
**********************************************************************
|
||||
* *
|
||||
* (c) 1995 - 2015 SEGGER Microcontroller GmbH & Co. KG *
|
||||
* *
|
||||
* www.segger.com Support: support@segger.com *
|
||||
* *
|
||||
**********************************************************************
|
||||
|
||||
----------------------------------------------------------------------
|
||||
File : JLINK_MONITOR_ISR_SES.s
|
||||
Purpose : Implementation of debug monitor for J-Link monitor mode
|
||||
debug on Cortex-M devices, supporting SES compiler.
|
||||
-------- END-OF-HEADER ---------------------------------------------
|
||||
*/
|
||||
|
||||
.name JLINK_MONITOR_ISR
|
||||
.syntax unified
|
||||
|
||||
.extern JLINK_MONITOR_OnEnter
|
||||
.extern JLINK_MONITOR_OnExit
|
||||
.extern JLINK_MONITOR_OnPoll
|
||||
|
||||
.global DebugMon_Handler
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines, configurable
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
#define _MON_VERSION 100 // V x.yy
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Defines, fixed
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
#define _APP_SP_OFF_R0 0x00
|
||||
#define _APP_SP_OFF_R1 0x04
|
||||
#define _APP_SP_OFF_R2 0x08
|
||||
#define _APP_SP_OFF_R3 0x0C
|
||||
#define _APP_SP_OFF_R12 0x10
|
||||
#define _APP_SP_OFF_R14_LR 0x14
|
||||
#define _APP_SP_OFF_PC 0x18
|
||||
#define _APP_SP_OFF_XPSR 0x1C
|
||||
#define _APP_SP_OFF_S0 0x20
|
||||
#define _APP_SP_OFF_S1 0x24
|
||||
#define _APP_SP_OFF_S2 0x28
|
||||
#define _APP_SP_OFF_S3 0x2C
|
||||
#define _APP_SP_OFF_S4 0x30
|
||||
#define _APP_SP_OFF_S5 0x34
|
||||
#define _APP_SP_OFF_S6 0x38
|
||||
#define _APP_SP_OFF_S7 0x3C
|
||||
#define _APP_SP_OFF_S8 0x40
|
||||
#define _APP_SP_OFF_S9 0x44
|
||||
#define _APP_SP_OFF_S10 0x48
|
||||
#define _APP_SP_OFF_S11 0x4C
|
||||
#define _APP_SP_OFF_S12 0x50
|
||||
#define _APP_SP_OFF_S13 0x54
|
||||
#define _APP_SP_OFF_S14 0x58
|
||||
#define _APP_SP_OFF_S15 0x5C
|
||||
#define _APP_SP_OFF_FPSCR 0x60
|
||||
|
||||
#define _NUM_BYTES_BASIC_STACKFRAME 32
|
||||
#define _NUM_BYTES_EXTENDED_STACKFRAME 72
|
||||
|
||||
#define _SYSTEM_DCRDR_OFF 0x00
|
||||
#define _SYSTEM_DEMCR_OFF 0x04
|
||||
|
||||
#define _SYSTEM_DHCSR 0xE000EDF0 // Debug Halting Control and Status Register (DHCSR)
|
||||
#define _SYSTEM_DCRSR 0xE000EDF4 // Debug Core Register Selector Register (DCRSR)
|
||||
#define _SYSTEM_DCRDR 0xE000EDF8 // Debug Core Register Data Register (DCRDR)
|
||||
#define _SYSTEM_DEMCR 0xE000EDFC // Debug Exception and Monitor Control Register (DEMCR)
|
||||
|
||||
#define _SYSTEM_FPCCR 0xE000EF34 // Floating-Point Context Control Register (FPCCR)
|
||||
#define _SYSTEM_FPCAR 0xE000EF38 // Floating-Point Context Address Register (FPCAR)
|
||||
#define _SYSTEM_FPDSCR 0xE000EF3C // Floating-Point Default Status Control Register (FPDSCR)
|
||||
#define _SYSTEM_MVFR0 0xE000EF40 // Media and FP Feature Register 0 (MVFR0)
|
||||
#define _SYSTEM_MVFR1 0xE000EF44 // Media and FP Feature Register 1 (MVFR1)
|
||||
|
||||
/*
|
||||
* Defines for determining if the current debug config supports FPU registers
|
||||
* For some compilers like IAR EWARM when disabling the FPU in the compiler settings an error is thrown when
|
||||
*/
|
||||
#ifdef __FPU_PRESENT
|
||||
#if __FPU_PRESENT
|
||||
#define _HAS_FPU_REGS 1
|
||||
#else
|
||||
#define _HAS_FPU_REGS 0
|
||||
#endif
|
||||
#else
|
||||
#define _HAS_FPU_REGS 0
|
||||
#endif
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* Signature of monitor
|
||||
*
|
||||
* Function description
|
||||
* Needed for targets where also a boot ROM is present that possibly specifies a vector table with a valid debug monitor exception entry
|
||||
*/
|
||||
.section .text, "ax"
|
||||
|
||||
//
|
||||
// JLINKMONHANDLER
|
||||
//
|
||||
.byte 0x4A
|
||||
.byte 0x4C
|
||||
.byte 0x49
|
||||
.byte 0x4E
|
||||
.byte 0x4B
|
||||
.byte 0x4D
|
||||
.byte 0x4F
|
||||
.byte 0x4E
|
||||
.byte 0x48
|
||||
.byte 0x41
|
||||
.byte 0x4E
|
||||
.byte 0x44
|
||||
.byte 0x4C
|
||||
.byte 0x45
|
||||
.byte 0x52
|
||||
.byte 0x00 // Align to 8-bytes
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* DebugMon_Handler()
|
||||
*
|
||||
* Function description
|
||||
* Debug monitor handler. CPU enters this handler in case a "halt" request is made from the debugger.
|
||||
* This handler is also responsible for handling commands that are sent by the debugger.
|
||||
*
|
||||
* Notes
|
||||
* This is actually the ISR for the debug inerrupt (exception no. 12)
|
||||
*/
|
||||
.thumb_func
|
||||
|
||||
DebugMon_Handler:
|
||||
/*
|
||||
General procedure:
|
||||
DCRDR is used as communication register
|
||||
DEMCR[19] is used as ready flag
|
||||
For the command J-Link sends to the monitor: DCRDR[7:0] == Cmd, DCRDR[31:8] == ParamData
|
||||
|
||||
1) Monitor sets DEMCR[19] whenever it is ready to receive new commands/data
|
||||
DEMCR[19] is initially set on debug monitor entry
|
||||
2) J-Link will clear once it has placed conmmand/data in DCRDR for J-Link
|
||||
3) Monitor will wait for DEMCR[19] to be cleared
|
||||
4) Monitor will process command (May cause additional data transfers etc., depends on command
|
||||
5) No restart-CPU command? => Back to 2), Otherwise => 6)
|
||||
6) Monitor will clear DEMCR[19] 19 to indicate that it is no longer ready
|
||||
*/
|
||||
PUSH {LR}
|
||||
BL JLINK_MONITOR_OnEnter
|
||||
POP {LR}
|
||||
LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR
|
||||
B.N _IndicateMonReady
|
||||
_WaitProbeReadIndicateMonRdy: // while(_SYSTEM_DEMCR & (1uL << 19)); => Wait until J-Link has read item
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR
|
||||
LSLS R0,R0,#+12
|
||||
BMI.N _WaitProbeReadIndicateMonRdy
|
||||
_IndicateMonReady:
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
/*
|
||||
During command loop:
|
||||
R0 = Tmp
|
||||
R1 = Tmp
|
||||
R2 = Tmp
|
||||
R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset)
|
||||
R12 = Tmp
|
||||
|
||||
Outside command loop R0-R3 and R12 may be overwritten by MONITOR_OnPoll()
|
||||
*/
|
||||
_WaitForJLinkCmd: // do {
|
||||
PUSH {LR}
|
||||
BL JLINK_MONITOR_OnPoll
|
||||
POP {LR}
|
||||
LDR.N R3,_AddrDCRDR // 0xe000edf8 == _SYSTEM_DCRDR
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
LSRS R0,R0,#+20 // DEMCR[19] -> Carry Clear? => J-Link has placed command for us
|
||||
BCS _WaitForJLinkCmd
|
||||
/*
|
||||
Perform command
|
||||
Command is placed by J-Link in DCRDR[7:0] and additional parameter data is stored in DCRDR[31:8]
|
||||
J-Link clears DEMCR[19] to indicate that it placed a command/data or read data
|
||||
Monitor sets DEMCR[19] to indicate that it placed data or read data / is ready for a new command
|
||||
Setting DEMCR[19] indicates "monitor ready for new command / data" and also indicates: "data has been placed in DCRDR by monitor, for J-Link"
|
||||
Therefore it is responsibility of the commands to respond to the commands accordingly
|
||||
|
||||
Commands for debug monitor
|
||||
Commands must not exceed 0xFF (255) as we only defined 8-bits for command-part. Higher 24-bits are parameter info for current command
|
||||
|
||||
Protocol for different commands:
|
||||
J-Link: Cmd -> DCRDR, DEMCR[19] -> 0 => Cmd placed by probe
|
||||
*/
|
||||
LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // ParamInfo = _SYSTEM_DCRDR
|
||||
LSRS R1,R0,#+8 // ParamInfo >>= 8
|
||||
LSLS R0,R0,#+24
|
||||
LSRS R0,R0,#+24 // Cmd = ParamInfo & 0xFF
|
||||
//
|
||||
// switch (Cmd)
|
||||
//
|
||||
CMP R0,#+0
|
||||
BEQ.N _HandleGetMonVersion // case _MON_CMD_GET_MONITOR_VERSION
|
||||
CMP R0,#+2
|
||||
BEQ.N _HandleReadReg // case _MON_CMD_READ_REG
|
||||
BCC.N _HandleRestartCPU // case _MON_CMD_RESTART_CPU
|
||||
CMP R0,#+3
|
||||
BEQ.N _HandleWriteReg_Veneer // case _MON_CMD_WRITE_REG
|
||||
B.N _IndicateMonReady // default : while (1);
|
||||
/*
|
||||
Return
|
||||
_MON_CMD_RESTART_CPU
|
||||
CPU: DEMCR[19] -> 0 => Monitor no longer ready
|
||||
*/
|
||||
_HandleRestartCPU:
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR &= ~(1uL << 19); => Clear MON_REQ to indicate that monitor is no longer active
|
||||
BIC R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
PUSH {LR}
|
||||
BL JLINK_MONITOR_OnExit
|
||||
POP {PC}
|
||||
//
|
||||
// Place data section here to not get in trouble with load-offsets
|
||||
//
|
||||
.section .text, "ax", %progbits
|
||||
.align 2
|
||||
_AddrDCRDR:
|
||||
.long 0xE000EDF8
|
||||
_AddrCPACR:
|
||||
.long 0xE000ED88
|
||||
|
||||
.section .text, "ax"
|
||||
.thumb_func
|
||||
|
||||
;/*********************************************************************
|
||||
;*
|
||||
;* _HandleGetMonVersion
|
||||
;*
|
||||
;*/
|
||||
_HandleGetMonVersion:
|
||||
/*
|
||||
_MON_CMD_GET_MONITOR_VERSION
|
||||
CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready
|
||||
J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read
|
||||
CPU: DEMCR[19] -> 1 => Mon ready
|
||||
*/
|
||||
MOVS R0,#+_MON_VERSION
|
||||
STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // _SYSTEM_DCRDR = x
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready
|
||||
B _WaitProbeReadIndicateMonRdy
|
||||
|
||||
/*********************************************************************
|
||||
*
|
||||
* _HandleReadReg
|
||||
*
|
||||
*/
|
||||
_HandleWriteReg_Veneer:
|
||||
B.N _HandleWriteReg
|
||||
_HandleReadReg:
|
||||
/*
|
||||
_MON_CMD_READ_REG
|
||||
CPU: Data -> DCRDR, DEMCR[19] -> 1 => Data ready
|
||||
J-Link: DCRDR -> Read, DEMCR[19] -> 0 => Data read
|
||||
CPU: DEMCR[19] -> 1 => Mon ready
|
||||
|
||||
|
||||
Register indexes
|
||||
0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!)
|
||||
16: XPSR
|
||||
17: MSP
|
||||
18: PSP
|
||||
19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
20: FPSCR
|
||||
21-52: FPS0-FPS31
|
||||
|
||||
|
||||
Register usage when entering this "subroutine":
|
||||
R0 Cmd
|
||||
R1 ParamInfo
|
||||
R2 ---
|
||||
R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset)
|
||||
R12 ---
|
||||
|
||||
Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension
|
||||
LR Return to Return SP Frame type
|
||||
---------------------------------------------------------
|
||||
0xFFFFFFE1 Handler mode. MSP Extended
|
||||
0xFFFFFFE9 Thread mode MSP Extended
|
||||
0xFFFFFFED Thread mode PSP Extended
|
||||
0xFFFFFFF1 Handler mode. MSP Basic
|
||||
0xFFFFFFF9 Thread mode MSP Basic
|
||||
0xFFFFFFFD Thread mode PSP Basic
|
||||
|
||||
So LR[2] == 1 => Return stack == PSP else MSP
|
||||
|
||||
R0-R3, R12, PC, xPSR can be read from application stackpointer
|
||||
Other regs can be read directly
|
||||
*/
|
||||
LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP
|
||||
ITE CS
|
||||
MRSCS R2,PSP
|
||||
MRSCC R2,MSP
|
||||
CMP R1,#+4 // if (RegIndex < 4) { (R0-R3)
|
||||
BCS _HandleReadRegR4
|
||||
LDR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3)
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR4:
|
||||
CMP R1,#+5 // if (RegIndex < 5) { (R4)
|
||||
BCS _HandleReadRegR5
|
||||
MOV R0,R4
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR5:
|
||||
CMP R1,#+6 // if (RegIndex < 6) { (R5)
|
||||
BCS _HandleReadRegR6
|
||||
MOV R0,R5
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR6:
|
||||
CMP R1,#+7 // if (RegIndex < 7) { (R6)
|
||||
BCS _HandleReadRegR7
|
||||
MOV R0,R6
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR7:
|
||||
CMP R1,#+8 // if (RegIndex < 8) { (R7)
|
||||
BCS _HandleReadRegR8
|
||||
MOV R0,R7
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR8:
|
||||
CMP R1,#+9 // if (RegIndex < 9) { (R8)
|
||||
BCS _HandleReadRegR9
|
||||
MOV R0,R8
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR9:
|
||||
CMP R1,#+10 // if (RegIndex < 10) { (R9)
|
||||
BCS _HandleReadRegR10
|
||||
MOV R0,R9
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR10:
|
||||
CMP R1,#+11 // if (RegIndex < 11) { (R10)
|
||||
BCS _HandleReadRegR11
|
||||
MOV R0,R10
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR11:
|
||||
CMP R1,#+12 // if (RegIndex < 12) { (R11)
|
||||
BCS _HandleReadRegR12
|
||||
MOV R0,R11
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR12:
|
||||
CMP R1,#+14 // if (RegIndex < 14) { (R12)
|
||||
BCS _HandleReadRegR14
|
||||
LDR R0,[R2, #+_APP_SP_OFF_R12]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR14:
|
||||
CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR)
|
||||
BCS _HandleReadRegR15
|
||||
LDR R0,[R2, #+_APP_SP_OFF_R14_LR]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegR15:
|
||||
CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC)
|
||||
BCS _HandleReadRegXPSR
|
||||
LDR R0,[R2, #+_APP_SP_OFF_PC]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegXPSR:
|
||||
CMP R1,#+17 // if (RegIndex < 17) { (xPSR)
|
||||
BCS _HandleReadRegMSP
|
||||
LDR R0,[R2, #+_APP_SP_OFF_XPSR]
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegMSP:
|
||||
/*
|
||||
Stackpointer is tricky because we need to get some info about the SP used in the user app, first
|
||||
|
||||
Handle reading R0-R3 which can be read right from application stackpointer
|
||||
|
||||
Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension
|
||||
LR Return to Return SP Frame type
|
||||
---------------------------------------------------------
|
||||
0xFFFFFFE1 Handler mode. MSP Extended
|
||||
0xFFFFFFE9 Thread mode MSP Extended
|
||||
0xFFFFFFED Thread mode PSP Extended
|
||||
0xFFFFFFF1 Handler mode. MSP Basic
|
||||
0xFFFFFFF9 Thread mode MSP Basic
|
||||
0xFFFFFFFD Thread mode PSP Basic
|
||||
|
||||
So LR[2] == 1 => Return stack == PSP else MSP
|
||||
Per architecture definition: Inside monitor (exception) SP = MSP
|
||||
|
||||
Stack pointer handling is complicated because it is different what is pushed on the stack before entering the monitor ISR...
|
||||
Cortex-M: 8 regs
|
||||
Cortex-M + forced-stack-alignment: 8 regs + 1 dummy-word if stack was not 8-byte aligned
|
||||
Cortex-M + FPU: 8 regs + 17 FPU regs + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned
|
||||
Cortex-M + FPU + lazy mode: 8 regs + 17 dummy-words + 1 dummy-word + 1-dummy word if stack was not 8-byte aligned
|
||||
*/
|
||||
CMP R1,#+18 // if (RegIndex < 18) { (MSP)
|
||||
BCS _HandleReadRegPSP
|
||||
MRS R0,MSP
|
||||
LSRS R1,LR,#+3 // LR[2] -> Carry == 0 => CPU was running on MSP => Needs correction
|
||||
BCS _HandleReadRegDone_Veneer // CPU was running on PSP? => No correction necessary
|
||||
_HandleSPCorrection:
|
||||
LSRS R1,LR,#+5 // LR[4] -> Carry == 0 => extended stack frame has been allocated. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry
|
||||
ITE CS
|
||||
ADDCS R0,R0,#+_NUM_BYTES_BASIC_STACKFRAME
|
||||
ADDCC R0,R0,#+_NUM_BYTES_EXTENDED_STACKFRAME
|
||||
LDR R1,[R2, #+_APP_SP_OFF_XPSR] // Get xPSR from application stack (R2 has been set to app stack on beginning of _HandleReadReg)
|
||||
LSRS R1,R1,#+5 // xPSR[9] -> Carry == 1 => Stack has been force-aligned before pushing regs. See ARM DDI0403D, B1.5.7 Stack alignment on exception entry
|
||||
IT CS
|
||||
ADDCS R0,R0,#+4
|
||||
B _HandleReadRegDone
|
||||
_HandleReadRegPSP: // RegIndex == 18
|
||||
CMP R1,#+19 // if (RegIndex < 19) {
|
||||
BCS _HandleReadRegCFBP
|
||||
MRS R0,PSP // PSP is not touched by monitor
|
||||
LSRS R1,LR,#+3 // LR[2] -> Carry == 1 => CPU was running on PSP => Needs correction
|
||||
BCC _HandleReadRegDone_Veneer // CPU was running on MSP? => No correction of PSP necessary
|
||||
B _HandleSPCorrection
|
||||
_HandleReadRegCFBP:
|
||||
/*
|
||||
CFBP is a register that can only be read via debug probe and is a merger of the following regs:
|
||||
CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode
|
||||
*/
|
||||
CMP R1,#+20 // if (RegIndex < 20) { (CFBP)
|
||||
BCS _HandleReadRegFPU
|
||||
MOVS R0,#+0
|
||||
MRS R2,PRIMASK
|
||||
ORRS R0,R2 // Merge PRIMASK into CFBP[7:0]
|
||||
MRS R2,BASEPRI
|
||||
LSLS R2,R2,#+8 // Merge BASEPRI into CFBP[15:8]
|
||||
ORRS R0,R2
|
||||
MRS R2,FAULTMASK
|
||||
LSLS R2,R2,#+16 // Merge FAULTMASK into CFBP[23:16]
|
||||
ORRS R0,R2
|
||||
MRS R2,CONTROL
|
||||
LSRS R1,LR,#3 // LR[2] -> Carry. CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior
|
||||
IT CS // As J-Link sees value of CONTROL at application time, we need reconstruct original value of CONTROL
|
||||
ORRCS R2,R2,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor
|
||||
LSRS R1,LR,#+5 // LR[4] == NOT(CONTROL.FPCA) -> Carry
|
||||
ITE CS // Merge original value of FPCA (CONTROL[2]) into read data
|
||||
BICCS R2,R2,#+0x04 // Remember LR contains NOT(CONTROL)
|
||||
ORRCC R2,R2,#+0x04
|
||||
LSLS R2,R2,#+24
|
||||
ORRS R0,R2
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegFPU:
|
||||
#if _HAS_FPU_REGS
|
||||
CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31)
|
||||
BCS _HandleReadRegDone_Veneer
|
||||
/*
|
||||
Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled
|
||||
If not, access to floating point is not possible
|
||||
CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
*/
|
||||
LDR R0,_AddrCPACR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+8
|
||||
LSRS R0,R0,#+28
|
||||
CMP R0,#+0xF
|
||||
BEQ _HandleReadRegFPU_Allowed
|
||||
CMP R0,#+0x5
|
||||
BNE _HandleReadRegDone_Veneer
|
||||
_HandleReadRegFPU_Allowed:
|
||||
CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR)
|
||||
BCS _HandleReadRegFPS0_FPS31
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleReadFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleReadFPSCRLazyMode
|
||||
LDR R0,[R2, #+_APP_SP_OFF_FPSCR]
|
||||
B _HandleReadRegDone
|
||||
_HandleReadFPSCRLazyMode:
|
||||
VMRS R0,FPSCR
|
||||
B _HandleReadRegDone
|
||||
_HandleReadRegFPS0_FPS31: // RegIndex == 21-52
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleReadFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleReadFPS0_FPS31LazyMode
|
||||
SUBS R1,#+21 // Convert absolute reg index into rel. one
|
||||
LSLS R1,R1,#+2 // RegIndex to position on stack
|
||||
ADDS R1,#+_APP_SP_OFF_S0
|
||||
LDR R0,[R2, R1]
|
||||
_HandleReadRegDone_Veneer:
|
||||
B _HandleReadRegDone
|
||||
_HandleReadFPS0_FPS31LazyMode:
|
||||
SUBS R1,#+20 // convert abs. RegIndex into rel. one
|
||||
MOVS R0,#+6
|
||||
MULS R1,R0,R1
|
||||
LDR R0,=_HandleReadRegUnknown
|
||||
SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1)
|
||||
ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr
|
||||
BX R0
|
||||
//
|
||||
// Table for reading FPS0-FPS31
|
||||
//
|
||||
VMOV R0,S31 // v = FPSx
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S30
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S29
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S28
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S27
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S26
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S25
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S24
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S23
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S22
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S21
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S20
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S19
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S18
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S17
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S16
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S15
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S14
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S13
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S12
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S11
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S10
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S9
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S8
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S7
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S6
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S5
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S4
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S3
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S2
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S1
|
||||
B _HandleReadRegDone
|
||||
VMOV R0,S0
|
||||
B _HandleReadRegDone
|
||||
#else
|
||||
B _HandleReadRegUnknown
|
||||
_HandleReadRegDone_Veneer:
|
||||
B _HandleReadRegDone
|
||||
#endif
|
||||
_HandleReadRegUnknown:
|
||||
MOVS R0,#+0 // v = 0
|
||||
B.N _HandleReadRegDone
|
||||
_HandleReadRegDone:
|
||||
|
||||
// Send register content to J-Link and wait until J-Link has read the data
|
||||
|
||||
STR R0,[R3, #+_SYSTEM_DCRDR_OFF] // DCRDR = v;
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Set MON_REQ bit, so J-Link knows monitor is ready to receive commands
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF] // Indicate data ready
|
||||
B _WaitProbeReadIndicateMonRdy
|
||||
|
||||
// Data section for register addresses
|
||||
|
||||
_HandleWriteReg:
|
||||
/*
|
||||
_MON_CMD_WRITE_REG
|
||||
CPU: DEMCR[19] -> 1 => Mon ready
|
||||
J-Link: Data -> DCRDR, DEMCR[19] -> 0 => Data placed by probe
|
||||
CPU: DCRDR -> Read, Process command, DEMCR[19] -> 1 => Data read & mon ready
|
||||
|
||||
Register indexes
|
||||
0-15: R0-R15 (13 == R13 reserved => is banked ... Has to be read as PSP / MSP. Decision has to be done by J-Link DLL side!)
|
||||
16: XPSR
|
||||
17: MSP
|
||||
18: PSP
|
||||
19: CFBP CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
20: FPSCR
|
||||
21-52: FPS0-FPS31
|
||||
|
||||
|
||||
Register usage when entering this "subroutine":
|
||||
R0 Cmd
|
||||
R1 ParamInfo
|
||||
R2 ---
|
||||
R3 = &_SYSTEM_DCRDR (allows also access to DEMCR with offset)
|
||||
R12 ---
|
||||
|
||||
Table B1-9 EXC_RETURN definition of exception return behavior, with FP extension
|
||||
LR Return to Return SP Frame type
|
||||
---------------------------------------------------------
|
||||
0xFFFFFFE1 Handler mode. MSP Extended
|
||||
0xFFFFFFE9 Thread mode MSP Extended
|
||||
0xFFFFFFED Thread mode PSP Extended
|
||||
0xFFFFFFF1 Handler mode. MSP Basic
|
||||
0xFFFFFFF9 Thread mode MSP Basic
|
||||
0xFFFFFFFD Thread mode PSP Basic
|
||||
|
||||
So LR[2] == 1 => Return stack == PSP else MSP
|
||||
|
||||
R0-R3, R12, PC, xPSR can be written via application stackpointer
|
||||
Other regs can be written directly
|
||||
|
||||
|
||||
Read register data from J-Link into R0
|
||||
*/
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF] // _SYSTEM_DEMCR |= (1uL << 19); => Monitor is ready to receive register data
|
||||
ORR R0,R0,#0x80000
|
||||
STR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
_HandleWRegWaitUntilDataRecv:
|
||||
LDR R0,[R3, #+_SYSTEM_DEMCR_OFF]
|
||||
LSLS R0,R0,#+12
|
||||
BMI.N _HandleWRegWaitUntilDataRecv // DEMCR[19] == 0 => J-Link has placed new data for us
|
||||
LDR R0,[R3, #+_SYSTEM_DCRDR_OFF] // Get register data
|
||||
//
|
||||
// Determine application SP
|
||||
//
|
||||
LSRS R2,LR,#+3 // Shift LR[2] into carry => Carry clear means that CPU was running on MSP
|
||||
ITE CS
|
||||
MRSCS R2,PSP
|
||||
MRSCC R2,MSP
|
||||
CMP R1,#+4 // if (RegIndex < 4) { (R0-R3)
|
||||
BCS _HandleWriteRegR4
|
||||
STR R0,[R2, R1, LSL #+2] // v = [SP + Rx * 4] (R0-R3)
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR4:
|
||||
CMP R1,#+5 // if (RegIndex < 5) { (R4)
|
||||
BCS _HandleWriteRegR5
|
||||
MOV R4,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR5:
|
||||
CMP R1,#+6 // if (RegIndex < 6) { (R5)
|
||||
BCS _HandleWriteRegR6
|
||||
MOV R5,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR6:
|
||||
CMP R1,#+7 // if (RegIndex < 7) { (R6)
|
||||
BCS _HandleWriteRegR7
|
||||
MOV R6,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR7:
|
||||
CMP R1,#+8 // if (RegIndex < 8) { (R7)
|
||||
BCS _HandleWriteRegR8
|
||||
MOV R7,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR8:
|
||||
CMP R1,#+9 // if (RegIndex < 9) { (R8)
|
||||
BCS _HandleWriteRegR9
|
||||
MOV R8,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR9:
|
||||
CMP R1,#+10 // if (RegIndex < 10) { (R9)
|
||||
BCS _HandleWriteRegR10
|
||||
MOV R9,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR10:
|
||||
CMP R1,#+11 // if (RegIndex < 11) { (R10)
|
||||
BCS _HandleWriteRegR11
|
||||
MOV R10,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR11:
|
||||
CMP R1,#+12 // if (RegIndex < 12) { (R11)
|
||||
BCS _HandleWriteRegR12
|
||||
MOV R11,R0
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR12:
|
||||
CMP R1,#+14 // if (RegIndex < 14) { (R12)
|
||||
BCS _HandleWriteRegR14
|
||||
STR R0,[R2, #+_APP_SP_OFF_R12]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR14:
|
||||
CMP R1,#+15 // if (RegIndex < 15) { (R14 / LR)
|
||||
BCS _HandleWriteRegR15
|
||||
STR R0,[R2, #+_APP_SP_OFF_R14_LR]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegR15:
|
||||
CMP R1,#+16 // if (RegIndex < 16) { (R15 / PC)
|
||||
BCS _HandleWriteRegXPSR
|
||||
STR R0,[R2, #+_APP_SP_OFF_PC]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegXPSR:
|
||||
CMP R1,#+17 // if (RegIndex < 17) { (xPSR)
|
||||
BCS _HandleWriteRegMSP
|
||||
STR R0,[R2, #+_APP_SP_OFF_XPSR]
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegMSP:
|
||||
//
|
||||
// For now, SP cannot be modified because it is needed to jump back from monitor mode
|
||||
//
|
||||
CMP R1,#+18 // if (RegIndex < 18) { (MSP)
|
||||
BCS _HandleWriteRegPSP
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegPSP: // RegIndex == 18
|
||||
CMP R1,#+19 // if (RegIndex < 19) {
|
||||
BCS _HandleWriteRegCFBP
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegCFBP:
|
||||
/*
|
||||
CFBP is a register that can only be read via debug probe and is a merger of the following regs:
|
||||
CONTROL/FAULTMASK/BASEPRI/PRIMASK (packed into 4 bytes of word. CONTROL = CFBP[31:24], FAULTMASK = CFBP[16:23], BASEPRI = CFBP[15:8], PRIMASK = CFBP[7:0]
|
||||
To keep J-Link side the same for monitor and halt mode, we also return CFBP in monitor mode
|
||||
*/
|
||||
CMP R1,#+20 // if (RegIndex < 20) { (CFBP)
|
||||
BCS _HandleWriteRegFPU
|
||||
LSLS R1,R0,#+24
|
||||
LSRS R1,R1,#+24 // Extract CFBP[7:0] => PRIMASK
|
||||
MSR PRIMASK,R1
|
||||
LSLS R1,R0,#+16
|
||||
LSRS R1,R1,#+24 // Extract CFBP[15:8] => BASEPRI
|
||||
MSR BASEPRI,R1
|
||||
LSLS R1,R0,#+8 // Extract CFBP[23:16] => FAULTMASK
|
||||
LSRS R1,R1,#+24
|
||||
MSR FAULTMASK,R1
|
||||
LSRS R1,R0,#+24 // Extract CFBP[31:24] => CONTROL
|
||||
LSRS R0,R1,#2 // Current CONTROL[1] -> Carry
|
||||
ITE CS // Update saved CONTROL.SPSEL (CONTROL[1]). CONTROL.SPSEL is saved to LR[2] on exception entry => ARM DDI0403D, B1.5.6 Exception entry behavior
|
||||
ORRCS LR,LR,#+4
|
||||
BICCC LR,LR,#+4
|
||||
BIC R1,R1,#+2 // CONTROL.SPSEL (CONTROL[1]) == 0 inside monitor. Otherwise behavior is UNPREDICTABLE
|
||||
LSRS R0,R1,#+3 // New CONTROL.FPCA (CONTROL[2]) -> Carry
|
||||
ITE CS // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BICCS LR,LR,#+0x10 // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
ORRCC LR,LR,#+0x10
|
||||
MRS R0,CONTROL
|
||||
LSRS R0,R0,#+3 // CONTROL[2] -> Carry
|
||||
ITE CS // Preserve original value of current CONTROL[2]
|
||||
ORRCS R1,R1,#+0x04
|
||||
BICCC R1,R1,#+0x04
|
||||
MSR CONTROL,R1
|
||||
ISB // Necessary after writing to CONTROL, see ARM DDI0403D, B1.4.4 The special-purpose CONTROL register
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegFPU:
|
||||
#if _HAS_FPU_REGS
|
||||
CMP R1,#+53 // if (RegIndex < 53) { (20 (FPSCR), 21-52 FPS0-FPS31)
|
||||
BCS _HandleWriteRegDone_Veneer
|
||||
/*
|
||||
Read Coprocessor Access Control Register (CPACR) to check if CP10 and CP11 are enabled
|
||||
If not, access to floating point is not possible
|
||||
CPACR[21:20] == CP10 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
CPACR[23:22] == CP11 enable. 0b01 = Privileged access only. 0b11 = Full access. Other = reserved
|
||||
*/
|
||||
MOV R12,R0 // Save register data
|
||||
LDR R0,_AddrCPACR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+8
|
||||
LSRS R0,R0,#+28
|
||||
CMP R0,#+0xF
|
||||
BEQ _HandleWriteRegFPU_Allowed
|
||||
CMP R0,#+0x5
|
||||
BNE _HandleWriteRegDone_Veneer
|
||||
_HandleWriteRegFPU_Allowed:
|
||||
CMP R1,#+21 // if (RegIndex < 21) (20 == FPSCR)
|
||||
BCS _HandleWriteRegFPS0_FPS31
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleWriteFPSCRLazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleWriteFPSCRLazyMode
|
||||
STR R12,[R2, #+_APP_SP_OFF_FPSCR]
|
||||
B _HandleWriteRegDone
|
||||
_HandleWriteFPSCRLazyMode:
|
||||
VMSR FPSCR,R12
|
||||
B _HandleWriteRegDone
|
||||
_HandleWriteRegFPS0_FPS31: // RegIndex == 21-52
|
||||
LDR R0,=_SYSTEM_FPCCR
|
||||
LDR R0,[R0]
|
||||
LSLS R0,R0,#+2 // FPCCR[30] -> Carry == 1 indicates if lazy mode is active, so space on stack is reserved but FPU registers are not saved on stack
|
||||
BCS _HandleWriteFPS0_FPS31LazyMode
|
||||
LSRS R0,LR,#+5 // CONTROL[2] == FPCA => NOT(FPCA) saved to LR[4]. LR[4] == 0 => Extended stack frame, so FPU regs possibly on stack
|
||||
BCS _HandleWriteFPS0_FPS31LazyMode // Remember: NOT(FPCA) is stored to LR. == 0 means: Extended stack frame
|
||||
SUBS R1,#+21 // Convert absolute reg index into rel. one
|
||||
LSLS R1,R1,#+2 // RegIndex to position on stack
|
||||
ADDS R1,#+_APP_SP_OFF_S0
|
||||
STR R12,[R2, R1]
|
||||
_HandleWriteRegDone_Veneer:
|
||||
B _HandleWriteRegDone
|
||||
_HandleWriteFPS0_FPS31LazyMode:
|
||||
SUBS R1,#+20 // Convert abs. RegIndex into rel. one
|
||||
MOVS R0,#+6
|
||||
MULS R1,R0,R1
|
||||
LDR R0,=_HandleReadRegUnknown
|
||||
SUB R0,R0,R1 // _HandleReadRegUnknown - 6 * ((RegIndex - 21) + 1)
|
||||
ORR R0,R0,#1 // Thumb bit needs to be set in DestAddr
|
||||
BX R0
|
||||
//
|
||||
// Table for reading FPS0-FPS31
|
||||
//
|
||||
VMOV S31,R12 // v = FPSx
|
||||
B _HandleWriteRegDone
|
||||
VMOV S30,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S29,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S28,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S27,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S26,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S25,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S24,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S23,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S22,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S21,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S20,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S19,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S18,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S17,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S16,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S15,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S14,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S13,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S12,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S11,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S10,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S9,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S8,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S7,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S6,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S5,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S4,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S3,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S2,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S1,R12
|
||||
B _HandleWriteRegDone
|
||||
VMOV S0,R12
|
||||
B _HandleWriteRegDone
|
||||
#else
|
||||
B _HandleWriteRegUnknown
|
||||
#endif
|
||||
_HandleWriteRegUnknown:
|
||||
B.N _HandleWriteRegDone
|
||||
_HandleWriteRegDone:
|
||||
B _IndicateMonReady // Indicate that monitor has read data, processed command and is ready for a new one
|
||||
.end
|
||||
/****** End Of File *************************************************/
|
||||
284
src/platform/nrf52/NRF52Bluetooth.cpp
Normal file
284
src/platform/nrf52/NRF52Bluetooth.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
#include "configuration.h"
|
||||
#include "NRF52Bluetooth.h"
|
||||
#include "BluetoothCommon.h"
|
||||
#include "main.h"
|
||||
#include "mesh/PhoneAPI.h"
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include <bluefruit.h>
|
||||
#include <utility/bonding.h>
|
||||
|
||||
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
|
||||
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
|
||||
static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
|
||||
static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
|
||||
|
||||
static BLEDis bledis; // DIS (Device Information Service) helper class instance
|
||||
static BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
static BLEDfu bledfu; // DFU software update helper service
|
||||
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
// proccess at once
|
||||
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
|
||||
static uint8_t fromRadioBytes[FromRadio_size];
|
||||
static uint8_t toRadioBytes[ToRadio_size];
|
||||
|
||||
static bool bleConnected;
|
||||
|
||||
class BluetoothPhoneAPI : public PhoneAPI
|
||||
{
|
||||
/**
|
||||
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
|
||||
*/
|
||||
virtual void onNowHasData(uint32_t fromRadioNum) override
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
DEBUG_MSG("BLE notify fromNum\n");
|
||||
fromNum.notify32(fromRadioNum);
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override {
|
||||
return bleConnected;
|
||||
}
|
||||
};
|
||||
|
||||
static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
void connect_callback(uint16_t conn_handle)
|
||||
{
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
|
||||
DEBUG_MSG("BLE Connected to %s\n", central_name);
|
||||
bleConnected = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked when a connection is dropped
|
||||
* @param conn_handle connection where this event happens
|
||||
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
|
||||
*/
|
||||
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
|
||||
{
|
||||
// FIXME - we currently assume only one active connection
|
||||
bleConnected = false;
|
||||
|
||||
DEBUG_MSG("BLE Disconnected, reason = 0x%x\n", reason);
|
||||
}
|
||||
|
||||
void cccd_callback(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
||||
{
|
||||
// Display the raw request packet
|
||||
DEBUG_MSG("CCCD Updated: %u\n", cccd_value);
|
||||
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
if (chr->uuid == fromNum.uuid) {
|
||||
if (chr->notifyEnabled(conn_hdl)) {
|
||||
DEBUG_MSG("fromNum 'Notify' enabled\n");
|
||||
} else {
|
||||
DEBUG_MSG("fromNum 'Notify' disabled\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startAdv(void)
|
||||
{
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
|
||||
// IncludeService UUID
|
||||
// Bluefruit.ScanResponse.addService(meshBleService);
|
||||
Bluefruit.ScanResponse.addTxPower();
|
||||
Bluefruit.ScanResponse.addName();
|
||||
|
||||
// Include Name
|
||||
// Bluefruit.Advertising.addName();
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
* For recommended advertising interval
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
|
||||
}
|
||||
|
||||
// Just ack that the caller is allowed to read
|
||||
static void authorizeRead(uint16_t conn_hdl)
|
||||
{
|
||||
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_READ};
|
||||
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* client is starting read, pull the bytes from our API class
|
||||
*/
|
||||
void fromRadioAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
||||
{
|
||||
if (request->offset == 0) {
|
||||
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
|
||||
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
|
||||
// DEBUG_MSG("fromRadioAuthorizeCb numBytes=%u\n", numBytes);
|
||||
// if (numBytes >= 2) DEBUG_MSG("fromRadio bytes %x %x\n", fromRadioBytes[0], fromRadioBytes[1]);
|
||||
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
fromRadio.write(fromRadioBytes, numBytes);
|
||||
} else {
|
||||
// DEBUG_MSG("Ignoring successor read\n");
|
||||
}
|
||||
authorizeRead(conn_hdl);
|
||||
}
|
||||
|
||||
void toRadioWriteCb(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
|
||||
{
|
||||
DEBUG_MSG("toRadioWriteCb data %p, len %u\n", data, len);
|
||||
|
||||
bluetoothPhoneAPI->handleToRadio(data, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* client is starting read, pull the bytes from our API class
|
||||
*/
|
||||
void fromNumAuthorizeCb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
||||
{
|
||||
DEBUG_MSG("fromNumAuthorizeCb\n");
|
||||
|
||||
authorizeRead(conn_hdl);
|
||||
}
|
||||
|
||||
void setupMeshService(void)
|
||||
{
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
meshBleService.begin();
|
||||
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
|
||||
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
fromNum.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
||||
fromNum.setFixedLen(
|
||||
0); // Variable len (either 0 or 4) FIXME consider changing protocol so it is fixed 4 byte len, where 0 means empty
|
||||
fromNum.setMaxLen(4);
|
||||
fromNum.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
|
||||
// We don't yet need to hook the fromNum auth callback
|
||||
// fromNum.setReadAuthorizeCallback(fromNumAuthorizeCb);
|
||||
fromNum.write32(0); // Provide default fromNum of 0
|
||||
fromNum.begin();
|
||||
// uint8_t hrmdata[2] = {0b00000110, 0x40}; // Set the characteristic to use 8-bit values, with the sensor connected and
|
||||
// detected
|
||||
// hrmc.write(hrmdata, 2);
|
||||
|
||||
fromRadio.setProperties(CHR_PROPS_READ);
|
||||
fromRadio.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); // FIXME secure this!
|
||||
fromRadio.setMaxLen(sizeof(fromRadioBytes));
|
||||
fromRadio.setReadAuthorizeCallback(
|
||||
fromRadioAuthorizeCb,
|
||||
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
fromRadio.setBuffer(fromRadioBytes, sizeof(fromRadioBytes)); // we preallocate our fromradio buffer so we won't waste space
|
||||
// for two copies
|
||||
fromRadio.begin();
|
||||
|
||||
toRadio.setProperties(CHR_PROPS_WRITE);
|
||||
toRadio.setPermission(SECMODE_OPEN, SECMODE_OPEN); // FIXME secure this!
|
||||
toRadio.setFixedLen(0);
|
||||
toRadio.setMaxLen(512);
|
||||
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
|
||||
toRadio.setWriteCallback(
|
||||
toRadioWriteCb,
|
||||
false); // We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
toRadio.begin();
|
||||
}
|
||||
|
||||
// FIXME, turn off soft device access for debugging
|
||||
static bool isSoftDeviceAllowed = true;
|
||||
|
||||
void NRF52Bluetooth::shutdown()
|
||||
{
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
DEBUG_MSG("Disable NRF52 bluetooth\n");
|
||||
Bluefruit.Advertising.stop();
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::setup()
|
||||
{
|
||||
// Initialise the Bluefruit module
|
||||
DEBUG_MSG("Initialise the Bluefruit nRF52 module\n");
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.begin();
|
||||
|
||||
// Clear existing data.
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
Bluefruit.ScanResponse.clearData();
|
||||
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName());
|
||||
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(connect_callback);
|
||||
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
|
||||
|
||||
// Configure and Start the Device Information Service
|
||||
DEBUG_MSG("Configuring the Device Information Service\n");
|
||||
// FIXME, we should set a mfg string based on our HW_VENDOR enum
|
||||
// bledis.setManufacturer(HW_VENDOR);
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
DEBUG_MSG("Configuring the Battery Service\n");
|
||||
blebas.begin();
|
||||
blebas.write(0); // Unknown battery level for now
|
||||
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
DEBUG_MSG("Configuring the Mesh bluetooth service\n");
|
||||
setupMeshService();
|
||||
|
||||
// Supposedly debugging works with soft device if you disable advertising
|
||||
if (isSoftDeviceAllowed) {
|
||||
// Setup the advertising packet(s)
|
||||
DEBUG_MSG("Setting up the advertising payload(s)\n");
|
||||
startAdv();
|
||||
|
||||
DEBUG_MSG("Advertising\n");
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
blebas.write(level);
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::clearBonds()
|
||||
{
|
||||
DEBUG_MSG("Clearing bluetooth bonds!\n");
|
||||
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
|
||||
Bluefruit.Periph.clearBonds();
|
||||
Bluefruit.Central.clearBonds();
|
||||
}
|
||||
10
src/platform/nrf52/NRF52Bluetooth.h
Normal file
10
src/platform/nrf52/NRF52Bluetooth.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
class NRF52Bluetooth
|
||||
{
|
||||
public:
|
||||
void setup();
|
||||
void shutdown();
|
||||
void clearBonds();
|
||||
};
|
||||
|
||||
49
src/platform/nrf52/NRF52CryptoEngine.cpp
Normal file
49
src/platform/nrf52/NRF52CryptoEngine.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "configuration.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
#include "aes-256/tiny-aes.h"
|
||||
class NRF52CryptoEngine : public CryptoEngine
|
||||
{
|
||||
public:
|
||||
NRF52CryptoEngine() {}
|
||||
|
||||
~NRF52CryptoEngine() {}
|
||||
|
||||
/**
|
||||
* Encrypt a packet
|
||||
*
|
||||
* @param bytes is updated in place
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
if (key.length > 16) {
|
||||
DEBUG_MSG("Software encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetId, numBytes);
|
||||
AES_ctx ctx;
|
||||
initNonce(fromNode, packetId);
|
||||
AES_init_ctx_iv(&ctx, key.bytes, nonce);
|
||||
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
|
||||
} else if (key.length > 0) {
|
||||
DEBUG_MSG("nRF52 encrypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetId, numBytes);
|
||||
nRFCrypto.begin();
|
||||
nRFCrypto_AES ctx;
|
||||
uint8_t myLen = ctx.blockLen(numBytes);
|
||||
char encBuf[myLen] = {0};
|
||||
initNonce(fromNode, packetId);
|
||||
ctx.begin();
|
||||
ctx.Process((char*)bytes, numBytes, nonce, key.bytes, key.length, encBuf, ctx.encryptFlag, ctx.ctrMode);
|
||||
ctx.end();
|
||||
nRFCrypto.end();
|
||||
memcpy(bytes, encBuf, numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
// For CTR, the implementation is the same
|
||||
encrypt(fromNode, packetId, numBytes, bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
CryptoEngine *crypto = new NRF52CryptoEngine();
|
||||
229
src/platform/nrf52/aes-256/tiny-aes.cpp
Normal file
229
src/platform/nrf52/aes-256/tiny-aes.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
AES-256 Software Implementation
|
||||
|
||||
based on https://github.com/kokke/tiny-AES-C/ which is in public domain
|
||||
|
||||
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
|
||||
You should pad the end of the string with zeros if this is not the case.
|
||||
For AES192/256 the key size is proportionally larger.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "tiny-aes.h"
|
||||
|
||||
#define Nb 4
|
||||
#define Nk 8
|
||||
#define Nr 14
|
||||
|
||||
typedef uint8_t state_t[4][4];
|
||||
|
||||
static const uint8_t sbox[256] = {
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
|
||||
|
||||
static const uint8_t Rcon[11] = {
|
||||
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
|
||||
|
||||
#define getSBoxValue(num) (sbox[(num)])
|
||||
|
||||
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
|
||||
{
|
||||
uint8_t tempa[4];
|
||||
|
||||
for (unsigned i = 0; i < Nk; ++i)
|
||||
{
|
||||
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
|
||||
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
|
||||
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
|
||||
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
|
||||
}
|
||||
|
||||
for (unsigned i = Nk; i < Nb * (Nr + 1); ++i)
|
||||
{
|
||||
unsigned k = (i - 1) * 4;
|
||||
tempa[0]=RoundKey[k + 0];
|
||||
tempa[1]=RoundKey[k + 1];
|
||||
tempa[2]=RoundKey[k + 2];
|
||||
tempa[3]=RoundKey[k + 3];
|
||||
|
||||
if (i % Nk == 0)
|
||||
{
|
||||
const uint8_t u8tmp = tempa[0];
|
||||
tempa[0] = tempa[1];
|
||||
tempa[1] = tempa[2];
|
||||
tempa[2] = tempa[3];
|
||||
tempa[3] = u8tmp;
|
||||
|
||||
tempa[0] = getSBoxValue(tempa[0]);
|
||||
tempa[1] = getSBoxValue(tempa[1]);
|
||||
tempa[2] = getSBoxValue(tempa[2]);
|
||||
tempa[3] = getSBoxValue(tempa[3]);
|
||||
|
||||
tempa[0] = tempa[0] ^ Rcon[i/Nk];
|
||||
}
|
||||
|
||||
if (i % Nk == 4)
|
||||
{
|
||||
tempa[0] = getSBoxValue(tempa[0]);
|
||||
tempa[1] = getSBoxValue(tempa[1]);
|
||||
tempa[2] = getSBoxValue(tempa[2]);
|
||||
tempa[3] = getSBoxValue(tempa[3]);
|
||||
}
|
||||
|
||||
unsigned j = i * 4; k=(i - Nk) * 4;
|
||||
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
|
||||
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
|
||||
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
|
||||
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
|
||||
}
|
||||
}
|
||||
|
||||
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
|
||||
{
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
}
|
||||
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
|
||||
{
|
||||
KeyExpansion(ctx->RoundKey, key);
|
||||
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
|
||||
}
|
||||
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
|
||||
{
|
||||
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
|
||||
}
|
||||
|
||||
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
{
|
||||
for (uint8_t j = 0; j < 4; ++j)
|
||||
{
|
||||
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SubBytes(state_t* state)
|
||||
{
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
{
|
||||
for (uint8_t j = 0; j < 4; ++j)
|
||||
{
|
||||
(*state)[j][i] = getSBoxValue((*state)[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ShiftRows(state_t* state)
|
||||
{
|
||||
uint8_t temp = (*state)[0][1];
|
||||
(*state)[0][1] = (*state)[1][1];
|
||||
(*state)[1][1] = (*state)[2][1];
|
||||
(*state)[2][1] = (*state)[3][1];
|
||||
(*state)[3][1] = temp;
|
||||
|
||||
temp = (*state)[0][2];
|
||||
(*state)[0][2] = (*state)[2][2];
|
||||
(*state)[2][2] = temp;
|
||||
|
||||
temp = (*state)[1][2];
|
||||
(*state)[1][2] = (*state)[3][2];
|
||||
(*state)[3][2] = temp;
|
||||
|
||||
temp = (*state)[0][3];
|
||||
(*state)[0][3] = (*state)[3][3];
|
||||
(*state)[3][3] = (*state)[2][3];
|
||||
(*state)[2][3] = (*state)[1][3];
|
||||
(*state)[1][3] = temp;
|
||||
}
|
||||
|
||||
static uint8_t xtime(uint8_t x)
|
||||
{
|
||||
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
|
||||
}
|
||||
|
||||
static void MixColumns(state_t* state)
|
||||
{
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
{
|
||||
uint8_t t = (*state)[i][0];
|
||||
uint8_t Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
|
||||
uint8_t Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
|
||||
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
|
||||
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
|
||||
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
|
||||
}
|
||||
}
|
||||
|
||||
#define Multiply(x, y) \
|
||||
( ((y & 1) * x) ^ \
|
||||
((y>>1 & 1) * xtime(x)) ^ \
|
||||
((y>>2 & 1) * xtime(xtime(x))) ^ \
|
||||
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
|
||||
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
|
||||
|
||||
|
||||
static void Cipher(state_t* state, const uint8_t* RoundKey)
|
||||
{
|
||||
uint8_t round = 0;
|
||||
|
||||
AddRoundKey(0, state, RoundKey);
|
||||
|
||||
for (round = 1; ; ++round)
|
||||
{
|
||||
SubBytes(state);
|
||||
ShiftRows(state);
|
||||
if (round == Nr) {
|
||||
break;
|
||||
}
|
||||
MixColumns(state);
|
||||
AddRoundKey(round, state, RoundKey);
|
||||
}
|
||||
AddRoundKey(Nr, state, RoundKey);
|
||||
}
|
||||
|
||||
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
|
||||
{
|
||||
uint8_t buffer[AES_BLOCKLEN];
|
||||
|
||||
size_t i;
|
||||
int bi;
|
||||
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
|
||||
{
|
||||
if (bi == AES_BLOCKLEN)
|
||||
{
|
||||
|
||||
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
|
||||
Cipher((state_t*)buffer,ctx->RoundKey);
|
||||
|
||||
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
|
||||
{
|
||||
if (ctx->Iv[bi] == 255)
|
||||
{
|
||||
ctx->Iv[bi] = 0;
|
||||
continue;
|
||||
}
|
||||
ctx->Iv[bi] += 1;
|
||||
break;
|
||||
}
|
||||
bi = 0;
|
||||
}
|
||||
|
||||
buf[i] = (buf[i] ^ buffer[bi]);
|
||||
}
|
||||
}
|
||||
23
src/platform/nrf52/aes-256/tiny-aes.h
Normal file
23
src/platform/nrf52/aes-256/tiny-aes.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _TINY_AES_H_
|
||||
#define _TINY_AES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
|
||||
// #define AES_KEYLEN 32
|
||||
#define AES_keyExpSize 240
|
||||
|
||||
struct AES_ctx
|
||||
{
|
||||
uint8_t RoundKey[AES_keyExpSize];
|
||||
uint8_t Iv[AES_BLOCKLEN];
|
||||
};
|
||||
|
||||
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
|
||||
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
|
||||
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
|
||||
|
||||
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||
|
||||
#endif // _TINY_AES_H_
|
||||
32
src/platform/nrf52/alloc.cpp
Normal file
32
src/platform/nrf52/alloc.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "configuration.h"
|
||||
#include "rtos.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Custom new/delete to panic if out out memory
|
||||
*/
|
||||
|
||||
void *operator new(size_t size)
|
||||
{
|
||||
auto p = rtos_malloc(size);
|
||||
assert(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *operator new[](size_t size)
|
||||
{
|
||||
auto p = rtos_malloc(size);
|
||||
assert(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void operator delete(void *ptr)
|
||||
{
|
||||
rtos_free(ptr);
|
||||
}
|
||||
|
||||
void operator delete[](void *ptr)
|
||||
{
|
||||
rtos_free(ptr);
|
||||
}
|
||||
93
src/platform/nrf52/architecture.h
Normal file
93
src/platform/nrf52/architecture.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#define ARCH_NRF52
|
||||
|
||||
//
|
||||
// defaults for NRF52 architecture
|
||||
//
|
||||
|
||||
#ifndef HAS_SCREEN
|
||||
#define HAS_SCREEN 1
|
||||
#endif
|
||||
#ifndef HAS_WIRE
|
||||
#define HAS_WIRE 1
|
||||
#endif
|
||||
#ifndef HAS_GPS
|
||||
#define HAS_GPS 1
|
||||
#endif
|
||||
#ifndef HAS_BUTTON
|
||||
#define HAS_BUTTON 1
|
||||
#endif
|
||||
#ifndef HAS_TELEMETRY
|
||||
#define HAS_TELEMETRY 1
|
||||
#endif
|
||||
#ifndef HAS_RADIO
|
||||
#define HAS_RADIO 1
|
||||
#endif
|
||||
|
||||
//
|
||||
// set HW_VENDOR
|
||||
//
|
||||
|
||||
// This string must exactly match the case used in release file names or the android updater won't work
|
||||
#ifdef ARDUINO_NRF52840_PCA10056
|
||||
#define HW_VENDOR HardwareModel_NRF52840DK
|
||||
#elif defined(ARDUINO_NRF52840_PPR)
|
||||
#define HW_VENDOR HardwareModel_PPR
|
||||
#elif defined(RAK4630)
|
||||
#define HW_VENDOR HardwareModel_RAK4631
|
||||
#elif defined(TTGO_T_ECHO)
|
||||
#define HW_VENDOR HardwareModel_T_ECHO
|
||||
#elif defined(NORDIC_PCA10059)
|
||||
#define HW_VENDOR HardwareModel_NRF52840_PCA10059
|
||||
#elif defined(PRIVATE_HW)
|
||||
#define HW_VENDOR HardwareModel_PRIVATE_HW
|
||||
#else
|
||||
#define HW_VENDOR HardwareModel_NRF52_UNKNOWN
|
||||
#endif
|
||||
|
||||
//
|
||||
// Standard definitions for NRF52 targets
|
||||
//
|
||||
|
||||
#ifdef ARDUINO_NRF52840_PCA10056
|
||||
|
||||
// This board uses 0 to be mean LED on
|
||||
#undef LED_INVERTED
|
||||
#define LED_INVERTED 1
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef TTGO_T_ECHO
|
||||
#define GPS_UBLOX
|
||||
#endif
|
||||
|
||||
#define LED_PIN PIN_LED1 // LED1 on nrf52840-DK
|
||||
|
||||
#ifdef PIN_BUTTON1
|
||||
#define BUTTON_PIN PIN_BUTTON1
|
||||
#endif
|
||||
|
||||
#ifdef PIN_BUTTON2
|
||||
#define BUTTON_PIN_ALT PIN_BUTTON2
|
||||
#endif
|
||||
|
||||
#ifdef PIN_BUTTON_TOUCH
|
||||
#define BUTTON_PIN_TOUCH PIN_BUTTON_TOUCH
|
||||
#endif
|
||||
|
||||
// Always include the SEGGER code on NRF52 - because useful for debugging
|
||||
#include "SEGGER_RTT.h"
|
||||
|
||||
// The channel we send stdout data to
|
||||
#define SEGGER_STDOUT_CH 0
|
||||
|
||||
// Debug printing to segger console
|
||||
#define SEGGER_MSG(...) SEGGER_RTT_printf(SEGGER_STDOUT_CH, __VA_ARGS__)
|
||||
|
||||
// If we are not on a NRF52840 (which has built in USB-ACM serial support) and we don't have serial pins hooked up, then we MUST
|
||||
// use SEGGER for debug output
|
||||
#if !defined(PIN_SERIAL_RX) && !defined(NRF52840_XXAA)
|
||||
// No serial ports on this board - ONLY use segger in memory console
|
||||
#define USE_SEGGER
|
||||
#endif
|
||||
115
src/platform/nrf52/hardfault.cpp
Normal file
115
src/platform/nrf52/hardfault.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "configuration.h"
|
||||
#include <core_cm4.h>
|
||||
|
||||
// Based on reading/modifying https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/
|
||||
|
||||
enum { r0, r1, r2, r3, r12, lr, pc, psr };
|
||||
|
||||
// we can't use the regular DEBUG_MSG for these crash dumps because it depends on threading still being running. Instead use the
|
||||
// segger in memory tool
|
||||
#define FAULT_MSG(...) SEGGER_MSG(__VA_ARGS__)
|
||||
|
||||
// Per http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Cihcfefj.html
|
||||
static void printUsageErrorMsg(uint32_t cfsr)
|
||||
{
|
||||
FAULT_MSG("Usage fault: ");
|
||||
cfsr >>= SCB_CFSR_USGFAULTSR_Pos; // right shift to lsb
|
||||
if ((cfsr & (1 << 9)) != 0)
|
||||
FAULT_MSG("Divide by zero\n");
|
||||
else if ((cfsr & (1 << 8)) != 0)
|
||||
FAULT_MSG("Unaligned\n");
|
||||
else if ((cfsr & (1 << 1)) != 0)
|
||||
FAULT_MSG("Invalid state\n");
|
||||
else if ((cfsr & (1 << 0)) != 0)
|
||||
FAULT_MSG("Invalid instruction\n");
|
||||
else
|
||||
FAULT_MSG("FIXME add to printUsageErrorMsg!\n");
|
||||
}
|
||||
|
||||
static void printBusErrorMsg(uint32_t cfsr)
|
||||
{
|
||||
FAULT_MSG("Bus fault: ");
|
||||
cfsr >>= SCB_CFSR_BUSFAULTSR_Pos; // right shift to lsb
|
||||
if ((cfsr & (1 << 0)) != 0)
|
||||
FAULT_MSG("Instruction bus error\n");
|
||||
if ((cfsr & (1 << 1)) != 0)
|
||||
FAULT_MSG("Precise data bus error\n");
|
||||
if ((cfsr & (1 << 2)) != 0)
|
||||
FAULT_MSG("Imprecise data bus error\n");
|
||||
}
|
||||
|
||||
static void printMemErrorMsg(uint32_t cfsr)
|
||||
{
|
||||
FAULT_MSG("Memory fault: ");
|
||||
cfsr >>= SCB_CFSR_MEMFAULTSR_Pos; // right shift to lsb
|
||||
if ((cfsr & (1 << 0)) != 0)
|
||||
FAULT_MSG("Instruction access violation\n");
|
||||
if ((cfsr & (1 << 1)) != 0)
|
||||
FAULT_MSG("Data access violation\n");
|
||||
}
|
||||
|
||||
extern "C" void HardFault_Impl(uint32_t stack[])
|
||||
{
|
||||
FAULT_MSG("Hard Fault occurred! SCB->HFSR = 0x%08lx\n", SCB->HFSR);
|
||||
|
||||
if ((SCB->HFSR & SCB_HFSR_FORCED_Msk) != 0) {
|
||||
FAULT_MSG("Forced Hard Fault: SCB->CFSR = 0x%08lx\n", SCB->CFSR);
|
||||
|
||||
if ((SCB->CFSR & SCB_CFSR_USGFAULTSR_Msk) != 0) {
|
||||
printUsageErrorMsg(SCB->CFSR);
|
||||
}
|
||||
if ((SCB->CFSR & SCB_CFSR_BUSFAULTSR_Msk) != 0) {
|
||||
printBusErrorMsg(SCB->CFSR);
|
||||
}
|
||||
if ((SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk) != 0) {
|
||||
printMemErrorMsg(SCB->CFSR);
|
||||
}
|
||||
|
||||
FAULT_MSG("r0 = 0x%08lx\n", stack[r0]);
|
||||
FAULT_MSG("r1 = 0x%08lx\n", stack[r1]);
|
||||
FAULT_MSG("r2 = 0x%08lx\n", stack[r2]);
|
||||
FAULT_MSG("r3 = 0x%08lx\n", stack[r3]);
|
||||
FAULT_MSG("r12 = 0x%08lx\n", stack[r12]);
|
||||
FAULT_MSG("lr = 0x%08lx\n", stack[lr]);
|
||||
FAULT_MSG("pc = 0x%08lx\n", stack[pc]);
|
||||
FAULT_MSG("psr = 0x%08lx\n", stack[psr]);
|
||||
}
|
||||
|
||||
FAULT_MSG("Done with fault report - Waiting to reboot\n");
|
||||
asm volatile("bkpt #01"); // Enter the debugger if one is connected
|
||||
|
||||
// Don't spin, so that the debugger will let the user step to next instruction
|
||||
// while (1) ;
|
||||
}
|
||||
|
||||
#ifndef INC_FREERTOS_H
|
||||
// This is a generic cortex M entrypoint that doesn't assume freertos
|
||||
|
||||
extern "C" void HardFault_Handler(void)
|
||||
{
|
||||
asm volatile(" mrs r0,msp\n"
|
||||
" b HardFault_Impl \n");
|
||||
}
|
||||
#else
|
||||
|
||||
/* The prototype shows it is a naked function - in effect this is just an
|
||||
assembly function. */
|
||||
extern "C" void HardFault_Handler( void ) __attribute__( ( naked ) );
|
||||
|
||||
/* The fault handler implementation calls a function called
|
||||
prvGetRegistersFromStack(). */
|
||||
extern "C" void HardFault_Handler(void)
|
||||
{
|
||||
__asm volatile
|
||||
(
|
||||
" tst lr, #4 \n"
|
||||
" ite eq \n"
|
||||
" mrseq r0, msp \n"
|
||||
" mrsne r0, psp \n"
|
||||
" ldr r1, [r0, #24] \n"
|
||||
" ldr r2, handler2_address_const \n"
|
||||
" bx r2 \n"
|
||||
" handler2_address_const: .word HardFault_Impl \n"
|
||||
);
|
||||
}
|
||||
#endif
|
||||
2
src/platform/nrf52/main-bare.cpp
Normal file
2
src/platform/nrf52/main-bare.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "target_specific.h"
|
||||
|
||||
199
src/platform/nrf52/main-nrf52.cpp
Normal file
199
src/platform/nrf52/main-nrf52.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "configuration.h"
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include <assert.h>
|
||||
#include <ble_gap.h>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include <Adafruit_nRFCrypto.h>
|
||||
// #include <Adafruit_USBD_Device.h>
|
||||
|
||||
#include "NRF52Bluetooth.h"
|
||||
#include "error.h"
|
||||
|
||||
#ifdef BQ25703A_ADDR
|
||||
#include "BQ25713.h"
|
||||
#endif
|
||||
|
||||
static inline void debugger_break(void)
|
||||
{
|
||||
__asm volatile("bkpt #0x01\n\t"
|
||||
"mov pc, lr\n\t");
|
||||
}
|
||||
|
||||
bool loopCanSleep() {
|
||||
// turn off sleep only while connected via USB
|
||||
// return true;
|
||||
return !Serial; // the bool operator on the nrf52 serial class returns true if connected to a PC currently
|
||||
// return !(TinyUSBDevice.mounted() && !TinyUSBDevice.suspended());
|
||||
}
|
||||
|
||||
// handle standard gcc assert failures
|
||||
void __attribute__((noreturn)) __assert_func(const char *file, int line, const char *func, const char *failedexpr)
|
||||
{
|
||||
DEBUG_MSG("assert failed %s: %d, %s, test=%s\n", file, line, func, failedexpr);
|
||||
// debugger_break(); FIXME doesn't work, possibly not for segger
|
||||
// Reboot cpu
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
const uint8_t *src = (const uint8_t *)NRF_FICR->DEVICEADDR;
|
||||
dmac[5] = src[0];
|
||||
dmac[4] = src[1];
|
||||
dmac[3] = src[2];
|
||||
dmac[2] = src[3];
|
||||
dmac[1] = src[4];
|
||||
dmac[0] = src[5] | 0xc0; // MSB high two bits get set elsewhere in the bluetooth stack
|
||||
}
|
||||
|
||||
static void initBrownout()
|
||||
{
|
||||
auto vccthresh = POWER_POFCON_THRESHOLD_V17;
|
||||
|
||||
auto err_code = sd_power_pof_enable(POWER_POFCON_POF_Enabled);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
|
||||
err_code = sd_power_pof_threshold_set(vccthresh);
|
||||
assert(err_code == NRF_SUCCESS);
|
||||
|
||||
// We don't bother with setting up brownout if soft device is disabled - because during production we always use softdevice
|
||||
}
|
||||
|
||||
NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
static bool bleOn = false;
|
||||
static const bool useSoftDevice = true; // Set to false for easier debugging
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
if (on != bleOn) {
|
||||
if (on) {
|
||||
if (!nrf52Bluetooth) {
|
||||
if (!useSoftDevice)
|
||||
DEBUG_MSG("DISABLING NRF52 BLUETOOTH WHILE DEBUGGING\n");
|
||||
else {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
|
||||
// We delay brownout init until after BLE because BLE starts soft device
|
||||
initBrownout();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (nrf52Bluetooth)
|
||||
nrf52Bluetooth->shutdown();
|
||||
}
|
||||
bleOn = on;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override printf to use the SEGGER output library (note - this does not effect the printf method on the debug console)
|
||||
*/
|
||||
int printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
auto res = SEGGER_RTT_vprintf(0, fmt, &args);
|
||||
va_end(args);
|
||||
return res;
|
||||
}
|
||||
|
||||
void checkSDEvents()
|
||||
{
|
||||
if (useSoftDevice) {
|
||||
uint32_t evt;
|
||||
while (NRF_SUCCESS == sd_evt_get(&evt)) {
|
||||
switch (evt) {
|
||||
case NRF_EVT_POWER_FAILURE_WARNING:
|
||||
RECORD_CRITICALERROR(CriticalErrorCode_Brownout);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_MSG("Unexpected SDevt %d\n", evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (NRF_POWER->EVENTS_POFWARN)
|
||||
RECORD_CRITICALERROR(CriticalErrorCode_Brownout);
|
||||
}
|
||||
}
|
||||
|
||||
void nrf52Loop()
|
||||
{
|
||||
checkSDEvents();
|
||||
}
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
auto why = NRF_POWER->RESETREAS;
|
||||
// per
|
||||
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
|
||||
DEBUG_MSG("Reset reason: 0x%x\n", why);
|
||||
|
||||
// Per
|
||||
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
|
||||
// This is the recommended setting for Monitor Mode Debugging
|
||||
NVIC_SetPriority(DebugMonitor_IRQn, 6UL);
|
||||
|
||||
#ifdef BQ25703A_ADDR
|
||||
auto *bq = new BQ25713();
|
||||
if (!bq->setup())
|
||||
DEBUG_MSG("ERROR! Charge controller init failed\n");
|
||||
#endif
|
||||
|
||||
// Init random seed
|
||||
union seedParts {
|
||||
uint32_t seed32;
|
||||
uint8_t seed8[4];
|
||||
} seed;
|
||||
nRFCrypto.begin();
|
||||
nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8));
|
||||
DEBUG_MSG("Setting random seed %u\n", seed.seed32);
|
||||
randomSeed(seed.seed32);
|
||||
nRFCrypto.end();
|
||||
}
|
||||
|
||||
void cpuDeepSleep(uint64_t msecToWake)
|
||||
{
|
||||
// FIXME, configure RTC or button press to wake us
|
||||
// FIXME, power down SPI, I2C, RAMs
|
||||
#if HAS_WIRE
|
||||
Wire.end();
|
||||
#endif
|
||||
SPI.end();
|
||||
// This may cause crashes as debug messages continue to flow.
|
||||
Serial.end();
|
||||
|
||||
#ifdef PIN_SERIAL_RX1
|
||||
Serial1.end();
|
||||
#endif
|
||||
setBluetoothEnable(false);
|
||||
// FIXME, use system off mode with ram retention for key state?
|
||||
// FIXME, use non-init RAM per
|
||||
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
|
||||
|
||||
auto ok = sd_power_system_off();
|
||||
if (ok != NRF_SUCCESS) {
|
||||
DEBUG_MSG("FIXME: Ignoring soft device (EasyDMA pending?) and forcing "
|
||||
"system-off!\n");
|
||||
NRF_POWER->SYSTEMOFF = 1;
|
||||
}
|
||||
|
||||
// The following code should not be run, because we are off
|
||||
while (1) {
|
||||
delay(5000);
|
||||
DEBUG_MSG(".");
|
||||
}
|
||||
}
|
||||
|
||||
void clearBonds() {
|
||||
if (!nrf52Bluetooth) {
|
||||
nrf52Bluetooth = new NRF52Bluetooth();
|
||||
nrf52Bluetooth->setup();
|
||||
}
|
||||
nrf52Bluetooth->clearBonds();
|
||||
}
|
||||
5
src/platform/nrf52/pgmspace.h
Normal file
5
src/platform/nrf52/pgmspace.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
// dummy file to keep old arduino code happy
|
||||
#define PROGMEM
|
||||
#define pgm_read_byte(addr) (*((unsigned const char *)addr))
|
||||
79
src/platform/portduino/CrossPlatformCryptoEngine.cpp
Normal file
79
src/platform/portduino/CrossPlatformCryptoEngine.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "AES.h"
|
||||
#include "CTR.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include "configuration.h"
|
||||
|
||||
/** A platform independent AES engine implemented using Tiny-AES
|
||||
*/
|
||||
class CrossPlatformCryptoEngine : public CryptoEngine
|
||||
{
|
||||
|
||||
CTRCommon *ctr = NULL;
|
||||
|
||||
public:
|
||||
CrossPlatformCryptoEngine() {}
|
||||
|
||||
~CrossPlatformCryptoEngine() {}
|
||||
|
||||
/**
|
||||
* Set the key used for encrypt, decrypt.
|
||||
*
|
||||
* As a special case: If all bytes are zero, we assume _no encryption_ and send all data in cleartext.
|
||||
*
|
||||
* @param numBytes must be 16 (AES128), 32 (AES256) or 0 (no crypt)
|
||||
* @param bytes a _static_ buffer that will remain valid for the life of this crypto instance (i.e. this class will cache the
|
||||
* provided pointer)
|
||||
*/
|
||||
virtual void setKey(const CryptoKey &k) override
|
||||
{
|
||||
CryptoEngine::setKey(k);
|
||||
DEBUG_MSG("Installing AES%d key!\n", key.length * 8);
|
||||
if (ctr) {
|
||||
delete ctr;
|
||||
ctr = NULL;
|
||||
}
|
||||
if (key.length != 0) {
|
||||
if (key.length == 16)
|
||||
ctr = new CTR<AES128>();
|
||||
else
|
||||
ctr = new CTR<AES256>();
|
||||
|
||||
ctr->setKey(key.bytes, key.length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a packet
|
||||
*
|
||||
* @param bytes is updated in place
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
if (key.length > 0) {
|
||||
//uint8_t stream_block[16];
|
||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||
//size_t nc_off = 0;
|
||||
|
||||
// DEBUG_MSG("ESP32 encrypt!\n");
|
||||
initNonce(fromNode, packetId);
|
||||
assert(numBytes <= MAX_BLOCKSIZE);
|
||||
memcpy(scratch, bytes, numBytes);
|
||||
memset(scratch + numBytes, 0,
|
||||
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
|
||||
|
||||
ctr->setIV(nonce, sizeof(nonce));
|
||||
ctr->setCounterSize(4);
|
||||
ctr->encrypt(bytes, scratch, numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
// For CTR, the implementation is the same
|
||||
encrypt(fromNode, packetId, numBytes, bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
CryptoEngine *crypto = new CrossPlatformCryptoEngine();
|
||||
104
src/platform/portduino/PortduinoGlue.cpp
Normal file
104
src/platform/portduino/PortduinoGlue.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "CryptoEngine.h"
|
||||
#include "PortduinoGPIO.h"
|
||||
#include "SPIChip.h"
|
||||
#include "mesh/RF95Interface.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
#include <Utility.h>
|
||||
#include <assert.h>
|
||||
#include <linux/gpio/LinuxGPIOPin.h>
|
||||
|
||||
// FIXME - move setBluetoothEnable into a HALPlatform class
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
// not needed
|
||||
}
|
||||
|
||||
void cpuDeepSleep(uint64_t msecs)
|
||||
{
|
||||
notImplemented("cpuDeepSleep");
|
||||
}
|
||||
|
||||
void updateBatteryLevel(uint8_t level) NOT_IMPLEMENTED("updateBatteryLevel");
|
||||
|
||||
|
||||
/** a simulated pin for busted IRQ hardware
|
||||
* Porduino helper class to do this i2c based polling:
|
||||
*/
|
||||
class PolledIrqPin : public GPIOPin
|
||||
{
|
||||
public:
|
||||
PolledIrqPin() : GPIOPin(LORA_DIO1, "loraIRQ") {}
|
||||
|
||||
/// Read the low level hardware for this pin
|
||||
virtual PinStatus readPinHardware()
|
||||
{
|
||||
if (isrPinStatus < 0)
|
||||
return LOW; // No interrupt handler attached, don't bother polling i2c right now
|
||||
else {
|
||||
extern RadioInterface *rIf; // FIXME, temporary hack until we know if we need to keep this
|
||||
|
||||
assert(rIf);
|
||||
RadioLibInterface *rIf95 = static_cast<RadioLibInterface *>(rIf);
|
||||
bool p = rIf95->isIRQPending();
|
||||
log(SysGPIO, LogDebug, "PolledIrqPin::readPinHardware(%s, %d, %d)", getName(), getPinNum(), p);
|
||||
return p ? HIGH : LOW;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static GPIOPin *loraIrq;
|
||||
|
||||
/** apps run under portduino can optionally define a portduinoSetup() to
|
||||
* use portduino specific init code (such as gpioBind) to setup portduino on their host machine,
|
||||
* before running 'arduino' code.
|
||||
*/
|
||||
void portduinoSetup()
|
||||
{
|
||||
printf("Setting up Meshtastic on Porduino...\n");
|
||||
|
||||
#ifdef PORTDUINO_LINUX_HARDWARE
|
||||
SPI.begin(); // We need to create SPI
|
||||
bool usePineLora = !spiChip->isSimulated();
|
||||
if(usePineLora) {
|
||||
printf("Connecting to PineLora board...\n");
|
||||
|
||||
// FIXME: remove this hack once interrupts are confirmed to work on new pine64 board
|
||||
// loraIrq = new PolledIrqPin();
|
||||
loraIrq = new LinuxGPIOPin(LORA_DIO1, "ch341", "int", "loraIrq"); // or "err"?
|
||||
loraIrq->setSilent();
|
||||
gpioBind(loraIrq);
|
||||
|
||||
// BUSY hw was busted on current board - just use the simulated pin (which will read low)
|
||||
auto busy = new LinuxGPIOPin(SX126X_BUSY, "ch341", "slct", "loraBusy");
|
||||
busy->setSilent();
|
||||
gpioBind(busy);
|
||||
|
||||
gpioBind(new LinuxGPIOPin(SX126X_RESET, "ch341", "ini", "loraReset"));
|
||||
|
||||
auto loraCs = new LinuxGPIOPin(SX126X_CS, "ch341", "cs0", "loraCs");
|
||||
loraCs->setSilent();
|
||||
gpioBind(loraCs);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
{
|
||||
auto fakeBusy = new SimGPIOPin(SX126X_BUSY, "fakeBusy");
|
||||
fakeBusy->writePin(LOW);
|
||||
fakeBusy->setSilent(true);
|
||||
gpioBind(fakeBusy);
|
||||
|
||||
auto cs = new SimGPIOPin(SX126X_CS, "fakeLoraCS");
|
||||
cs->setSilent(true);
|
||||
gpioBind(cs);
|
||||
|
||||
gpioBind(new SimGPIOPin(SX126X_RESET, "fakeLoraReset"));
|
||||
gpioBind(new SimGPIOPin(LORA_DIO1, "fakeLoraIrq"));
|
||||
}
|
||||
|
||||
// gpioBind((new SimGPIOPin(LORA_RESET, "LORA_RESET")));
|
||||
// gpioBind((new SimGPIOPin(RF95_NSS, "RF95_NSS"))->setSilent());
|
||||
}
|
||||
16
src/platform/portduino/architecture.h
Normal file
16
src/platform/portduino/architecture.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#define ARCH_PORTDUINO
|
||||
|
||||
//
|
||||
// defaults for NRF52 architecture
|
||||
//
|
||||
|
||||
//
|
||||
// set HW_VENDOR
|
||||
//
|
||||
|
||||
#define HW_VENDOR HardwareModel_PORTDUINO
|
||||
|
||||
#define HAS_RTC 1
|
||||
#define HAS_WIFI 1
|
||||
7
src/platform/rp2040/architecture.h
Normal file
7
src/platform/rp2040/architecture.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define ARCH_RP2040
|
||||
|
||||
#if defined(PRIVATE_HW)
|
||||
#define HW_VENDOR HardwareModel_PRIVATE_HW
|
||||
#endif
|
||||
30
src/platform/rp2040/main-rp2040.cpp
Normal file
30
src/platform/rp2040/main-rp2040.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "configuration.h"
|
||||
#include <stdio.h>
|
||||
#include <pico/unique_id.h>
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
// not needed
|
||||
}
|
||||
|
||||
void cpuDeepSleep(uint64_t msecs)
|
||||
{
|
||||
// not needed
|
||||
}
|
||||
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
// not needed
|
||||
}
|
||||
|
||||
void getMacAddr(uint8_t *dmac)
|
||||
{
|
||||
pico_unique_board_id_t src;
|
||||
pico_get_unique_board_id(&src);
|
||||
dmac[5] = src.id[0];
|
||||
dmac[4] = src.id[1];
|
||||
dmac[3] = src.id[2];
|
||||
dmac[2] = src.id[3];
|
||||
dmac[1] = src.id[4];
|
||||
dmac[0] = src.id[5];
|
||||
}
|
||||
36
src/platform/rp2040/rp2040CryptoEngine.cpp
Normal file
36
src/platform/rp2040/rp2040CryptoEngine.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "configuration.h"
|
||||
#include "CryptoEngine.h"
|
||||
#include "aes.hpp"
|
||||
|
||||
class RP2040CryptoEngine : public CryptoEngine
|
||||
{
|
||||
public:
|
||||
RP2040CryptoEngine() {}
|
||||
|
||||
~RP2040CryptoEngine() {}
|
||||
|
||||
/**
|
||||
* Encrypt a packet
|
||||
*
|
||||
* @param bytes is updated in place
|
||||
*/
|
||||
virtual void encrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
if (key.length > 0) {
|
||||
AES_ctx ctx;
|
||||
initNonce(fromNode, packetNum);
|
||||
AES_init_ctx_iv(&ctx, key.bytes, nonce);
|
||||
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void decrypt(uint32_t fromNode, uint64_t packetNum, size_t numBytes, uint8_t *bytes) override
|
||||
{
|
||||
// For CTR, the implementation is the same
|
||||
encrypt(fromNode, packetNum, numBytes, bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
CryptoEngine *crypto = new RP2040CryptoEngine();
|
||||
Reference in New Issue
Block a user