Revert "add a .clang-format file (#9154)" (#9172)

I thought git would be smart enough to understand all the whitespace changes but even with all the flags I know to make it ignore theses it still blows up if there are identical changes on both sides.

I have a solution but it require creating a new commit at the merge base for each conflicting PR and merging it into develop.

I don't think blowing up all PRs is worth for now, maybe if we can coordinate this for V3 let's say.

This reverts commit 0d11331d18.
This commit is contained in:
Jorropo
2026-01-04 05:15:53 -06:00
committed by GitHub
co-authored by GitHub
parent 0d11331d18
commit beb268ff25
771 changed files with 83399 additions and 77967 deletions
+32 -29
View File
@@ -4,40 +4,43 @@
static const String MESHTASTIC_OTA_APP_PROJECT_NAME("Meshtastic-OTA");
const esp_partition_t *BleOta::findEspOtaAppPartition() {
const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, nullptr);
const esp_partition_t *BleOta::findEspOtaAppPartition()
{
const esp_partition_t *part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, nullptr);
esp_app_desc_t app_desc;
esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
esp_app_desc_t app_desc;
esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
if (ret != ESP_OK || MESHTASTIC_OTA_APP_PROJECT_NAME != app_desc.project_name) {
part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, nullptr);
ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
}
if (ret != ESP_OK || MESHTASTIC_OTA_APP_PROJECT_NAME != app_desc.project_name) {
part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, nullptr);
ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
}
if (ret == ESP_OK && MESHTASTIC_OTA_APP_PROJECT_NAME == app_desc.project_name) {
return part;
} else {
return nullptr;
}
if (ret == ESP_OK && MESHTASTIC_OTA_APP_PROJECT_NAME == app_desc.project_name) {
return part;
} else {
return nullptr;
}
}
String BleOta::getOtaAppVersion() {
const esp_partition_t *part = findEspOtaAppPartition();
esp_app_desc_t app_desc;
esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
String version;
if (ret == ESP_OK) {
version = app_desc.version;
}
return version;
String BleOta::getOtaAppVersion()
{
const esp_partition_t *part = findEspOtaAppPartition();
esp_app_desc_t app_desc;
esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_get_partition_description(part, &app_desc));
String version;
if (ret == ESP_OK) {
version = app_desc.version;
}
return version;
}
bool BleOta::switchToOtaApp() {
bool success = false;
const esp_partition_t *part = findEspOtaAppPartition();
if (part) {
success = (ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_set_boot_partition(part)) == ESP_OK);
}
return success;
bool BleOta::switchToOtaApp()
{
bool success = false;
const esp_partition_t *part = findEspOtaAppPartition();
if (part) {
success = (ESP_ERROR_CHECK_WITHOUT_ABORT(esp_ota_set_boot_partition(part)) == ESP_OK);
}
return success;
}
+9 -8
View File
@@ -4,16 +4,17 @@
#include <Arduino.h>
#include <functional>
class BleOta {
public:
explicit BleOta(){};
class BleOta
{
public:
explicit BleOta(){};
static String getOtaAppVersion();
static bool switchToOtaApp();
static String getOtaAppVersion();
static bool switchToOtaApp();
private:
String mUserAgent;
static const esp_partition_t *findEspOtaAppPartition();
private:
String mUserAgent;
static const esp_partition_t *findEspOtaAppPartition();
};
#endif // BLEOTA_H
+28 -26
View File
@@ -3,37 +3,39 @@
#include "mbedtls/aes.h"
class ESP32CryptoEngine : public CryptoEngine {
class ESP32CryptoEngine : public CryptoEngine
{
mbedtls_aes_context aes;
mbedtls_aes_context aes;
public:
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
public:
ESP32CryptoEngine() { mbedtls_aes_init(&aes); }
~ESP32CryptoEngine() { mbedtls_aes_free(&aes); }
~ESP32CryptoEngine() { mbedtls_aes_free(&aes); }
/**
* Encrypt a packet
*
* @param bytes is updated in place
* TODO: return bool, and handle graciously when something fails
*/
virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override {
if (_key.length > 0) {
if (numBytes <= MAX_BLOCKSIZE) {
mbedtls_aes_setkey_enc(&aes, _key.bytes, _key.length * 8);
static uint8_t scratch[MAX_BLOCKSIZE];
uint8_t stream_block[16];
size_t nc_off = 0;
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!", numBytes);
}
/**
* Encrypt a packet
*
* @param bytes is updated in place
* TODO: return bool, and handle graciously when something fails
*/
virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override
{
if (_key.length > 0) {
if (numBytes <= MAX_BLOCKSIZE) {
mbedtls_aes_setkey_enc(&aes, _key.bytes, _key.length * 8);
static uint8_t scratch[MAX_BLOCKSIZE];
uint8_t stream_block[16];
size_t nc_off = 0;
memcpy(scratch, bytes, numBytes);
memset(scratch + numBytes, 0,
sizeof(scratch) - numBytes); // Fill rest of buffer with zero (in case cypher looks at it)
mbedtls_aes_crypt_ctr(&aes, numBytes, &nc_off, _nonce, stream_block, scratch, bytes);
} else {
LOG_ERROR("Packet too large for crypto engine: %d. noop encryption!", numBytes);
}
}
}
}
};
CryptoEngine *crypto = new ESP32CryptoEngine();
+21 -11
View File
@@ -2,17 +2,27 @@
#include "assert.h"
#include "configuration.h"
SimpleAllocator::SimpleAllocator() { reset(); }
void *SimpleAllocator::alloc(size_t size) {
assert(nextFree + size <= sizeof(bytes));
void *res = &bytes[nextFree];
nextFree += size;
LOG_DEBUG("Total simple allocs %u", nextFree);
return res;
SimpleAllocator::SimpleAllocator()
{
reset();
}
void SimpleAllocator::reset() { nextFree = 0; }
void *SimpleAllocator::alloc(size_t size)
{
assert(nextFree + size <= sizeof(bytes));
void *res = &bytes[nextFree];
nextFree += size;
LOG_DEBUG("Total simple allocs %u", nextFree);
void *operator new(size_t size, SimpleAllocator &p) { return p.alloc(size); }
return res;
}
void SimpleAllocator::reset()
{
nextFree = 0;
}
void *operator new(size_t size, SimpleAllocator &p)
{
return p.alloc(size);
}
+16 -14
View File
@@ -12,20 +12,21 @@
* 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] = {};
class SimpleAllocator
{
uint8_t bytes[POOL_SIZE] = {};
uint32_t nextFree = 0;
uint32_t nextFree = 0;
public:
SimpleAllocator();
public:
SimpleAllocator();
void *alloc(size_t size);
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();
/** 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);
@@ -34,8 +35,9 @@ 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();
class AllocatorScope
{
public:
explicit AllocatorScope(SimpleAllocator &a);
~AllocatorScope();
};
+68 -55
View File
@@ -3,77 +3,90 @@
#include <Preferences.h>
#include <esp_ota_ops.h>
namespace WiFiOTA {
namespace WiFiOTA
{
static const char *nvsNamespace = "ota-wifi";
static const char *appProjectName = "OTA-WiFi";
static bool updated = false;
bool isUpdated() { return updated; }
bool isUpdated()
{
return updated;
}
void initialize() {
Preferences prefs;
prefs.begin(nvsNamespace);
if (prefs.getBool("updated")) {
LOG_INFO("First boot after OTA update");
updated = true;
void initialize()
{
Preferences prefs;
prefs.begin(nvsNamespace);
if (prefs.getBool("updated")) {
LOG_INFO("First boot after OTA update");
updated = true;
prefs.putBool("updated", false);
}
prefs.end();
}
void recoverConfig(meshtastic_Config_NetworkConfig *network)
{
LOG_INFO("Recovering WiFi settings after OTA update");
Preferences prefs;
prefs.begin(nvsNamespace, true);
String ssid = prefs.getString("ssid");
String psk = prefs.getString("psk");
prefs.end();
network->wifi_enabled = true;
strncpy(network->wifi_ssid, ssid.c_str(), sizeof(network->wifi_ssid));
strncpy(network->wifi_psk, psk.c_str(), sizeof(network->wifi_psk));
}
void saveConfig(meshtastic_Config_NetworkConfig *network)
{
LOG_INFO("Saving WiFi settings for upcoming OTA update");
Preferences prefs;
prefs.begin(nvsNamespace);
prefs.putString("ssid", network->wifi_ssid);
prefs.putString("psk", network->wifi_psk);
prefs.putBool("updated", false);
}
prefs.end();
prefs.end();
}
void recoverConfig(meshtastic_Config_NetworkConfig *network) {
LOG_INFO("Recovering WiFi settings after OTA update");
Preferences prefs;
prefs.begin(nvsNamespace, true);
String ssid = prefs.getString("ssid");
String psk = prefs.getString("psk");
prefs.end();
network->wifi_enabled = true;
strncpy(network->wifi_ssid, ssid.c_str(), sizeof(network->wifi_ssid));
strncpy(network->wifi_psk, psk.c_str(), sizeof(network->wifi_psk));
const esp_partition_t *getAppPartition()
{
return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL);
}
void saveConfig(meshtastic_Config_NetworkConfig *network) {
LOG_INFO("Saving WiFi settings for upcoming OTA update");
Preferences prefs;
prefs.begin(nvsNamespace);
prefs.putString("ssid", network->wifi_ssid);
prefs.putString("psk", network->wifi_psk);
prefs.putBool("updated", false);
prefs.end();
bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc)
{
if (esp_ota_get_partition_description(part, app_desc) != ESP_OK)
return false;
if (strcmp(app_desc->project_name, appProjectName) != 0)
return false;
return true;
}
const esp_partition_t *getAppPartition() { return esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL); }
bool getAppDesc(const esp_partition_t *part, esp_app_desc_t *app_desc) {
if (esp_ota_get_partition_description(part, app_desc) != ESP_OK)
return false;
if (strcmp(app_desc->project_name, appProjectName) != 0)
return false;
return true;
bool trySwitchToOTA()
{
const esp_partition_t *part = getAppPartition();
esp_app_desc_t app_desc;
if (!getAppDesc(part, &app_desc))
return false;
if (esp_ota_set_boot_partition(part) != ESP_OK)
return false;
return true;
}
bool trySwitchToOTA() {
const esp_partition_t *part = getAppPartition();
esp_app_desc_t app_desc;
if (!getAppDesc(part, &app_desc))
return false;
if (esp_ota_set_boot_partition(part) != ESP_OK)
return false;
return true;
}
const char *getVersion() {
const esp_partition_t *part = getAppPartition();
static esp_app_desc_t app_desc;
if (!getAppDesc(part, &app_desc))
return "";
return app_desc.version;
const char *getVersion()
{
const esp_partition_t *part = getAppPartition();
static esp_app_desc_t app_desc;
if (!getAppDesc(part, &app_desc))
return "";
return app_desc.version;
}
} // namespace WiFiOTA
+2 -1
View File
@@ -4,7 +4,8 @@
#include "mesh-pb-constants.h"
#include <Arduino.h>
namespace WiFiOTA {
namespace WiFiOTA
{
void initialize();
bool isUpdated();
+2 -2
View File
@@ -212,8 +212,8 @@
// -----------------------------------------------------------------------------
// If an SPI-related pin used by the LoRa module isn't defined, use the conventional pin number for it.
// FIXME: these pins should really be defined in each variant.h file to prevent breakages if the defaults change,
// currently many ESP32 variants don't define these pins in their variant.h file.
// FIXME: these pins should really be defined in each variant.h file to prevent breakages if the defaults change, currently many
// ESP32 variants don't define these pins in their variant.h file.
#ifndef LORA_SCK
#define LORA_SCK 5
#endif
+4 -1
View File
@@ -8,7 +8,10 @@
#define IRAM_SECTION section(".iram1.stub")
IRAM_ATTR esp_err_t stub_probe(esp_flash_t *chip, uint32_t flash_id) { return ESP_ERR_NOT_FOUND; }
IRAM_ATTR esp_err_t stub_probe(esp_flash_t *chip, uint32_t flash_id)
{
return ESP_ERR_NOT_FOUND;
}
const spi_flash_chip_t stub_flash_chip __attribute__((IRAM_SECTION)) = {
.name = "stub",
+149 -142
View File
@@ -26,138 +26,143 @@
#include <nvs_flash.h>
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable) {
void setBluetoothEnable(bool enable)
{
#ifdef USE_WS5500
if ((config.bluetooth.enabled == true) && (config.network.wifi_enabled == false))
if ((config.bluetooth.enabled == true) && (config.network.wifi_enabled == false))
#elif HAS_WIFI
if (!isWifiAvailable() && config.bluetooth.enabled == true)
if (!isWifiAvailable() && config.bluetooth.enabled == true)
#else
if (config.bluetooth.enabled == true)
if (config.bluetooth.enabled == true)
#endif
{
if (!nimbleBluetooth) {
nimbleBluetooth = new NimbleBluetooth();
{
if (!nimbleBluetooth) {
nimbleBluetooth = new NimbleBluetooth();
}
if (enable && !nimbleBluetooth->isActive()) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
nimbleBluetooth->setup();
}
// For ESP32, no way to recover from bluetooth shutdown without reboot
// BLE advertising automatically stops when MCU enters light-sleep(?)
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
}
if (enable && !nimbleBluetooth->isActive()) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
nimbleBluetooth->setup();
}
// For ESP32, no way to recover from bluetooth shutdown without reboot
// BLE advertising automatically stops when MCU enters light-sleep(?)
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
}
}
#else
void setBluetoothEnable(bool enable) {}
void updateBatteryLevel(uint8_t level) {}
#endif
void getMacAddr(uint8_t *dmac) {
void getMacAddr(uint8_t *dmac)
{
#if defined(CONFIG_IDF_TARGET_ESP32C6) && defined(CONFIG_SOC_IEEE802154_SUPPORTED)
auto res = esp_base_mac_addr_get(dmac);
assert(res == ESP_OK);
auto res = esp_base_mac_addr_get(dmac);
assert(res == ESP_OK);
#else
auto res = esp_efuse_mac_get_default(dmac);
assert(res == ESP_OK);
auto res = esp_efuse_mac_get_default(dmac);
assert(res == ESP_OK);
#endif
}
#if HAS_32768HZ
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
static uint32_t calibrate_one(rtc_cal_sel_t cal_clk, const char *name) {
const uint32_t cal_count = 1000;
// const float factor = (1 << 19) * 1000.0f; unused var?
uint32_t cali_val;
for (int i = 0; i < 5; ++i) {
cali_val = rtc_clk_cal(cal_clk, cal_count);
}
return cali_val;
static uint32_t calibrate_one(rtc_cal_sel_t cal_clk, const char *name)
{
const uint32_t cal_count = 1000;
// const float factor = (1 << 19) * 1000.0f; unused var?
uint32_t cali_val;
for (int i = 0; i < 5; ++i) {
cali_val = rtc_clk_cal(cal_clk, cal_count);
}
return cali_val;
}
void enableSlowCLK() {
rtc_clk_32k_enable(true);
void enableSlowCLK()
{
rtc_clk_32k_enable(true);
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
uint32_t cal_32k = CALIBRATE_ONE(RTC_CAL_32K_XTAL);
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
uint32_t cal_32k = CALIBRATE_ONE(RTC_CAL_32K_XTAL);
if (cal_32k == 0) {
LOG_DEBUG("32k XTAL OSC has not started up");
} else {
rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
LOG_DEBUG("Switch RTC Source to 32.768kHz succeeded, using 32k XTAL");
if (cal_32k == 0) {
LOG_DEBUG("32k XTAL OSC has not started up");
} else {
rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
LOG_DEBUG("Switch RTC Source to 32.768kHz succeeded, using 32k XTAL");
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
}
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
}
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
LOG_WARN("Failed to switch 32K XTAL RTC source to 32.768kHz !!! ");
return;
}
if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
LOG_WARN("Failed to switch 32K XTAL RTC source to 32.768kHz !!! ");
return;
}
}
#endif
void esp32Setup() {
/* We explicitly don't want to do call randomSeed,
// as that triggers the esp32 core to use a less secure pseudorandom function.
uint32_t seed = esp_random();
LOG_DEBUG("Set random seed %u", seed);
randomSeed(seed);
*/
void esp32Setup()
{
/* We explicitly don't want to do call randomSeed,
// as that triggers the esp32 core to use a less secure pseudorandom function.
uint32_t seed = esp_random();
LOG_DEBUG("Set random seed %u", seed);
randomSeed(seed);
*/
#ifdef ADC_V
pinMode(ADC_V, INPUT);
pinMode(ADC_V, INPUT);
#endif
LOG_DEBUG("Total heap: %d", ESP.getHeapSize());
LOG_DEBUG("Free heap: %d", ESP.getFreeHeap());
LOG_DEBUG("Total PSRAM: %d", ESP.getPsramSize());
LOG_DEBUG("Free PSRAM: %d", ESP.getFreePsram());
LOG_DEBUG("Total heap: %d", ESP.getHeapSize());
LOG_DEBUG("Free heap: %d", ESP.getFreeHeap());
LOG_DEBUG("Total PSRAM: %d", ESP.getPsramSize());
LOG_DEBUG("Free PSRAM: %d", ESP.getFreePsram());
nvs_stats_t nvs_stats;
auto res = nvs_get_stats(NULL, &nvs_stats);
assert(res == ESP_OK);
LOG_DEBUG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d, NameSpaces %d", nvs_stats.used_entries, nvs_stats.free_entries,
nvs_stats.total_entries, nvs_stats.namespace_count);
nvs_stats_t nvs_stats;
auto res = nvs_get_stats(NULL, &nvs_stats);
assert(res == ESP_OK);
LOG_DEBUG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d, NameSpaces %d", nvs_stats.used_entries, nvs_stats.free_entries,
nvs_stats.total_entries, nvs_stats.namespace_count);
LOG_DEBUG("Setup Preferences in Flash Storage");
LOG_DEBUG("Setup Preferences in Flash Storage");
// Create object to store our persistent data
Preferences preferences;
preferences.begin("meshtastic", false);
// Create object to store our persistent data
Preferences preferences;
preferences.begin("meshtastic", false);
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
rebootCounter++;
preferences.putUInt("rebootCounter", rebootCounter);
// store firmware version and hwrevision for access from OTA firmware
String fwrev = preferences.getString("firmwareVersion", "");
if (fwrev.compareTo(optstr(APP_VERSION)) != 0)
preferences.putString("firmwareVersion", optstr(APP_VERSION));
uint8_t hwven = preferences.getUInt("hwVendor", 0);
if (hwven != HW_VENDOR)
preferences.putUInt("hwVendor", HW_VENDOR);
preferences.end();
LOG_DEBUG("Number of Device Reboots: %d", rebootCounter);
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
rebootCounter++;
preferences.putUInt("rebootCounter", rebootCounter);
// store firmware version and hwrevision for access from OTA firmware
String fwrev = preferences.getString("firmwareVersion", "");
if (fwrev.compareTo(optstr(APP_VERSION)) != 0)
preferences.putString("firmwareVersion", optstr(APP_VERSION));
uint8_t hwven = preferences.getUInt("hwVendor", 0);
if (hwven != HW_VENDOR)
preferences.putUInt("hwVendor", HW_VENDOR);
preferences.end();
LOG_DEBUG("Number of Device Reboots: %d", rebootCounter);
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
String BLEOTA = BleOta::getOtaAppVersion();
if (BLEOTA.isEmpty()) {
LOG_INFO("No BLE OTA firmware available");
} else {
LOG_INFO("BLE OTA firmware version %s", BLEOTA.c_str());
}
String BLEOTA = BleOta::getOtaAppVersion();
if (BLEOTA.isEmpty()) {
LOG_INFO("No BLE OTA firmware available");
} else {
LOG_INFO("BLE OTA firmware version %s", BLEOTA.c_str());
}
#endif
#if !MESHTASTIC_EXCLUDE_WIFI
String version = WiFiOTA::getVersion();
if (version.isEmpty()) {
LOG_INFO("No WiFi OTA firmware available");
} else {
LOG_INFO("WiFi OTA firmware version %s", version.c_str());
}
WiFiOTA::initialize();
String version = WiFiOTA::getVersion();
if (version.isEmpty()) {
LOG_INFO("No WiFi OTA firmware available");
} else {
LOG_INFO("WiFi OTA firmware version %s", version.c_str());
}
WiFiOTA::initialize();
#endif
// enableModemSleep();
// 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.
@@ -165,96 +170,98 @@ void esp32Setup() {
#define APP_WATCHDOG_SECS 90
#ifdef CONFIG_IDF_TARGET_ESP32C6
esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t));
wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000;
wdt_config->trigger_panic = true;
res = esp_task_wdt_init(wdt_config);
assert(res == ESP_OK);
esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t));
wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000;
wdt_config->trigger_panic = true;
res = esp_task_wdt_init(wdt_config);
assert(res == ESP_OK);
#else
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
assert(res == ESP_OK);
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
assert(res == ESP_OK);
#endif
res = esp_task_wdt_add(NULL);
assert(res == ESP_OK);
res = esp_task_wdt_add(NULL);
assert(res == ESP_OK);
#if HAS_32768HZ
enableSlowCLK();
enableSlowCLK();
#endif
}
/// loop code specific to ESP32 targets
void esp32Loop() {
esp_task_wdt_reset(); // service our app level watchdog
void esp32Loop()
{
esp_task_wdt_reset(); // service our app level watchdog
// for debug printing
// radio.radioIf.canSleep();
// for debug printing
// radio.radioIf.canSleep();
}
void cpuDeepSleep(uint32_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.
void cpuDeepSleep(uint32_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.
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, or ST7735 Display for the Chatter2, spi or the wake
button(s), maybe we should not include any other GPIOs...
*/
Note: we don't isolate pins that are used for the LORA, LED, i2c, or ST7735 Display for the Chatter2, spi or the wake
button(s), maybe we should not include any other GPIOs...
*/
#if SOC_RTCIO_HOLD_SUPPORTED
static const uint8_t rtcGpios[] = {
static const uint8_t rtcGpios[] = {
#ifndef HELTEC_VISION_MASTER_E213
// For this variant, >20mA leaks through the display if pin 2 held
// Todo: check if it's safe to remove this pin for all variants
2,
// For this variant, >20mA leaks through the display if pin 2 held
// Todo: check if it's safe to remove this pin for all variants
2,
#endif
#ifndef USE_JTAG
13,
13,
#endif
34, 35, 37};
34, 35, 37};
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
#endif
// 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.
// 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.
#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.
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
#if SOC_RTCIO_HOLD_SUPPORTED && SOC_PM_SUPPORT_EXT_WAKEUP
uint64_t gpioMask = (1ULL << (config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN));
uint64_t gpioMask = (1ULL << (config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN));
#endif
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
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);
// 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);
#ifdef ESP32S3_WAKE_TYPE
esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE);
#else
#if SOC_PM_SUPPORT_EXT_WAKEUP
#ifdef CONFIG_IDF_TARGET_ESP32
// ESP_EXT1_WAKEUP_ALL_LOW has been deprecated since esp-idf v5.4 for any other target.
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
// ESP_EXT1_WAKEUP_ALL_LOW has been deprecated since esp-idf v5.4 for any other target.
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
#else
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_LOW);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_LOW);
#endif
#endif
#endif // #end ESP32S3_WAKE_TYPE
#endif
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
}
@@ -7,33 +7,34 @@
#include "graphics/TFTDisplay.h"
// Heltec tracker specific init
void lateInitVariant() {
// LOG_DEBUG("Heltec tracker initVariant");
void lateInitVariant()
{
// LOG_DEBUG("Heltec tracker initVariant");
#ifndef MESHTASTIC_EXCLUDE_GPS
GpioVirtPin *virtGpsEnable = gps ? gps->enablePin : new GpioVirtPin();
GpioVirtPin *virtGpsEnable = gps ? gps->enablePin : new GpioVirtPin();
#else
GpioVirtPin *virtGpsEnable = new GpioVirtPin();
GpioVirtPin *virtGpsEnable = new GpioVirtPin();
#endif
#ifndef MESHTASTIC_EXCLUDE_SCREEN
// On this board we are actually using the backlightEnable signal to already be controlling a physical enable to the
// display controller. But we'd _ALSO_ like to have that signal drive a virtual GPIO. So nest it as needed.
GpioVirtPin *virtScreenEnable = new GpioVirtPin();
if (TFTDisplay::backlightEnable) {
GpioPin *physScreenEnable = TFTDisplay::backlightEnable;
GpioPin *splitter = new GpioSplitter(virtScreenEnable, physScreenEnable);
TFTDisplay::backlightEnable = splitter;
// On this board we are actually using the backlightEnable signal to already be controlling a physical enable to the
// display controller. But we'd _ALSO_ like to have that signal drive a virtual GPIO. So nest it as needed.
GpioVirtPin *virtScreenEnable = new GpioVirtPin();
if (TFTDisplay::backlightEnable) {
GpioPin *physScreenEnable = TFTDisplay::backlightEnable;
GpioPin *splitter = new GpioSplitter(virtScreenEnable, physScreenEnable);
TFTDisplay::backlightEnable = splitter;
// Assume screen is initially powered
splitter->set(true);
}
// Assume screen is initially powered
splitter->set(true);
}
#endif
#if defined(VEXT_ENABLE) && (!defined(MESHTASTIC_EXCLUDE_GPS) || !defined(MESHTASTIC_EXCLUDE_SCREEN))
// If either the GPS or the screen is on, turn on the external power regulator
GpioPin *hwEnable = new GpioHwPin(VEXT_ENABLE);
new GpioBinaryTransformer(virtGpsEnable, virtScreenEnable, hwEnable, GpioBinaryTransformer::Or);
// If either the GPS or the screen is on, turn on the external power regulator
GpioPin *hwEnable = new GpioHwPin(VEXT_ENABLE);
new GpioBinaryTransformer(virtGpsEnable, virtScreenEnable, hwEnable, GpioBinaryTransformer::Or);
#endif
}
@@ -8,19 +8,21 @@
CSE_CST328 tsPanel = CSE_CST328(EINK_WIDTH, EINK_HEIGHT, &Wire, CST328_PIN_RST, CST328_PIN_INT);
bool readTouch(int16_t *x, int16_t *y) {
if (tsPanel.getTouches()) {
*x = tsPanel.getPoint(0).x;
*y = tsPanel.getPoint(0).y;
return true;
}
return false;
bool readTouch(int16_t *x, int16_t *y)
{
if (tsPanel.getTouches()) {
*x = tsPanel.getPoint(0).x;
*y = tsPanel.getPoint(0).y;
return true;
}
return false;
}
// T-Deck Pro specific init
void lateInitVariant() {
tsPanel.begin();
touchScreenImpl1 = new TouchScreenImpl1(EINK_WIDTH, EINK_HEIGHT, readTouch);
touchScreenImpl1->init();
void lateInitVariant()
{
tsPanel.begin();
touchScreenImpl1 = new TouchScreenImpl1(EINK_WIDTH, EINK_HEIGHT, readTouch);
touchScreenImpl1->init();
}
#endif
@@ -8,19 +8,20 @@ DriverPins PinsAudioBoardES8311;
AudioBoard board(AudioDriverES8311, PinsAudioBoardES8311);
// TLora Pager specific init
void lateInitVariant() {
// AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug);
// I2C: function, scl, sda
PinsAudioBoardES8311.addI2C(PinFunction::CODEC, Wire);
// I2S: function, mclk, bck, ws, data_out, data_in
PinsAudioBoardES8311.addI2S(PinFunction::CODEC, DAC_I2S_MCLK, DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_DIN);
void lateInitVariant()
{
// AudioDriverLogger.begin(Serial, AudioDriverLogLevel::Debug);
// I2C: function, scl, sda
PinsAudioBoardES8311.addI2C(PinFunction::CODEC, Wire);
// I2S: function, mclk, bck, ws, data_out, data_in
PinsAudioBoardES8311.addI2S(PinFunction::CODEC, DAC_I2S_MCLK, DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT, DAC_I2S_DIN);
// configure codec
CodecConfig cfg;
cfg.input_device = ADC_INPUT_LINE1;
cfg.output_device = DAC_OUTPUT_ALL;
cfg.i2s.bits = BIT_LENGTH_16BITS;
cfg.i2s.rate = RATE_44K;
board.begin(cfg);
// configure codec
CodecConfig cfg;
cfg.input_device = ADC_INPUT_LINE1;
cfg.output_device = DAC_OUTPUT_ALL;
cfg.i2s.bits = BIT_LENGTH_16BITS;
cfg.i2s.rate = RATE_44K;
board.begin(cfg);
}
#endif
@@ -10,32 +10,34 @@ TouchDrvCSTXXX tsPanel;
static constexpr uint8_t PossibleAddresses[2] = {CST328_ADDR, CST226SE_ADDR_ALT};
uint8_t i2cAddress = 0;
bool readTouch(int16_t *x, int16_t *y) {
int16_t x_array[1], y_array[1];
uint8_t touched = tsPanel.getPoint(x_array, y_array, 1);
if (touched > 0) {
*y = x_array[0];
*x = (TFT_WIDTH - y_array[0]);
// Check bounds
if (*x < 0 || *x >= TFT_WIDTH || *y < 0 || *y >= TFT_HEIGHT) {
return false;
bool readTouch(int16_t *x, int16_t *y)
{
int16_t x_array[1], y_array[1];
uint8_t touched = tsPanel.getPoint(x_array, y_array, 1);
if (touched > 0) {
*y = x_array[0];
*x = (TFT_WIDTH - y_array[0]);
// Check bounds
if (*x < 0 || *x >= TFT_WIDTH || *y < 0 || *y >= TFT_HEIGHT) {
return false;
}
return true; // Valid touch detected
}
return true; // Valid touch detected
}
return false; // No valid touch data
return false; // No valid touch data
}
void lateInitVariant() {
tsPanel.setTouchDrvModel(TouchDrv_CST226);
for (uint8_t addr : PossibleAddresses) {
if (tsPanel.begin(Wire, addr, I2C_SDA, I2C_SCL)) {
i2cAddress = addr;
LOG_DEBUG("CST226SE init OK at address 0x%02X", addr);
touchScreenImpl1 = new TouchScreenImpl1(TFT_WIDTH, TFT_HEIGHT, readTouch);
touchScreenImpl1->init();
return;
void lateInitVariant()
{
tsPanel.setTouchDrvModel(TouchDrv_CST226);
for (uint8_t addr : PossibleAddresses) {
if (tsPanel.begin(Wire, addr, I2C_SDA, I2C_SCL)) {
i2cAddress = addr;
LOG_DEBUG("CST226SE init OK at address 0x%02X", addr);
touchScreenImpl1 = new TouchScreenImpl1(TFT_WIDTH, TFT_HEIGHT, readTouch);
touchScreenImpl1->init();
return;
}
}
}
LOG_ERROR("CST226SE init failed at all known addresses");
LOG_ERROR("CST226SE init failed at all known addresses");
}
#endif
+51 -29
View File
@@ -4,48 +4,70 @@
AsyncUDP::AsyncUDP() : OSThread("AsyncUDP"), localPort(0) {}
bool AsyncUDP::listenMulticast(IPAddress multicastIP, uint16_t port, uint8_t ttl) {
if (!isMulticast(multicastIP))
return false;
localPort = port;
udp.beginMulticast(multicastIP, port);
return true;
bool AsyncUDP::listenMulticast(IPAddress multicastIP, uint16_t port, uint8_t ttl)
{
if (!isMulticast(multicastIP))
return false;
localPort = port;
udp.beginMulticast(multicastIP, port);
return true;
}
size_t AsyncUDP::write(uint8_t b) { return udp.write(&b, 1); }
size_t AsyncUDP::write(uint8_t b)
{
return udp.write(&b, 1);
}
size_t AsyncUDP::write(const uint8_t *data, size_t len) { return udp.write(data, len); }
size_t AsyncUDP::write(const uint8_t *data, size_t len)
{
return udp.write(data, len);
}
void AsyncUDP::onPacket(const std::function<void(AsyncUDPPacket)> &callback) { _onPacket = callback; }
void AsyncUDP::onPacket(const std::function<void(AsyncUDPPacket)> &callback)
{
_onPacket = callback;
}
bool AsyncUDP::writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t port) {
if (!udp.beginPacket(ip, port))
return false;
udp.write(data, len);
return udp.endPacket();
bool AsyncUDP::writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t port)
{
if (!udp.beginPacket(ip, port))
return false;
udp.write(data, len);
return udp.endPacket();
}
// AsyncUDPPacket
AsyncUDPPacket::AsyncUDPPacket(EthernetUDP &source) : _udp(source), _remoteIP(source.remoteIP()), _remotePort(source.remotePort()) {
if (_udp.available() > 0) {
_readLength = _udp.read(_buffer, sizeof(_buffer));
} else {
_readLength = 0;
}
AsyncUDPPacket::AsyncUDPPacket(EthernetUDP &source) : _udp(source), _remoteIP(source.remoteIP()), _remotePort(source.remotePort())
{
if (_udp.available() > 0) {
_readLength = _udp.read(_buffer, sizeof(_buffer));
} else {
_readLength = 0;
}
}
IPAddress AsyncUDPPacket::remoteIP() { return _remoteIP; }
IPAddress AsyncUDPPacket::remoteIP()
{
return _remoteIP;
}
uint16_t AsyncUDPPacket::length() { return _readLength; }
uint16_t AsyncUDPPacket::length()
{
return _readLength;
}
const uint8_t *AsyncUDPPacket::data() { return _buffer; }
const uint8_t *AsyncUDPPacket::data()
{
return _buffer;
}
int32_t AsyncUDP::runOnce() {
if (_onPacket && udp.parsePacket() > 0) {
AsyncUDPPacket packet(udp);
_onPacket(packet);
}
return 5; // check every 5ms
int32_t AsyncUDP::runOnce()
{
if (_onPacket && udp.parsePacket() > 0) {
AsyncUDPPacket packet(udp);
_onPacket(packet);
}
return 5; // check every 5ms
}
#endif // HAS_ETHERNET
+33 -28
View File
@@ -14,44 +14,49 @@
class AsyncUDPPacket;
class AsyncUDP : public Print, private concurrency::OSThread {
public:
AsyncUDP();
explicit operator bool() const { return localPort != 0; }
class AsyncUDP : public Print, private concurrency::OSThread
{
public:
AsyncUDP();
explicit operator bool() const { return localPort != 0; }
bool listenMulticast(IPAddress multicastIP, uint16_t port, uint8_t ttl = 64);
bool writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t port);
bool listenMulticast(IPAddress multicastIP, uint16_t port, uint8_t ttl = 64);
bool writeTo(const uint8_t *data, size_t len, IPAddress ip, uint16_t port);
size_t write(uint8_t b) override;
size_t write(const uint8_t *data, size_t len) override;
void onPacket(const std::function<void(AsyncUDPPacket)> &callback);
size_t write(uint8_t b) override;
size_t write(const uint8_t *data, size_t len) override;
void onPacket(const std::function<void(AsyncUDPPacket)> &callback);
private:
EthernetUDP udp;
uint16_t localPort;
std::function<void(AsyncUDPPacket)> _onPacket;
virtual int32_t runOnce() override;
private:
EthernetUDP udp;
uint16_t localPort;
std::function<void(AsyncUDPPacket)> _onPacket;
virtual int32_t runOnce() override;
};
class AsyncUDPPacket {
public:
AsyncUDPPacket(EthernetUDP &source);
class AsyncUDPPacket
{
public:
AsyncUDPPacket(EthernetUDP &source);
IPAddress remoteIP();
uint16_t length();
const uint8_t *data();
IPAddress remoteIP();
uint16_t length();
const uint8_t *data();
private:
EthernetUDP &_udp;
IPAddress _remoteIP;
uint16_t _remotePort;
size_t _readLength = 0;
private:
EthernetUDP &_udp;
IPAddress _remoteIP;
uint16_t _remotePort;
size_t _readLength = 0;
static constexpr size_t BUF_SIZE = 512;
uint8_t _buffer[BUF_SIZE];
static constexpr size_t BUF_SIZE = 512;
uint8_t _buffer[BUF_SIZE];
};
inline bool isMulticast(const IPAddress &ip) { return (ip[0] & 0xF0) == 0xE0; }
inline bool isMulticast(const IPAddress &ip)
{
return (ip[0] & 0xF0) == 0xE0;
}
#endif // HAS_ETHERNET
+80 -76
View File
@@ -41,99 +41,103 @@
const uint16_t UUID16_SVC_DFU_OTA = 0xFE59;
const uint8_t UUID128_CHR_DFU_CONTROL[16] = {0x50, 0xEA, 0xDA, 0x30, 0x88, 0x83, 0xB8, 0x9F, 0x60, 0x4F, 0x15, 0xF3, 0x03, 0x00, 0xC9, 0x8E};
const uint8_t UUID128_CHR_DFU_CONTROL[16] = {0x50, 0xEA, 0xDA, 0x30, 0x88, 0x83, 0xB8, 0x9F,
0x60, 0x4F, 0x15, 0xF3, 0x03, 0x00, 0xC9, 0x8E};
extern "C" void bootloader_util_app_start(uint32_t start_addr);
static uint16_t crc16(const uint8_t *data_p, uint8_t length) {
uint16_t crc = 0xFFFF;
static uint16_t crc16(const uint8_t *data_p, uint8_t length)
{
uint16_t crc = 0xFFFF;
while (length--) {
uint8_t x = crc >> 8 ^ *data_p++;
x ^= x >> 4;
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x << 5)) ^ ((uint16_t)x);
}
return crc;
while (length--) {
uint8_t x = crc >> 8 ^ *data_p++;
x ^= x >> 4;
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x << 5)) ^ ((uint16_t)x);
}
return crc;
}
static void bledfu_control_wr_authorize_cb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_write_t *request) {
if ((request->handle == chr->handles().value_handle) && (request->op != BLE_GATTS_OP_PREP_WRITE_REQ) &&
(request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) && (request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) {
BLEConnection *conn = Bluefruit.Connection(conn_hdl);
static void bledfu_control_wr_authorize_cb(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_write_t *request)
{
if ((request->handle == chr->handles().value_handle) && (request->op != BLE_GATTS_OP_PREP_WRITE_REQ) &&
(request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_NOW) && (request->op != BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL)) {
BLEConnection *conn = Bluefruit.Connection(conn_hdl);
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE};
ble_gatts_rw_authorize_reply_params_t reply = {.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE};
if (!chr->indicateEnabled(conn_hdl)) {
reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
return;
}
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
enum { START_DFU = 1 };
if (request->data[0] == START_DFU) {
// Peer data information so that bootloader could re-connect after reboot
typedef struct {
ble_gap_addr_t addr;
ble_gap_irk_t irk;
ble_gap_enc_key_t enc_key;
uint8_t sys_attr[8];
uint16_t crc16;
} peer_data_t;
VERIFY_STATIC(offsetof(peer_data_t, crc16) == 60);
/* Save Peer data
* Peer data address is defined in bootloader linker @0x20007F80
* - If bonded : save Security information
* - Otherwise : save Address for direct advertising
*
* TODO may force bonded only for security reason
*/
peer_data_t *peer_data = (peer_data_t *)(0x20007F80UL);
varclr(peer_data);
// Get CCCD
uint16_t sysattr_len = sizeof(peer_data->sys_attr);
sd_ble_gatts_sys_attr_get(conn_hdl, peer_data->sys_attr, &sysattr_len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
// Get Bond Data or using Address if not bonded
peer_data->addr = conn->getPeerAddr();
if (conn->secured()) {
bond_keys_t bkeys;
if (conn->loadBondKey(&bkeys)) {
peer_data->addr = bkeys.peer_id.id_addr_info;
peer_data->irk = bkeys.peer_id.id_info;
peer_data->enc_key = bkeys.own_enc;
if (!chr->indicateEnabled(conn_hdl)) {
reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
return;
}
}
// Calculate crc
peer_data->crc16 = crc16((uint8_t *)peer_data, offsetof(peer_data_t, crc16));
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
// Initiate DFU Sequence and reboot into DFU OTA mode
Bluefruit.Advertising.restartOnDisconnect(false);
conn->disconnect();
enum { START_DFU = 1 };
if (request->data[0] == START_DFU) {
// Peer data information so that bootloader could re-connect after reboot
typedef struct {
ble_gap_addr_t addr;
ble_gap_irk_t irk;
ble_gap_enc_key_t enc_key;
uint8_t sys_attr[8];
uint16_t crc16;
} peer_data_t;
NRF_POWER->GPREGRET = 0xB1;
NVIC_SystemReset();
VERIFY_STATIC(offsetof(peer_data_t, crc16) == 60);
/* Save Peer data
* Peer data address is defined in bootloader linker @0x20007F80
* - If bonded : save Security information
* - Otherwise : save Address for direct advertising
*
* TODO may force bonded only for security reason
*/
peer_data_t *peer_data = (peer_data_t *)(0x20007F80UL);
varclr(peer_data);
// Get CCCD
uint16_t sysattr_len = sizeof(peer_data->sys_attr);
sd_ble_gatts_sys_attr_get(conn_hdl, peer_data->sys_attr, &sysattr_len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
// Get Bond Data or using Address if not bonded
peer_data->addr = conn->getPeerAddr();
if (conn->secured()) {
bond_keys_t bkeys;
if (conn->loadBondKey(&bkeys)) {
peer_data->addr = bkeys.peer_id.id_addr_info;
peer_data->irk = bkeys.peer_id.id_info;
peer_data->enc_key = bkeys.own_enc;
}
}
// Calculate crc
peer_data->crc16 = crc16((uint8_t *)peer_data, offsetof(peer_data_t, crc16));
// Initiate DFU Sequence and reboot into DFU OTA mode
Bluefruit.Advertising.restartOnDisconnect(false);
conn->disconnect();
NRF_POWER->GPREGRET = 0xB1;
NVIC_SystemReset();
}
}
}
}
BLEDfuSecure::BLEDfuSecure(void) : BLEService(UUID16_SVC_DFU_OTA), _chr_control(UUID128_CHR_DFU_CONTROL) {}
err_t BLEDfuSecure::begin(void) {
// Invoke base class begin()
VERIFY_STATUS(BLEService::begin());
err_t BLEDfuSecure::begin(void)
{
// Invoke base class begin()
VERIFY_STATUS(BLEService::begin());
_chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_INDICATE);
_chr_control.setMaxLen(23);
_chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb);
VERIFY_STATUS(_chr_control.begin());
_chr_control.setProperties(CHR_PROPS_WRITE | CHR_PROPS_INDICATE);
_chr_control.setMaxLen(23);
_chr_control.setWriteAuthorizeCallback(bledfu_control_wr_authorize_cb);
VERIFY_STATUS(_chr_control.begin());
return ERROR_NONE;
return ERROR_NONE;
}
+7 -6
View File
@@ -41,14 +41,15 @@
#include "BLECharacteristic.h"
#include "BLEService.h"
class BLEDfuSecure : public BLEService {
protected:
BLECharacteristic _chr_control;
class BLEDfuSecure : public BLEService
{
protected:
BLECharacteristic _chr_control;
public:
BLEDfuSecure(void);
public:
BLEDfuSecure(void);
virtual err_t begin(void);
virtual err_t begin(void);
};
#endif /* BLEDFUSECURE_H_ */
+342 -311
View File
@@ -19,12 +19,12 @@ static BLEBas blebas; // BAS (Battery Service) helper class instance
#ifndef BLE_DFU_SECURE
static BLEDfu bledfu; // DFU software update helper service
#else
static BLEDfuSecure bledfusecure; // DFU software update helper service
static BLEDfuSecure bledfusecure; // DFU software update helper service
#endif
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be
// in process at once static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size),
// MyNodeInfo_size), FromRadio_size)];
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
// process at once
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
static uint8_t fromRadioBytes[meshtastic_FromRadio_size];
static uint8_t toRadioBytes[meshtastic_ToRadio_size];
@@ -33,372 +33,403 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
static uint16_t connectionHandle;
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);
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);
LOG_INFO("BLE notify fromNum");
fromNum.notify32(fromRadioNum);
}
LOG_INFO("BLE notify fromNum");
fromNum.notify32(fromRadioNum);
}
/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override { return Bluefruit.connected(connectionHandle); }
/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override { return Bluefruit.connected(connectionHandle); }
public:
BluetoothPhoneAPI() { api_type = TYPE_BLE; }
public:
BluetoothPhoneAPI() { api_type = TYPE_BLE; }
};
static BluetoothPhoneAPI *bluetoothPhoneAPI;
void onConnect(uint16_t conn_handle) {
// Get the reference to current connection
BLEConnection *connection = Bluefruit.Connection(conn_handle);
connectionHandle = conn_handle;
char central_name[32] = {0};
connection->getPeerName(central_name, sizeof(central_name));
LOG_INFO("BLE Connected to %s", central_name);
void onConnect(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection *connection = Bluefruit.Connection(conn_handle);
connectionHandle = conn_handle;
char central_name[32] = {0};
connection->getPeerName(central_name, sizeof(central_name));
LOG_INFO("BLE Connected to %s", central_name);
// Notify UI (or any other interested firmware components)
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newStatus);
// Notify UI (or any other interested firmware components)
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newStatus);
}
/**
* 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 onDisconnect(uint16_t conn_handle, uint8_t reason) {
LOG_INFO("BLE Disconnected, reason = 0x%x", reason);
if (bluetoothPhoneAPI) {
bluetoothPhoneAPI->close();
}
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
memset(lastToRadio, 0, sizeof(lastToRadio));
// Notify UI (or any other interested firmware components)
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus);
}
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value) {
// Display the raw request packet
LOG_INFO("CCCD Updated: %u", cccd_value);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
// According to the GATT spec: cccd value = 0x0001 means notifications are enabled
// and cccd value = 0x0002 means indications are enabled
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
if (result) {
LOG_INFO("Notify/Indicate enabled");
} else {
LOG_INFO("Notify/Indicate disabled");
void onDisconnect(uint16_t conn_handle, uint8_t reason)
{
LOG_INFO("BLE Disconnected, reason = 0x%x", reason);
if (bluetoothPhoneAPI) {
bluetoothPhoneAPI->close();
}
}
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
memset(lastToRadio, 0, sizeof(lastToRadio));
// Notify UI (or any other interested firmware components)
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newStatus);
}
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
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
{
// Display the raw request packet
LOG_INFO("CCCD Updated: %u", cccd_value);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
// According to the GATT spec: cccd value = 0x0001 means notifications are enabled
// and cccd value = 0x0002 means indications are enabled
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
if (result) {
LOG_INFO("Notify/Indicate enabled");
} else {
LOG_INFO("Notify/Indicate disabled");
}
}
}
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);
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 onFromRadioAuthorize(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);
// 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 {
// LOG_INFO("Ignore successor read");
}
authorizeRead(conn_hdl);
void onFromRadioAuthorize(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);
// 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 {
// LOG_INFO("Ignore successor read");
}
authorizeRead(conn_hdl);
}
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len) {
LOG_INFO("toRadioWriteCb data %p, len %u", data, len);
if (memcmp(lastToRadio, data, len) != 0) {
LOG_DEBUG("New ToRadio packet");
memcpy(lastToRadio, data, len);
bluetoothPhoneAPI->handleToRadio(data, len);
} else {
LOG_DEBUG("Drop dup ToRadio packet we just saw");
}
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
{
LOG_INFO("toRadioWriteCb data %p, len %u", data, len);
if (memcmp(lastToRadio, data, len) != 0) {
LOG_DEBUG("New ToRadio packet");
memcpy(lastToRadio, data, len);
bluetoothPhoneAPI->handleToRadio(data, len);
} else {
LOG_DEBUG("Drop dup ToRadio packet we just saw");
}
}
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!
auto secMode = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
fromNum.setPermission(secMode, 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(onCccd); // 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();
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!
auto secMode =
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
fromNum.setPermission(secMode, 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(onCccd); // 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();
fromRadio.setProperties(CHR_PROPS_READ);
fromRadio.setPermission(secMode, SECMODE_NO_ACCESS);
fromRadio.setMaxLen(sizeof(fromRadioBytes));
fromRadio.setReadAuthorizeCallback(onFromRadioAuthorize,
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();
fromRadio.setProperties(CHR_PROPS_READ);
fromRadio.setPermission(secMode, SECMODE_NO_ACCESS);
fromRadio.setMaxLen(sizeof(fromRadioBytes));
fromRadio.setReadAuthorizeCallback(
onFromRadioAuthorize,
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, secMode); // FIXME secure this!
toRadio.setFixedLen(0);
toRadio.setMaxLen(512);
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
toRadio.setWriteCallback(onToRadioWrite, false);
toRadio.begin();
toRadio.setProperties(CHR_PROPS_WRITE);
toRadio.setPermission(secMode, secMode); // FIXME secure this!
toRadio.setFixedLen(0);
toRadio.setMaxLen(512);
toRadio.setBuffer(toRadioBytes, sizeof(toRadioBytes));
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
toRadio.setWriteCallback(onToRadioWrite, false);
toRadio.begin();
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
logRadio.setMaxLen(512);
logRadio.setCccdWriteCallback(onCccd);
logRadio.write32(0);
logRadio.begin();
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
logRadio.setMaxLen(512);
logRadio.setCccdWriteCallback(onCccd);
logRadio.write32(0);
logRadio.begin();
}
static uint32_t configuredPasskey;
void NRF52Bluetooth::shutdown() {
// Shutdown bluetooth for minimum power draw
LOG_INFO("Disable NRF52 bluetooth");
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onUnwantedPairing); // Actively refuse (during factory reset)
disconnect();
Bluefruit.Advertising.stop();
void NRF52Bluetooth::shutdown()
{
// Shutdown bluetooth for minimum power draw
LOG_INFO("Disable NRF52 bluetooth");
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onUnwantedPairing); // Actively refuse (during factory reset)
disconnect();
Bluefruit.Advertising.stop();
}
void NRF52Bluetooth::startDisabled() {
// Setup Bluetooth
nrf52Bluetooth->setup();
// Shutdown bluetooth for minimum power draw
Bluefruit.Advertising.stop();
Bluefruit.setTxPower(-40); // Minimum power
LOG_INFO("Disable NRF52 Bluetooth. (Workaround: tx power min, advertise stopped)");
void NRF52Bluetooth::startDisabled()
{
// Setup Bluetooth
nrf52Bluetooth->setup();
// Shutdown bluetooth for minimum power draw
Bluefruit.Advertising.stop();
Bluefruit.setTxPower(-40); // Minimum power
LOG_INFO("Disable NRF52 Bluetooth. (Workaround: tx power min, advertise stopped)");
}
bool NRF52Bluetooth::isConnected() { return Bluefruit.connected(connectionHandle); }
int NRF52Bluetooth::getRssi() {
return 0; // FIXME figure out where to source this
bool NRF52Bluetooth::isConnected()
{
return Bluefruit.connected(connectionHandle);
}
void NRF52Bluetooth::setup() {
// Initialise the Bluefruit module
LOG_INFO("Init the Bluefruit nRF52 module");
Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
// Clear existing data.
Bluefruit.Advertising.stop();
Bluefruit.Advertising.clearData();
Bluefruit.ScanResponse.clearData();
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
configuredPasskey =
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN ? config.bluetooth.fixed_pin : random(100000, 999999);
auto pinString = std::to_string(configuredPasskey);
LOG_INFO("Bluetooth pin set to '%i'", configuredPasskey);
Bluefruit.Security.setPIN(pinString.c_str());
Bluefruit.Security.setIOCaps(true, false, false);
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey);
Bluefruit.Security.setPairCompleteCallback(NRF52Bluetooth::onPairingCompleted);
Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured);
meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
} else {
Bluefruit.Security.setIOCaps(false, false, false);
meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN);
}
// Set the advertised device name (keep it short!)
Bluefruit.setName(getDeviceName());
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(onConnect);
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
int NRF52Bluetooth::getRssi()
{
return 0; // FIXME figure out where to source this
}
void NRF52Bluetooth::setup()
{
// Initialise the Bluefruit module
LOG_INFO("Init the Bluefruit nRF52 module");
Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
// Clear existing data.
Bluefruit.Advertising.stop();
Bluefruit.Advertising.clearData();
Bluefruit.ScanResponse.clearData();
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
? config.bluetooth.fixed_pin
: random(100000, 999999);
auto pinString = std::to_string(configuredPasskey);
LOG_INFO("Bluetooth pin set to '%i'", configuredPasskey);
Bluefruit.Security.setPIN(pinString.c_str());
Bluefruit.Security.setIOCaps(true, false, false);
Bluefruit.Security.setPairPasskeyCallback(NRF52Bluetooth::onPairingPasskey);
Bluefruit.Security.setPairCompleteCallback(NRF52Bluetooth::onPairingCompleted);
Bluefruit.Security.setSecuredCallback(NRF52Bluetooth::onConnectionSecured);
meshBleService.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
} else {
Bluefruit.Security.setIOCaps(false, false, false);
meshBleService.setPermission(SECMODE_OPEN, SECMODE_OPEN);
}
// Set the advertised device name (keep it short!)
Bluefruit.setName(getDeviceName());
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(onConnect);
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
#ifndef BLE_DFU_SECURE
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
bledfu.begin(); // Install the DFU helper
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
bledfu.begin(); // Install the DFU helper
#else
bledfusecure.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // add by WayenWeng
bledfusecure.begin(); // Install the DFU helper
bledfusecure.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM); // add by WayenWeng
bledfusecure.begin(); // Install the DFU helper
#endif
// Configure and Start the Device Information Service
LOG_INFO("Init the Device Information Service");
bledis.setModel(optstr(HW_VERSION));
bledis.setFirmwareRev(optstr(APP_VERSION));
bledis.begin();
// Start the BLE Battery Service and set it to 100%
LOG_INFO("Init the Battery Service");
blebas.begin();
blebas.write(0); // Unknown battery level for now
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
LOG_INFO("Init the Mesh bluetooth service");
setupMeshService();
// Setup the advertising packet(s)
LOG_INFO("Set up the advertising payload(s)");
startAdv();
LOG_INFO("Advertise");
// Configure and Start the Device Information Service
LOG_INFO("Init the Device Information Service");
bledis.setModel(optstr(HW_VERSION));
bledis.setFirmwareRev(optstr(APP_VERSION));
bledis.begin();
// Start the BLE Battery Service and set it to 100%
LOG_INFO("Init the Battery Service");
blebas.begin();
blebas.write(0); // Unknown battery level for now
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
LOG_INFO("Init the Mesh bluetooth service");
setupMeshService();
// Setup the advertising packet(s)
LOG_INFO("Set up the advertising payload(s)");
startAdv();
LOG_INFO("Advertise");
}
void NRF52Bluetooth::resumeAdvertising() {
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);
void NRF52Bluetooth::resumeAdvertising()
{
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);
}
/// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level) { blebas.write(level); }
void NRF52Bluetooth::clearBonds() {
LOG_INFO("Clear bluetooth bonds!");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
void updateBatteryLevel(uint8_t level)
{
blebas.write(level);
}
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle) { LOG_INFO("BLE connection secured"); }
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
char passkey1[4] = {passkey[0], passkey[1], passkey[2], '\0'};
char passkey2[4] = {passkey[3], passkey[4], passkey[5], '\0'};
LOG_INFO("BLE pair process started with passkey %s %s", passkey1, passkey2);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
void NRF52Bluetooth::clearBonds()
{
LOG_INFO("Clear bluetooth bonds!");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
}
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle)
{
LOG_INFO("BLE connection secured");
}
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
{
char passkey1[4] = {passkey[0], passkey[1], passkey[2], '\0'};
char passkey2[4] = {passkey[3], passkey[4], passkey[5], '\0'};
LOG_INFO("BLE pair process started with passkey %s %s", passkey1, passkey2);
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
// Get passkey as string
// Note: possible leading zeros
std::string textkey;
for (uint8_t i = 0; i < 6; i++)
textkey += (char)passkey[i];
// Get passkey as string
// Note: possible leading zeros
std::string textkey;
for (uint8_t i = 0; i < 6; i++)
textkey += (char)passkey[i];
// Notify UI (or other components) of pairing event and passkey
meshtastic::BluetoothStatus newStatus(textkey);
bluetoothStatus->updateStatus(&newStatus);
// Notify UI (or other components) of pairing event and passkey
meshtastic::BluetoothStatus newStatus(textkey);
bluetoothStatus->updateStatus(&newStatus);
#if HAS_SCREEN && !defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and
// observe bluetoothStatus
if (screen) {
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 12;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
#if HAS_SCREEN && \
!defined(MESHTASTIC_EXCLUDE_SCREEN) // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (screen) {
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
char btPIN[16] = "888888";
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 12;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
display->setFont(FONT_SMALL);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
display->drawString(x_offset + x, y_offset + y, "Enter this code");
display->setFont(FONT_SMALL);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
display->drawString(x_offset + x, y_offset + y, "Enter this code");
display->setFont(FONT_LARGE);
String displayPin(btPIN);
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
display->drawString(x_offset + x, y_offset + y, pin);
display->setFont(FONT_LARGE);
String displayPin(btPIN);
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
display->drawString(x_offset + x, y_offset + y, pin);
display->setFont(FONT_SMALL);
String deviceName = "Name: ";
deviceName.concat(getDeviceName());
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
display->drawString(x_offset + x, y_offset + y, deviceName);
});
}
#endif
if (match_request) {
uint32_t start_time = millis();
while (millis() < start_time + 30000) {
if (!Bluefruit.connected(conn_handle))
break;
display->setFont(FONT_SMALL);
String deviceName = "Name: ";
deviceName.concat(getDeviceName());
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
display->drawString(x_offset + x, y_offset + y, deviceName);
});
}
}
LOG_INFO("BLE passkey pair: match_request=%i", match_request);
return true;
#endif
if (match_request) {
uint32_t start_time = millis();
while (millis() < start_time + 30000) {
if (!Bluefruit.connected(conn_handle))
break;
}
}
LOG_INFO("BLE passkey pair: match_request=%i", match_request);
return true;
}
// Actively refuse new BLE pairings
// After clearing bonds (at factory reset), clients seem initially able to attempt to re-pair, even with advertising
// disabled. On NRF52Bluetooth::shutdown, we change the pairing callback to this method, to aggressively refuse any
// connection attempts.
bool NRF52Bluetooth::onUnwantedPairing(uint16_t conn_handle, uint8_t const passkey[6], bool match_request) {
NRF52Bluetooth::disconnect();
return false;
// After clearing bonds (at factory reset), clients seem initially able to attempt to re-pair, even with advertising disabled.
// On NRF52Bluetooth::shutdown, we change the pairing callback to this method, to aggressively refuse any connection attempts.
bool NRF52Bluetooth::onUnwantedPairing(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
{
NRF52Bluetooth::disconnect();
return false;
}
// Disconnect any BLE connections
void NRF52Bluetooth::disconnect() {
uint8_t connection_num = Bluefruit.connected();
if (connection_num) {
// Close all connections. We're only expecting one.
for (uint8_t i = 0; i < connection_num; i++)
Bluefruit.disconnect(i);
void NRF52Bluetooth::disconnect()
{
uint8_t connection_num = Bluefruit.connected();
if (connection_num) {
// Close all connections. We're only expecting one.
for (uint8_t i = 0; i < connection_num; i++)
Bluefruit.disconnect(i);
// Wait for disconnection
while (Bluefruit.connected())
yield();
// Wait for disconnection
while (Bluefruit.connected())
yield();
LOG_INFO("Ended BLE connection");
}
LOG_INFO("Ended BLE connection");
}
}
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status) {
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
LOG_INFO("BLE pair success");
meshtastic::BluetoothStatus newConnectedStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newConnectedStatus);
} else {
LOG_INFO("BLE pair failed");
// Notify UI (or any other interested firmware components)
meshtastic::BluetoothStatus newDisconnectedStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newDisconnectedStatus);
}
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status)
{
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS) {
LOG_INFO("BLE pair success");
meshtastic::BluetoothStatus newConnectedStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
bluetoothStatus->updateStatus(&newConnectedStatus);
} else {
LOG_INFO("BLE pair failed");
// Notify UI (or any other interested firmware components)
meshtastic::BluetoothStatus newDisconnectedStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
bluetoothStatus->updateStatus(&newDisconnectedStatus);
}
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (screen) {
screen->endAlert();
}
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
if (screen) {
screen->endAlert();
}
}
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length) {
if (!isConnected() || length > 512)
return;
if (logRadio.indicateEnabled())
logRadio.indicate(logMessage, (uint16_t)length);
else
logRadio.notify(logMessage, (uint16_t)length);
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
{
if (!isConnected() || length > 512)
return;
if (logRadio.indicateEnabled())
logRadio.indicate(logMessage, (uint16_t)length);
else
logRadio.notify(logMessage, (uint16_t)length);
}
+17 -16
View File
@@ -3,22 +3,23 @@
#include "BluetoothCommon.h"
#include <Arduino.h>
class NRF52Bluetooth : BluetoothApi {
public:
void setup();
void shutdown();
void startDisabled();
void resumeAdvertising();
void clearBonds();
bool isConnected();
int getRssi();
void sendLog(const uint8_t *logMessage, size_t length);
class NRF52Bluetooth : BluetoothApi
{
public:
void setup();
void shutdown();
void startDisabled();
void resumeAdvertising();
void clearBonds();
bool isConnected();
int getRssi();
void sendLog(const uint8_t *logMessage, size_t length);
private:
static void onConnectionSecured(uint16_t conn_handle);
static bool onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
static void onPairingCompleted(uint16_t conn_handle, uint8_t auth_status);
private:
static void onConnectionSecured(uint16_t conn_handle);
static bool onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
static void onPairingCompleted(uint16_t conn_handle, uint8_t auth_status);
static bool onUnwantedPairing(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
static void disconnect();
static bool onUnwantedPairing(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
static void disconnect();
};
+22 -20
View File
@@ -2,29 +2,31 @@
#include "aes-256/tiny-aes.h"
#include "configuration.h"
#include <Adafruit_nRFCrypto.h>
class NRF52CryptoEngine : public CryptoEngine {
public:
NRF52CryptoEngine() {}
class NRF52CryptoEngine : public CryptoEngine
{
public:
NRF52CryptoEngine() {}
~NRF52CryptoEngine() {}
~NRF52CryptoEngine() {}
virtual void encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override {
if (_key.length > 16) {
AES_ctx ctx;
AES_init_ctx_iv(&ctx, _key.bytes, _nonce);
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
} else if (_key.length > 0) {
nRFCrypto.begin();
nRFCrypto_AES ctx;
uint8_t myLen = ctx.blockLen(numBytes);
char encBuf[myLen] = {0};
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 encryptAESCtr(CryptoKey _key, uint8_t *_nonce, size_t numBytes, uint8_t *bytes) override
{
if (_key.length > 16) {
AES_ctx ctx;
AES_init_ctx_iv(&ctx, _key.bytes, _nonce);
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
} else if (_key.length > 0) {
nRFCrypto.begin();
nRFCrypto_AES ctx;
uint8_t myLen = ctx.blockLen(numBytes);
char encBuf[myLen] = {0};
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);
}
}
}
};
CryptoEngine *crypto = new NRF52CryptoEngine();
+180 -161
View File
@@ -19,179 +19,198 @@ 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,
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];
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];
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];
}
if (i % Nk == 4) {
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[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];
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];
}
}
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;
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); }
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
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;
tempa[0] = tempa[0] ^ Rcon[i / Nk];
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
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]);
}
}
+2 -2
View File
@@ -9,8 +9,8 @@
#define AES_keyExpSize 240
struct AES_ctx {
uint8_t RoundKey[AES_keyExpSize];
uint8_t Iv[AES_BLOCKLEN];
uint8_t RoundKey[AES_keyExpSize];
uint8_t Iv[AES_BLOCKLEN];
};
void AES_init_ctx(struct AES_ctx *ctx, const uint8_t *key);
+18 -10
View File
@@ -7,18 +7,26 @@
* 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 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);
}
void operator delete[](void *ptr) { rtos_free(ptr); }
void operator delete[](void *ptr)
{
rtos_free(ptr);
}
+2 -2
View File
@@ -155,8 +155,8 @@
// 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 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
+76 -71
View File
@@ -1,90 +1,94 @@
#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/
// 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 LOG_DEBUG for these crash dumps because it depends on threading still being running. Instead
// use the segger in memory tool
// we can't use the regular LOG_DEBUG 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 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 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");
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);
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->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);
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("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
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) ;
// 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");
extern "C" void HardFault_Handler(void)
{
asm volatile(" mrs r0,msp\n"
" b HardFault_Impl \n");
}
#elif !defined(ARCH_NRF52)
@@ -94,14 +98,15 @@ 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");
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
+317 -299
View File
@@ -38,96 +38,101 @@ void variant_shutdown() {}
static nrfx_wdt_t nrfx_wdt = NRFX_WDT_INSTANCE(0);
static nrfx_wdt_channel_id nrfx_wdt_channel_id_nrf52_main;
static inline void debugger_break(void) {
__asm volatile("bkpt #0x01\n\t"
"mov pc, lr\n\t");
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());
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) {
LOG_ERROR("assert failed %s: %d, %s, test=%s", file, line, func, failedexpr);
// debugger_break(); FIXME doesn't work, possibly not for segger
// Reboot cpu
NVIC_SystemReset();
void __attribute__((noreturn)) __assert_func(const char *file, int line, const char *func, const char *failedexpr)
{
LOG_ERROR("assert failed %s: %d, %s, test=%s", 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
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_V24;
static void initBrownout()
{
auto vccthresh = POWER_POFCON_THRESHOLD_V24;
auto err_code = sd_power_pof_enable(POWER_POFCON_POF_Enabled);
assert(err_code == NRF_SUCCESS);
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);
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
// We don't bother with setting up brownout if soft device is disabled - because during production we always use softdevice
}
// This is a public global so that the debugger can set it to false automatically from our gdbinit
bool useSoftDevice = true; // Set to false for easier debugging
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
void setBluetoothEnable(bool enable) {
// For debugging use: don't use bluetooth
if (!useSoftDevice) {
if (enable)
LOG_INFO("Disable NRF52 BLUETOOTH WHILE DEBUGGING");
return;
}
// If user disabled bluetooth: init then disable advertising & reduce power
// Workaround. Avoid issue where device hangs several days after boot..
// Allegedly, no significant increase in power consumption
if (!config.bluetooth.enabled) {
static bool initialized = false;
if (!initialized) {
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->startDisabled();
initBrownout();
initialized = true;
void setBluetoothEnable(bool enable)
{
// For debugging use: don't use bluetooth
if (!useSoftDevice) {
if (enable)
LOG_INFO("Disable NRF52 BLUETOOTH WHILE DEBUGGING");
return;
}
return;
}
if (enable) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
// If not yet set-up
if (!nrf52Bluetooth) {
LOG_DEBUG("Init NRF52 Bluetooth");
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
// We delay brownout init until after BLE because BLE starts soft device
initBrownout();
// If user disabled bluetooth: init then disable advertising & reduce power
// Workaround. Avoid issue where device hangs several days after boot..
// Allegedly, no significant increase in power consumption
if (!config.bluetooth.enabled) {
static bool initialized = false;
if (!initialized) {
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->startDisabled();
initBrownout();
initialized = true;
}
return;
}
if (enable) {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
// If not yet set-up
if (!nrf52Bluetooth) {
LOG_DEBUG("Init NRF52 Bluetooth");
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
// We delay brownout init until after BLE because BLE starts soft device
initBrownout();
}
// Already setup, apparently
else
nrf52Bluetooth->resumeAdvertising();
}
// Disable (if previously set-up)
else if (nrf52Bluetooth) {
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
nrf52Bluetooth->shutdown();
}
// Already setup, apparently
else
nrf52Bluetooth->resumeAdvertising();
}
// Disable (if previously set-up)
else if (nrf52Bluetooth) {
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
nrf52Bluetooth->shutdown();
}
}
#else
#warning NRF52 "Bluetooth disable" workaround does not apply to builds with MESHTASTIC_EXCLUDE_BLUETOOTH
@@ -136,90 +141,97 @@ void setBluetoothEnable(bool enable) {}
/**
* 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;
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;
}
namespace {
namespace
{
constexpr uint8_t NRF52_MAGIC_LFS_IS_CORRUPT = 0xF5;
constexpr uint32_t MULTIPLE_CORRUPTION_DELAY_MILLIS = 20 * 60 * 1000;
static unsigned long millis_until_formatting_again = 0;
// Report the critical error from loop(), giving a chance for the screen to be initialized first.
inline void reportLittleFSCorruptionOnce() {
static bool report_corruption = !!millis_until_formatting_again;
if (report_corruption) {
report_corruption = false;
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
}
inline void reportLittleFSCorruptionOnce()
{
static bool report_corruption = !!millis_until_formatting_again;
if (report_corruption) {
report_corruption = false;
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
}
}
} // namespace
void preFSBegin() {
// The GPREGRET register keeps its value across warm boots. Check that this is a warm boot and, if GPREGRET
// is set to NRF52_MAGIC_LFS_IS_CORRUPT, format LittleFS.
if (!(NRF_POWER->RESETREAS == 0 && NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT))
return;
NRF_POWER->GPREGRET = 0;
millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS;
InternalFS.format();
LOG_INFO("LittleFS format complete; restoring default settings");
void preFSBegin()
{
// The GPREGRET register keeps its value across warm boots. Check that this is a warm boot and, if GPREGRET
// is set to NRF52_MAGIC_LFS_IS_CORRUPT, format LittleFS.
if (!(NRF_POWER->RESETREAS == 0 && NRF_POWER->GPREGRET == NRF52_MAGIC_LFS_IS_CORRUPT))
return;
NRF_POWER->GPREGRET = 0;
millis_until_formatting_again = millis() + MULTIPLE_CORRUPTION_DELAY_MILLIS;
InternalFS.format();
LOG_INFO("LittleFS format complete; restoring default settings");
}
extern "C" void lfs_assert(const char *reason) {
LOG_ERROR("LittleFS corruption detected: %s", reason);
if (millis_until_formatting_again > millis()) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
const long millis_remain = millis_until_formatting_again - millis();
LOG_WARN("Pausing %d seconds to avoid wear on flash storage", millis_remain / 1000);
delay(millis_remain);
}
LOG_INFO("Rebooting to format LittleFS");
delay(500); // Give the serial port a bit of time to output that last message.
// Try setting GPREGRET with the SoftDevice first. If that fails (perhaps because the SD hasn't been initialize yet)
// then set NRF_POWER->GPREGRET directly.
if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS && sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) {
NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT;
}
NVIC_SystemReset();
}
void checkSDEvents() {
if (useSoftDevice) {
uint32_t evt;
while (NRF_SUCCESS == sd_evt_get(&evt)) {
switch (evt) {
case NRF_EVT_POWER_FAILURE_WARNING:
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
break;
default:
LOG_DEBUG("Unexpected SDevt %d", evt);
break;
}
extern "C" void lfs_assert(const char *reason)
{
LOG_ERROR("LittleFS corruption detected: %s", reason);
if (millis_until_formatting_again > millis()) {
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
const long millis_remain = millis_until_formatting_again - millis();
LOG_WARN("Pausing %d seconds to avoid wear on flash storage", millis_remain / 1000);
delay(millis_remain);
}
} else {
if (NRF_POWER->EVENTS_POFWARN)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
}
LOG_INFO("Rebooting to format LittleFS");
delay(500); // Give the serial port a bit of time to output that last message.
// Try setting GPREGRET with the SoftDevice first. If that fails (perhaps because the SD hasn't been initialize yet) then set
// NRF_POWER->GPREGRET directly.
if (!(sd_power_gpregret_clr(0, 0xFF) == NRF_SUCCESS && sd_power_gpregret_set(0, NRF52_MAGIC_LFS_IS_CORRUPT) == NRF_SUCCESS)) {
NRF_POWER->GPREGRET = NRF52_MAGIC_LFS_IS_CORRUPT;
}
NVIC_SystemReset();
}
void nrf52Loop() {
{
static bool watchdog_running = false;
if (!watchdog_running) {
nrfx_wdt_enable(&nrfx_wdt);
watchdog_running = true;
}
}
nrfx_wdt_channel_feed(&nrfx_wdt, nrfx_wdt_channel_id_nrf52_main);
void checkSDEvents()
{
if (useSoftDevice) {
uint32_t evt;
while (NRF_SUCCESS == sd_evt_get(&evt)) {
switch (evt) {
case NRF_EVT_POWER_FAILURE_WARNING:
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
break;
checkSDEvents();
reportLittleFSCorruptionOnce();
default:
LOG_DEBUG("Unexpected SDevt %d", evt);
break;
}
}
} else {
if (NRF_POWER->EVENTS_POFWARN)
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_BROWNOUT);
}
}
void nrf52Loop()
{
{
static bool watchdog_running = false;
if (!watchdog_running) {
nrfx_wdt_enable(&nrfx_wdt);
watchdog_running = true;
}
}
nrfx_wdt_channel_feed(&nrfx_wdt, nrfx_wdt_channel_id_nrf52_main);
checkSDEvents();
reportLittleFSCorruptionOnce();
}
#ifdef USE_SEMIHOSTING
@@ -235,243 +247,249 @@ bool wantSemihost;
/**
* Turn on semihosting if the ICE debugger wants it.
*/
void nrf52InitSemiHosting() {
if (wantSemihost) {
static SemihostingStream semiStream;
// We must dynamically alloc because the constructor does semihost operations which
// would crash any load not talking to a debugger
semiStream.open();
semiStream.println("Semihosting starts!");
// Redirect our serial output to instead go via the ICE port
console->setDestination(&semiStream);
}
void nrf52InitSemiHosting()
{
if (wantSemihost) {
static SemihostingStream semiStream;
// We must dynamically alloc because the constructor does semihost operations which
// would crash any load not talking to a debugger
semiStream.open();
semiStream.println("Semihosting starts!");
// Redirect our serial output to instead go via the ICE port
console->setDestination(&semiStream);
}
}
#endif
void nrf52Setup() {
void nrf52Setup()
{
#ifdef ADC_V
pinMode(ADC_V, INPUT);
pinMode(ADC_V, INPUT);
#endif
uint32_t why = NRF_POWER->RESETREAS;
// per
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
LOG_DEBUG("Reset reason: 0x%x", why);
uint32_t why = NRF_POWER->RESETREAS;
// per
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
LOG_DEBUG("Reset reason: 0x%x", why);
#ifdef USE_SEMIHOSTING
nrf52InitSemiHosting();
nrf52InitSemiHosting();
#endif
// 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);
// 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())
LOG_ERROR("ERROR! Charge controller init failed");
auto *bq = new BQ25713();
if (!bq->setup())
LOG_ERROR("ERROR! Charge controller init failed");
#endif
// Init random seed
union seedParts {
uint32_t seed32;
uint8_t seed8[4];
} seed;
nRFCrypto.begin();
nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8));
LOG_DEBUG("Set random seed %u", seed.seed32);
randomSeed(seed.seed32);
nRFCrypto.end();
// Init random seed
union seedParts {
uint32_t seed32;
uint8_t seed8[4];
} seed;
nRFCrypto.begin();
nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8));
LOG_DEBUG("Set random seed %u", seed.seed32);
randomSeed(seed.seed32);
nRFCrypto.end();
// Set up nrfx watchdog. Do not enable the watchdog yet (we do that
// the first time through the main loop), so that other threads can
// allocate their own wdt channel to protect themselves from hangs.
nrfx_wdt_config_t wdt0_config = {
.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT, .reload_value = APP_WATCHDOG_SECS * 1000,
// Note: Not using wdt interrupts.
// .interrupt_priority = NRFX_WDT_DEFAULT_CONFIG_IRQ_PRIORITY
};
nrfx_err_t r = nrfx_wdt_init(&nrfx_wdt, &wdt0_config,
nullptr // Watchdog event handler, not used, we just reset.
);
assert(r == NRFX_SUCCESS);
// Set up nrfx watchdog. Do not enable the watchdog yet (we do that
// the first time through the main loop), so that other threads can
// allocate their own wdt channel to protect themselves from hangs.
nrfx_wdt_config_t wdt0_config = {
.behaviour = NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT, .reload_value = APP_WATCHDOG_SECS * 1000,
// Note: Not using wdt interrupts.
// .interrupt_priority = NRFX_WDT_DEFAULT_CONFIG_IRQ_PRIORITY
};
nrfx_err_t r = nrfx_wdt_init(&nrfx_wdt, &wdt0_config,
nullptr // Watchdog event handler, not used, we just reset.
);
assert(r == NRFX_SUCCESS);
r = nrfx_wdt_channel_alloc(&nrfx_wdt, &nrfx_wdt_channel_id_nrf52_main);
assert(r == NRFX_SUCCESS);
r = nrfx_wdt_channel_alloc(&nrfx_wdt, &nrfx_wdt_channel_id_nrf52_main);
assert(r == NRFX_SUCCESS);
}
void cpuDeepSleep(uint32_t msecToWake) {
// FIXME, configure RTC or button press to wake us
// FIXME, power down SPI, I2C, RAMs
void cpuDeepSleep(uint32_t msecToWake)
{
// FIXME, configure RTC or button press to wake us
// FIXME, power down SPI, I2C, RAMs
#if HAS_WIRE
Wire.end();
Wire.end();
#endif
SPI.end();
SPI.end();
#if SPI_INTERFACES_COUNT > 1
SPI1.end();
SPI1.end();
#endif
if (Serial) // Another check in case of disabled default serial, does nothing bad
Serial.end(); // This may cause crashes as debug messages continue to flow.
if (Serial) // Another check in case of disabled default serial, does nothing bad
Serial.end(); // This may cause crashes as debug messages continue to flow.
// This causes troubles with waking up on nrf52 (on pro-micro in particular):
// we have no Serial1 in use on nrf52, check Serial and GPS modules.
// This causes troubles with waking up on nrf52 (on pro-micro in particular):
// we have no Serial1 in use on nrf52, check Serial and GPS modules.
#ifdef PIN_SERIAL1_RX
if (Serial1) // A straightforward solution to the wake from deepsleep problem
Serial1.end();
if (Serial1) // A straightforward solution to the wake from deepsleep problem
Serial1.end();
#endif
#ifdef TTGO_T_ECHO
// To power off the T-Echo, the display must be set
// as an input pin; otherwise, there will be leakage current.
pinMode(PIN_EINK_CS, INPUT);
pinMode(PIN_EINK_DC, INPUT);
pinMode(PIN_EINK_RES, INPUT);
pinMode(PIN_EINK_BUSY, INPUT);
// To power off the T-Echo, the display must be set
// as an input pin; otherwise, there will be leakage current.
pinMode(PIN_EINK_CS, INPUT);
pinMode(PIN_EINK_DC, INPUT);
pinMode(PIN_EINK_RES, INPUT);
pinMode(PIN_EINK_BUSY, INPUT);
#endif
setBluetoothEnable(false);
setBluetoothEnable(false);
#ifdef RAK4630
#ifdef PIN_3V3_EN
digitalWrite(PIN_3V3_EN, LOW);
digitalWrite(PIN_3V3_EN, LOW);
#endif
#ifdef AQ_SET_PIN
// RAK-12039 set pin for Air quality sensor
digitalWrite(AQ_SET_PIN, LOW);
// RAK-12039 set pin for Air quality sensor
digitalWrite(AQ_SET_PIN, LOW);
#endif
#ifdef RAK14014
// GPIO restores input status, otherwise there will be leakage current
nrf_gpio_cfg_default(TFT_BL);
nrf_gpio_cfg_default(TFT_DC);
nrf_gpio_cfg_default(TFT_CS);
nrf_gpio_cfg_default(TFT_SCLK);
nrf_gpio_cfg_default(TFT_MOSI);
nrf_gpio_cfg_default(TFT_MISO);
nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
nrf_gpio_cfg_default(WB_I2C1_SCL);
nrf_gpio_cfg_default(WB_I2C1_SDA);
// GPIO restores input status, otherwise there will be leakage current
nrf_gpio_cfg_default(TFT_BL);
nrf_gpio_cfg_default(TFT_DC);
nrf_gpio_cfg_default(TFT_CS);
nrf_gpio_cfg_default(TFT_SCLK);
nrf_gpio_cfg_default(TFT_MOSI);
nrf_gpio_cfg_default(TFT_MISO);
nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
nrf_gpio_cfg_default(WB_I2C1_SCL);
nrf_gpio_cfg_default(WB_I2C1_SDA);
// nrf_gpio_cfg_default(WB_I2C2_SCL);
// nrf_gpio_cfg_default(WB_I2C2_SDA);
// nrf_gpio_cfg_default(WB_I2C2_SCL);
// nrf_gpio_cfg_default(WB_I2C2_SDA);
#endif
#endif
#ifdef MESHLINK
#ifdef PIN_WD_EN
digitalWrite(PIN_WD_EN, LOW);
digitalWrite(PIN_WD_EN, LOW);
#endif
#endif
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_MESH_SOLAR)
nrf_gpio_cfg_default(PIN_GPS_PPS);
detachInterrupt(PIN_GPS_PPS);
detachInterrupt(PIN_BUTTON1);
nrf_gpio_cfg_default(PIN_GPS_PPS);
detachInterrupt(PIN_GPS_PPS);
detachInterrupt(PIN_BUTTON1);
#endif
#ifdef ELECROW_ThinkNode_M1
for (int pin = 0; pin < 48; pin++) {
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 || pin == PIN_BUTTON1 ||
pin == PIN_BUTTON2) {
continue;
for (int pin = 0; pin < 48; pin++) {
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 ||
pin == PIN_BUTTON1 || pin == PIN_BUTTON2) {
continue;
}
pinMode(pin, OUTPUT);
}
pinMode(pin, OUTPUT);
}
for (int pin = 0; pin < 48; pin++) {
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 || pin == PIN_BUTTON1 ||
pin == PIN_BUTTON2) {
continue;
for (int pin = 0; pin < 48; pin++) {
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 ||
pin == PIN_BUTTON1 || pin == PIN_BUTTON2) {
continue;
}
digitalWrite(pin, LOW);
}
digitalWrite(pin, LOW);
}
for (int pin = 0; pin < 48; pin++) {
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 || pin == PIN_BUTTON1 ||
pin == PIN_BUTTON2) {
continue;
for (int pin = 0; pin < 48; pin++) {
if (pin == 17 || pin == 19 || pin == 20 || pin == 22 || pin == 23 || pin == 24 || pin == 25 || pin == 9 || pin == 10 ||
pin == PIN_BUTTON1 || pin == PIN_BUTTON2) {
continue;
}
NRF_GPIO->DIRCLR = (1 << pin);
}
NRF_GPIO->DIRCLR = (1 << pin);
}
#endif
variant_shutdown();
variant_shutdown();
// Sleepy trackers or sensors can low power "sleep"
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
if (msecToWake != portMAX_DELAY && (IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER,
meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR) &&
config.power.is_power_saving == true)) {
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
delay(msecToWake);
NVIC_SystemReset();
} else {
// Resume on user button press
// https://github.com/lyusupov/SoftRF/blob/81c519ca75693b696752235d559e881f2e0511ee/software/firmware/source/SoftRF/src/platform/nRF52.cpp#L1738
constexpr uint32_t DFU_MAGIC_SKIP = 0x6d;
sd_power_gpregret_clr(0, 0xFF); // Clear the register before setting a new values in it for stability reasons
sd_power_gpregret_set(0, DFU_MAGIC_SKIP); // Equivalent NRF_POWER->GPREGRET = DFU_MAGIC_SKIP
// Sleepy trackers or sensors can low power "sleep"
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
if (msecToWake != portMAX_DELAY &&
(IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_TRACKER,
meshtastic_Config_DeviceConfig_Role_TAK_TRACKER, meshtastic_Config_DeviceConfig_Role_SENSOR) &&
config.power.is_power_saving == true)) {
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
delay(msecToWake);
NVIC_SystemReset();
} else {
// Resume on user button press
// https://github.com/lyusupov/SoftRF/blob/81c519ca75693b696752235d559e881f2e0511ee/software/firmware/source/SoftRF/src/platform/nRF52.cpp#L1738
constexpr uint32_t DFU_MAGIC_SKIP = 0x6d;
sd_power_gpregret_clr(0, 0xFF); // Clear the register before setting a new values in it for stability reasons
sd_power_gpregret_set(0, DFU_MAGIC_SKIP); // Equivalent NRF_POWER->GPREGRET = DFU_MAGIC_SKIP
// 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
// 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
#ifdef ELECROW_ThinkNode_M1
nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense);
nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense);
nrf_gpio_cfg_input(PIN_BUTTON2, NRF_GPIO_PIN_PULLUP);
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(PIN_BUTTON2, sense1);
nrf_gpio_cfg_input(PIN_BUTTON2, NRF_GPIO_PIN_PULLUP);
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(PIN_BUTTON2, sense1);
#endif
#ifdef PROMICRO_DIY_TCXO
nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep
nrf_gpio_cfg_input(BUTTON_PIN, NRF_GPIO_PIN_PULLUP); // Enable internal pull-up on the button pin
nrf_gpio_pin_sense_t sense = NRF_GPIO_PIN_SENSE_LOW; // Configure SENSE signal on low edge
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep
#endif
#ifdef BATTERY_LPCOMP_INPUT
// Wake up if power rises again
nrf_lpcomp_config_t c;
c.reference = BATTERY_LPCOMP_THRESHOLD;
c.detection = NRF_LPCOMP_DETECT_UP;
c.hyst = NRF_LPCOMP_HYST_NOHYST;
nrf_lpcomp_configure(NRF_LPCOMP, &c);
nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
nrf_lpcomp_enable(NRF_LPCOMP);
// Wake up if power rises again
nrf_lpcomp_config_t c;
c.reference = BATTERY_LPCOMP_THRESHOLD;
c.detection = NRF_LPCOMP_DETECT_UP;
c.hyst = NRF_LPCOMP_HYST_NOHYST;
nrf_lpcomp_configure(NRF_LPCOMP, &c);
nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
nrf_lpcomp_enable(NRF_LPCOMP);
battery_adcEnable();
battery_adcEnable();
nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
;
nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
;
#endif
auto ok = sd_power_system_off();
if (ok != NRF_SUCCESS) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!");
NRF_POWER->SYSTEMOFF = 1;
auto ok = sd_power_system_off();
if (ok != NRF_SUCCESS) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!");
NRF_POWER->SYSTEMOFF = 1;
}
}
}
// The following code should not be run, because we are off
while (1) {
delay(5000);
LOG_DEBUG(".");
}
// The following code should not be run, because we are off
while (1) {
delay(5000);
LOG_DEBUG(".");
}
}
void clearBonds() {
if (!nrf52Bluetooth) {
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
}
nrf52Bluetooth->clearBonds();
void clearBonds()
{
if (!nrf52Bluetooth) {
nrf52Bluetooth = new NRF52Bluetooth();
nrf52Bluetooth->setup();
}
nrf52Bluetooth->clearBonds();
}
void enterDfuMode() {
void enterDfuMode()
{
// SDK kit does not have native USB like almost all other NRF52 boards
#ifdef NRF_USE_SERIAL_DFU
enterSerialDfu();
enterSerialDfu();
#else
enterUf2Dfu();
enterUf2Dfu();
#endif
}
+117 -124
View File
@@ -70,26 +70,26 @@ extern "C" {
* @brief Common API SVC numbers.
*/
enum BLE_COMMON_SVCS {
SD_BLE_ENABLE = BLE_SVC_BASE, /**< Enable and initialize the BLE stack */
SD_BLE_EVT_GET, /**< Get an event from the pending events queue. */
SD_BLE_UUID_VS_ADD, /**< Add a Vendor Specific base UUID. */
SD_BLE_UUID_DECODE, /**< Decode UUID bytes. */
SD_BLE_UUID_ENCODE, /**< Encode UUID bytes. */
SD_BLE_VERSION_GET, /**< Get the local version information (company ID, Link Layer Version, Link Layer Subversion). */
SD_BLE_USER_MEM_REPLY, /**< User Memory Reply. */
SD_BLE_OPT_SET, /**< Set a BLE option. */
SD_BLE_OPT_GET, /**< Get a BLE option. */
SD_BLE_CFG_SET, /**< Add a configuration to the BLE stack. */
SD_BLE_UUID_VS_REMOVE, /**< Remove a Vendor Specific base UUID. */
SD_BLE_ENABLE = BLE_SVC_BASE, /**< Enable and initialize the BLE stack */
SD_BLE_EVT_GET, /**< Get an event from the pending events queue. */
SD_BLE_UUID_VS_ADD, /**< Add a Vendor Specific base UUID. */
SD_BLE_UUID_DECODE, /**< Decode UUID bytes. */
SD_BLE_UUID_ENCODE, /**< Encode UUID bytes. */
SD_BLE_VERSION_GET, /**< Get the local version information (company ID, Link Layer Version, Link Layer Subversion). */
SD_BLE_USER_MEM_REPLY, /**< User Memory Reply. */
SD_BLE_OPT_SET, /**< Set a BLE option. */
SD_BLE_OPT_GET, /**< Get a BLE option. */
SD_BLE_CFG_SET, /**< Add a configuration to the BLE stack. */
SD_BLE_UUID_VS_REMOVE, /**< Remove a Vendor Specific base UUID. */
};
/**
* @brief BLE Module Independent Event IDs.
*/
enum BLE_COMMON_EVTS {
BLE_EVT_USER_MEM_REQUEST = BLE_EVT_BASE + 0, /**< User Memory request. See @ref ble_evt_user_mem_request_t
\n Reply with @ref sd_ble_user_mem_reply. */
BLE_EVT_USER_MEM_RELEASE = BLE_EVT_BASE + 1, /**< User Memory release. See @ref ble_evt_user_mem_release_t */
BLE_EVT_USER_MEM_REQUEST = BLE_EVT_BASE + 0, /**< User Memory request. See @ref ble_evt_user_mem_request_t
\n Reply with @ref sd_ble_user_mem_reply. */
BLE_EVT_USER_MEM_RELEASE = BLE_EVT_BASE + 1, /**< User Memory release. See @ref ble_evt_user_mem_release_t */
};
/**@brief BLE Connection Configuration IDs.
@@ -97,11 +97,11 @@ enum BLE_COMMON_EVTS {
* IDs that uniquely identify a connection configuration.
*/
enum BLE_CONN_CFGS {
BLE_CONN_CFG_GAP = BLE_CONN_CFG_BASE + 0, /**< BLE GAP specific connection configuration. */
BLE_CONN_CFG_GATTC = BLE_CONN_CFG_BASE + 1, /**< BLE GATTC specific connection configuration. */
BLE_CONN_CFG_GATTS = BLE_CONN_CFG_BASE + 2, /**< BLE GATTS specific connection configuration. */
BLE_CONN_CFG_GATT = BLE_CONN_CFG_BASE + 3, /**< BLE GATT specific connection configuration. */
BLE_CONN_CFG_L2CAP = BLE_CONN_CFG_BASE + 4, /**< BLE L2CAP specific connection configuration. */
BLE_CONN_CFG_GAP = BLE_CONN_CFG_BASE + 0, /**< BLE GAP specific connection configuration. */
BLE_CONN_CFG_GATTC = BLE_CONN_CFG_BASE + 1, /**< BLE GATTC specific connection configuration. */
BLE_CONN_CFG_GATTS = BLE_CONN_CFG_BASE + 2, /**< BLE GATTS specific connection configuration. */
BLE_CONN_CFG_GATT = BLE_CONN_CFG_BASE + 3, /**< BLE GATT specific connection configuration. */
BLE_CONN_CFG_L2CAP = BLE_CONN_CFG_BASE + 4, /**< BLE L2CAP specific connection configuration. */
};
/**@brief BLE Common Configuration IDs.
@@ -109,16 +109,16 @@ enum BLE_CONN_CFGS {
* IDs that uniquely identify a common configuration.
*/
enum BLE_COMMON_CFGS {
BLE_COMMON_CFG_VS_UUID = BLE_CFG_BASE, /**< Vendor specific base UUID configuration */
BLE_COMMON_CFG_VS_UUID = BLE_CFG_BASE, /**< Vendor specific base UUID configuration */
};
/**@brief Common Option IDs.
* IDs that uniquely identify a common option.
*/
enum BLE_COMMON_OPTS {
BLE_COMMON_OPT_PA_LNA = BLE_OPT_BASE + 0, /**< PA and LNA options */
BLE_COMMON_OPT_CONN_EVT_EXT = BLE_OPT_BASE + 1, /**< Extended connection events option */
BLE_COMMON_OPT_EXTENDED_RC_CAL = BLE_OPT_BASE + 2, /**< Extended RC calibration option */
BLE_COMMON_OPT_PA_LNA = BLE_OPT_BASE + 0, /**< PA and LNA options */
BLE_COMMON_OPT_CONN_EVT_EXT = BLE_OPT_BASE + 1, /**< Extended connection events option */
BLE_COMMON_OPT_EXTENDED_RC_CAL = BLE_OPT_BASE + 2, /**< Extended RC calibration option */
};
/** @} */
@@ -136,11 +136,10 @@ enum BLE_COMMON_OPTS {
/** @brief Maximum possible length for BLE Events.
* @note The highest value used for @ref ble_gatt_conn_cfg_t::att_mtu in any connection configuration shall be used as a
* parameter. If that value has not been configured for any connections then @ref BLE_GATT_ATT_MTU_DEFAULT must be used
* instead.
* parameter. If that value has not been configured for any connections then @ref BLE_GATT_ATT_MTU_DEFAULT must be used instead.
*/
#define BLE_EVT_LEN_MAX(ATT_MTU) \
(offsetof(ble_evt_t, evt.gattc_evt.params.prim_srvc_disc_rsp.services) + ((ATT_MTU)-1) / 4 * sizeof(ble_gattc_service_t))
#define BLE_EVT_LEN_MAX(ATT_MTU) \
(offsetof(ble_evt_t, evt.gattc_evt.params.prim_srvc_disc_rsp.services) + ((ATT_MTU)-1) / 4 * sizeof(ble_gattc_service_t))
/** @defgroup BLE_USER_MEM_TYPES User Memory Types
* @{ */
@@ -169,68 +168,67 @@ enum BLE_COMMON_OPTS {
/**@brief User Memory Block. */
typedef struct {
uint8_t *p_mem; /**< Pointer to the start of the user memory block. */
uint16_t len; /**< Length in bytes of the user memory block. */
uint8_t *p_mem; /**< Pointer to the start of the user memory block. */
uint16_t len; /**< Length in bytes of the user memory block. */
} ble_user_mem_block_t;
/**@brief Event structure for @ref BLE_EVT_USER_MEM_REQUEST. */
typedef struct {
uint8_t type; /**< User memory type, see @ref BLE_USER_MEM_TYPES. */
uint8_t type; /**< User memory type, see @ref BLE_USER_MEM_TYPES. */
} ble_evt_user_mem_request_t;
/**@brief Event structure for @ref BLE_EVT_USER_MEM_RELEASE. */
typedef struct {
uint8_t type; /**< User memory type, see @ref BLE_USER_MEM_TYPES. */
ble_user_mem_block_t mem_block; /**< User memory block */
uint8_t type; /**< User memory type, see @ref BLE_USER_MEM_TYPES. */
ble_user_mem_block_t mem_block; /**< User memory block */
} ble_evt_user_mem_release_t;
/**@brief Event structure for events not associated with a specific function module. */
typedef struct {
uint16_t conn_handle; /**< Connection Handle on which this event occurred. */
union {
ble_evt_user_mem_request_t user_mem_request; /**< User Memory Request Event Parameters. */
ble_evt_user_mem_release_t user_mem_release; /**< User Memory Release Event Parameters. */
} params; /**< Event parameter union. */
uint16_t conn_handle; /**< Connection Handle on which this event occurred. */
union {
ble_evt_user_mem_request_t user_mem_request; /**< User Memory Request Event Parameters. */
ble_evt_user_mem_release_t user_mem_release; /**< User Memory Release Event Parameters. */
} params; /**< Event parameter union. */
} ble_common_evt_t;
/**@brief BLE Event header. */
typedef struct {
uint16_t evt_id; /**< Value from a BLE_<module>_EVT series. */
uint16_t evt_len; /**< Length in octets including this header. */
uint16_t evt_id; /**< Value from a BLE_<module>_EVT series. */
uint16_t evt_len; /**< Length in octets including this header. */
} ble_evt_hdr_t;
/**@brief Common BLE Event type, wrapping the module specific event reports. */
typedef struct {
ble_evt_hdr_t header; /**< Event header. */
union {
ble_common_evt_t common_evt; /**< Common Event, evt_id in BLE_EVT_* series. */
ble_gap_evt_t gap_evt; /**< GAP originated event, evt_id in BLE_GAP_EVT_* series. */
ble_gattc_evt_t gattc_evt; /**< GATT client originated event, evt_id in BLE_GATTC_EVT* series. */
ble_gatts_evt_t gatts_evt; /**< GATT server originated event, evt_id in BLE_GATTS_EVT* series. */
ble_l2cap_evt_t l2cap_evt; /**< L2CAP originated event, evt_id in BLE_L2CAP_EVT* series. */
} evt; /**< Event union. */
ble_evt_hdr_t header; /**< Event header. */
union {
ble_common_evt_t common_evt; /**< Common Event, evt_id in BLE_EVT_* series. */
ble_gap_evt_t gap_evt; /**< GAP originated event, evt_id in BLE_GAP_EVT_* series. */
ble_gattc_evt_t gattc_evt; /**< GATT client originated event, evt_id in BLE_GATTC_EVT* series. */
ble_gatts_evt_t gatts_evt; /**< GATT server originated event, evt_id in BLE_GATTS_EVT* series. */
ble_l2cap_evt_t l2cap_evt; /**< L2CAP originated event, evt_id in BLE_L2CAP_EVT* series. */
} evt; /**< Event union. */
} ble_evt_t;
/**
* @brief Version Information.
*/
typedef struct {
uint8_t version_number; /**< Link Layer Version number. See
https://www.bluetooth.org/en-us/specification/assigned-numbers/link-layer for assigned
values. */
uint16_t company_id; /**< Company ID, Nordic Semiconductor's company ID is 89 (0x0059)
(https://www.bluetooth.org/apps/content/Default.aspx?doc_id=49708). */
uint16_t subversion_number; /**< Link Layer Sub Version number, corresponds to the SoftDevice Config ID or Firmware ID
(FWID). */
uint8_t version_number; /**< Link Layer Version number. See
https://www.bluetooth.org/en-us/specification/assigned-numbers/link-layer for assigned values. */
uint16_t company_id; /**< Company ID, Nordic Semiconductor's company ID is 89 (0x0059)
(https://www.bluetooth.org/apps/content/Default.aspx?doc_id=49708). */
uint16_t
subversion_number; /**< Link Layer Sub Version number, corresponds to the SoftDevice Config ID or Firmware ID (FWID). */
} ble_version_t;
/**
* @brief Configuration parameters for the PA and LNA.
*/
typedef struct {
uint8_t enable : 1; /**< Enable toggling for this amplifier */
uint8_t active_high : 1; /**< Set the pin to be active high */
uint8_t gpio_pin : 6; /**< The GPIO pin to toggle for this amplifier */
uint8_t enable : 1; /**< Enable toggling for this amplifier */
uint8_t active_high : 1; /**< Set the pin to be active high */
uint8_t gpio_pin : 6; /**< The GPIO pin to toggle for this amplifier */
} ble_pa_lna_cfg_t;
/**
@@ -243,16 +241,16 @@ typedef struct {
* by the application and should be regarded as reserved as long as any PA/LNA toggling is enabled.
*
* @note @ref sd_ble_opt_get is not supported for this option.
* @note Setting this option while the radio is in use (i.e. any of the roles are active) may have undefined
* consequences and must be avoided by the application.
* @note Setting this option while the radio is in use (i.e. any of the roles are active) may have undefined consequences
* and must be avoided by the application.
*/
typedef struct {
ble_pa_lna_cfg_t pa_cfg; /**< Power Amplifier configuration */
ble_pa_lna_cfg_t lna_cfg; /**< Low Noise Amplifier configuration */
ble_pa_lna_cfg_t pa_cfg; /**< Power Amplifier configuration */
ble_pa_lna_cfg_t lna_cfg; /**< Low Noise Amplifier configuration */
uint8_t ppi_ch_id_set; /**< PPI channel used for radio pin setting */
uint8_t ppi_ch_id_clr; /**< PPI channel used for radio pin clearing */
uint8_t gpiote_ch_id; /**< GPIOTE channel used for radio pin toggling */
uint8_t ppi_ch_id_set; /**< PPI channel used for radio pin setting */
uint8_t ppi_ch_id_clr; /**< PPI channel used for radio pin clearing */
uint8_t gpiote_ch_id; /**< GPIOTE channel used for radio pin toggling */
} ble_common_opt_pa_lna_t;
/**
@@ -260,27 +258,25 @@ typedef struct {
*
* When enabled the SoftDevice will dynamically extend the connection event when possible.
*
* The connection event length is controlled by the connection configuration as set by @ref
* ble_gap_conn_cfg_t::event_length. The connection event can be extended if there is time to send another packet pair
* before the start of the next connection interval, and if there are no conflicts with other BLE roles requesting radio
* time.
* The connection event length is controlled by the connection configuration as set by @ref ble_gap_conn_cfg_t::event_length.
* The connection event can be extended if there is time to send another packet pair before the start of the next connection
* interval, and if there are no conflicts with other BLE roles requesting radio time.
*
* @note @ref sd_ble_opt_get is not supported for this option.
*/
typedef struct {
uint8_t enable : 1; /**< Enable extended BLE connection events, disabled by default. */
uint8_t enable : 1; /**< Enable extended BLE connection events, disabled by default. */
} ble_common_opt_conn_evt_ext_t;
/**
* @brief Enable/disable extended RC calibration.
*
* If extended RC calibration is enabled and the internal RC oscillator (@ref NRF_CLOCK_LF_SRC_RC) is used as the
* SoftDevice LFCLK source, the SoftDevice as a peripheral will by default try to increase the receive window if two
* consecutive packets are not received. If it turns out that the packets were not received due to clock drift, the RC
* calibration is started. This calibration comes in addition to the periodic calibration that is configured by @ref
* sd_softdevice_enable(). When using only peripheral connections, the periodic calibration can therefore be configured
* with a much longer interval as the peripheral will be able to detect and adjust automatically to clock drift, and
* calibrate on demand.
* If extended RC calibration is enabled and the internal RC oscillator (@ref NRF_CLOCK_LF_SRC_RC) is used as the SoftDevice
* LFCLK source, the SoftDevice as a peripheral will by default try to increase the receive window if two consecutive packets
* are not received. If it turns out that the packets were not received due to clock drift, the RC calibration is started.
* This calibration comes in addition to the periodic calibration that is configured by @ref sd_softdevice_enable(). When
* using only peripheral connections, the periodic calibration can therefore be configured with a much longer interval as the
* peripheral will be able to detect and adjust automatically to clock drift, and calibrate on demand.
*
* If extended RC calibration is disabled and the internal RC oscillator is used as the SoftDevice LFCLK source, the
* RC oscillator is calibrated periodically as configured by @ref sd_softdevice_enable().
@@ -288,29 +284,28 @@ typedef struct {
* @note @ref sd_ble_opt_get is not supported for this option.
*/
typedef struct {
uint8_t enable : 1; /**< Enable extended RC calibration, enabled by default. */
uint8_t enable : 1; /**< Enable extended RC calibration, enabled by default. */
} ble_common_opt_extended_rc_cal_t;
/**@brief Option structure for common options. */
typedef union {
ble_common_opt_pa_lna_t pa_lna; /**< Parameters for controlling PA and LNA pin toggling. */
ble_common_opt_conn_evt_ext_t conn_evt_ext; /**< Parameters for enabling extended connection events. */
ble_common_opt_extended_rc_cal_t extended_rc_cal; /**< Parameters for enabling extended RC calibration. */
ble_common_opt_pa_lna_t pa_lna; /**< Parameters for controlling PA and LNA pin toggling. */
ble_common_opt_conn_evt_ext_t conn_evt_ext; /**< Parameters for enabling extended connection events. */
ble_common_opt_extended_rc_cal_t extended_rc_cal; /**< Parameters for enabling extended RC calibration. */
} ble_common_opt_t;
/**@brief Common BLE Option type, wrapping the module specific options. */
typedef union {
ble_common_opt_t common_opt; /**< COMMON options, opt_id in @ref BLE_COMMON_OPTS series. */
ble_gap_opt_t gap_opt; /**< GAP option, opt_id in @ref BLE_GAP_OPTS series. */
ble_gattc_opt_t gattc_opt; /**< GATTC option, opt_id in @ref BLE_GATTC_OPTS series. */
ble_common_opt_t common_opt; /**< COMMON options, opt_id in @ref BLE_COMMON_OPTS series. */
ble_gap_opt_t gap_opt; /**< GAP option, opt_id in @ref BLE_GAP_OPTS series. */
ble_gattc_opt_t gattc_opt; /**< GATTC option, opt_id in @ref BLE_GATTC_OPTS series. */
} ble_opt_t;
/**@brief BLE connection configuration type, wrapping the module specific configurations, set with
* @ref sd_ble_cfg_set.
*
* @note Connection configurations don't have to be set.
* In the case that no configurations has been set, or fewer connection configurations has been set than enabled
connections,
* In the case that no configurations has been set, or fewer connection configurations has been set than enabled connections,
* the default connection configuration will be automatically added for the remaining connections.
* When creating connections with the default configuration, @ref BLE_CONN_CFG_TAG_DEFAULT should be used in
* place of @ref ble_conn_cfg_t::conn_cfg_tag.
@@ -324,18 +319,17 @@ typedef union {
*/
typedef struct {
uint8_t conn_cfg_tag; /**< The application chosen tag it can use with the
@ref sd_ble_gap_adv_start() and @ref sd_ble_gap_connect() calls
to select this configuration when creating a connection.
Must be different for all connection configurations added and not @ref
BLE_CONN_CFG_TAG_DEFAULT. */
union {
ble_gap_conn_cfg_t gap_conn_cfg; /**< GAP connection configuration, cfg_id is @ref BLE_CONN_CFG_GAP. */
ble_gattc_conn_cfg_t gattc_conn_cfg; /**< GATTC connection configuration, cfg_id is @ref BLE_CONN_CFG_GATTC. */
ble_gatts_conn_cfg_t gatts_conn_cfg; /**< GATTS connection configuration, cfg_id is @ref BLE_CONN_CFG_GATTS. */
ble_gatt_conn_cfg_t gatt_conn_cfg; /**< GATT connection configuration, cfg_id is @ref BLE_CONN_CFG_GATT. */
ble_l2cap_conn_cfg_t l2cap_conn_cfg; /**< L2CAP connection configuration, cfg_id is @ref BLE_CONN_CFG_L2CAP. */
} params; /**< Connection configuration union. */
uint8_t conn_cfg_tag; /**< The application chosen tag it can use with the
@ref sd_ble_gap_adv_start() and @ref sd_ble_gap_connect() calls
to select this configuration when creating a connection.
Must be different for all connection configurations added and not @ref BLE_CONN_CFG_TAG_DEFAULT. */
union {
ble_gap_conn_cfg_t gap_conn_cfg; /**< GAP connection configuration, cfg_id is @ref BLE_CONN_CFG_GAP. */
ble_gattc_conn_cfg_t gattc_conn_cfg; /**< GATTC connection configuration, cfg_id is @ref BLE_CONN_CFG_GATTC. */
ble_gatts_conn_cfg_t gatts_conn_cfg; /**< GATTS connection configuration, cfg_id is @ref BLE_CONN_CFG_GATTS. */
ble_gatt_conn_cfg_t gatt_conn_cfg; /**< GATT connection configuration, cfg_id is @ref BLE_CONN_CFG_GATT. */
ble_l2cap_conn_cfg_t l2cap_conn_cfg; /**< L2CAP connection configuration, cfg_id is @ref BLE_CONN_CFG_L2CAP. */
} params; /**< Connection configuration union. */
} ble_conn_cfg_t;
/**
@@ -344,22 +338,22 @@ typedef struct {
* @retval ::NRF_ERROR_INVALID_PARAM Too many UUIDs configured.
*/
typedef struct {
uint8_t vs_uuid_count; /**< Number of 128-bit Vendor Specific base UUID bases to allocate memory for.
Default value is @ref BLE_UUID_VS_COUNT_DEFAULT. Maximum value is
@ref BLE_UUID_VS_COUNT_MAX. */
uint8_t vs_uuid_count; /**< Number of 128-bit Vendor Specific base UUID bases to allocate memory for.
Default value is @ref BLE_UUID_VS_COUNT_DEFAULT. Maximum value is
@ref BLE_UUID_VS_COUNT_MAX. */
} ble_common_cfg_vs_uuid_t;
/**@brief Common BLE Configuration type, wrapping the common configurations. */
typedef union {
ble_common_cfg_vs_uuid_t vs_uuid_cfg; /**< Vendor Specific base UUID configuration, cfg_id is @ref BLE_COMMON_CFG_VS_UUID. */
ble_common_cfg_vs_uuid_t vs_uuid_cfg; /**< Vendor Specific base UUID configuration, cfg_id is @ref BLE_COMMON_CFG_VS_UUID. */
} ble_common_cfg_t;
/**@brief BLE Configuration type, wrapping the module specific configurations. */
typedef union {
ble_conn_cfg_t conn_cfg; /**< Connection specific configurations, cfg_id in @ref BLE_CONN_CFGS series. */
ble_common_cfg_t common_cfg; /**< Global common configurations, cfg_id in @ref BLE_COMMON_CFGS series. */
ble_gap_cfg_t gap_cfg; /**< Global GAP configurations, cfg_id in @ref BLE_GAP_CFGS series. */
ble_gatts_cfg_t gatts_cfg; /**< Global GATTS configuration, cfg_id in @ref BLE_GATTS_CFGS series. */
ble_conn_cfg_t conn_cfg; /**< Connection specific configurations, cfg_id in @ref BLE_CONN_CFGS series. */
ble_common_cfg_t common_cfg; /**< Global common configurations, cfg_id in @ref BLE_COMMON_CFGS series. */
ble_gap_cfg_t gap_cfg; /**< Global GAP configurations, cfg_id in @ref BLE_GAP_CFGS series. */
ble_gatts_cfg_t gatts_cfg; /**< Global GATTS configuration, cfg_id in @ref BLE_GATTS_CFGS series. */
} ble_cfg_t;
/** @} */
@@ -408,12 +402,11 @@ typedef union {
* @retval ::NRF_ERROR_INVALID_ADDR Invalid or not sufficiently aligned pointer supplied.
* @retval ::NRF_ERROR_NO_MEM One or more of the following is true:
* - The amount of memory assigned to the SoftDevice by *p_app_ram_base is not
* large enough to fit this configuration's memory requirement. Check
* *p_app_ram_base and set the start address of the application RAM region accordingly.
* large enough to fit this configuration's memory requirement. Check *p_app_ram_base
* and set the start address of the application RAM region accordingly.
* - Dynamic part of the SoftDevice RAM region is larger then 64 kB which
* is currently not supported.
* @retval ::NRF_ERROR_RESOURCES The total number of L2CAP Channels configured using @ref sd_ble_cfg_set is too
* large.
* @retval ::NRF_ERROR_RESOURCES The total number of L2CAP Channels configured using @ref sd_ble_cfg_set is too large.
*/
SVCALL(SD_BLE_ENABLE, uint32_t, sd_ble_enable(uint32_t *p_app_ram_base));
@@ -468,12 +461,11 @@ SVCALL(SD_BLE_CFG_SET, uint32_t, sd_ble_cfg_set(uint32_t cfg_id, ble_cfg_t const
* every time SD_EVT_IRQn is raised to ensure that all available events are pulled from the BLE stack. Failure to do so
* could potentially leave events in the internal queue without the application being aware of this fact.
*
* Sizing the p_dest buffer is equally important, since the application needs to provide all the memory necessary for
* the event to be copied into application memory. If the buffer provided is not large enough to fit the entire contents
* of the event,
* Sizing the p_dest buffer is equally important, since the application needs to provide all the memory necessary for the event to
* be copied into application memory. If the buffer provided is not large enough to fit the entire contents of the event,
* @ref NRF_ERROR_DATA_SIZE will be returned and the application can then call again with a larger buffer size.
* The maximum possible event length is defined by @ref BLE_EVT_LEN_MAX. The application may also "peek" the event
* length by providing p_dest as a NULL pointer and inspecting the value of *p_len upon return:
* The maximum possible event length is defined by @ref BLE_EVT_LEN_MAX. The application may also "peek" the event length
* by providing p_dest as a NULL pointer and inspecting the value of *p_len upon return:
*
* \code
* uint16_t len;
@@ -512,8 +504,8 @@ SVCALL(SD_BLE_EVT_GET, uint32_t, sd_ble_evt_get(uint8_t *p_dest, uint16_t *p_len
*
* @param[in] p_vs_uuid Pointer to a 16-octet (128-bit) little endian Vendor Specific base UUID disregarding
* bytes 12 and 13.
* @param[out] p_uuid_type Pointer to a uint8_t where the type field in @ref ble_uuid_t corresponding to this UUID will
* be stored.
* @param[out] p_uuid_type Pointer to a uint8_t where the type field in @ref ble_uuid_t corresponding to this UUID will be
* stored.
*
* @retval ::NRF_SUCCESS Successfully added the Vendor Specific base UUID.
* @retval ::NRF_ERROR_INVALID_ADDR If p_vs_uuid or p_uuid_type is NULL or invalid.
@@ -526,13 +518,14 @@ SVCALL(SD_BLE_UUID_VS_ADD, uint32_t, sd_ble_uuid_vs_add(ble_uuid128_t const *p_v
* @details This call removes a Vendor Specific base UUID. This function allows
* the application to reuse memory allocated for Vendor Specific base UUIDs.
*
* @note Currently this function can only be called with a p_uuid_type set to @ref BLE_UUID_TYPE_UNKNOWN or the last
* added UUID type.
* @note Currently this function can only be called with a p_uuid_type set to @ref BLE_UUID_TYPE_UNKNOWN or the last added UUID
* type.
*
* @param[inout] p_uuid_type Pointer to a uint8_t where its value matches the UUID type in @ref ble_uuid_t::type to be
* removed. If the type is set to @ref BLE_UUID_TYPE_UNKNOWN, or the pointer is NULL, the last Vendor Specific base UUID
* will be removed. If the function returns successfully, the UUID type that was removed will be written back to @p
* p_uuid_type. If function returns with a failure, it contains the last type that is in use by the ATT Server.
* @param[inout] p_uuid_type Pointer to a uint8_t where its value matches the UUID type in @ref ble_uuid_t::type to be removed.
* If the type is set to @ref BLE_UUID_TYPE_UNKNOWN, or the pointer is NULL, the last Vendor Specific
* base UUID will be removed. If the function returns successfully, the UUID type that was removed will
* be written back to @p p_uuid_type. If function returns with a failure, it contains the last type that
* is in use by the ATT Server.
*
* @retval ::NRF_SUCCESS Successfully removed the Vendor Specific base UUID.
* @retval ::NRF_ERROR_INVALID_ADDR If p_uuid_type is invalid.
@@ -563,8 +556,8 @@ SVCALL(SD_BLE_UUID_DECODE, uint32_t, sd_ble_uuid_decode(uint8_t uuid_le_len, uin
/** @brief Encode a @ref ble_uuid_t structure into little endian raw UUID bytes (16-bit or 128-bit).
*
* @note The pointer to the destination buffer p_uuid_le may be NULL, in which case only the validity and size of p_uuid
* is computed.
* @note The pointer to the destination buffer p_uuid_le may be NULL, in which case only the validity and size of p_uuid is
* computed.
*
* @param[in] p_uuid Pointer to a @ref ble_uuid_t structure that will be encoded into bytes.
* @param[out] p_uuid_le_len Pointer to a uint8_t that will be filled with the encoded length (2 or 16 bytes).
+2 -2
View File
@@ -67,8 +67,8 @@ extern "C" {
#define BLE_ERROR_INVALID_ATTR_HANDLE (NRF_ERROR_STK_BASE_NUM + 0x003) /**< Invalid attribute handle. */
#define BLE_ERROR_INVALID_ADV_HANDLE (NRF_ERROR_STK_BASE_NUM + 0x004) /**< Invalid advertising handle. */
#define BLE_ERROR_INVALID_ROLE (NRF_ERROR_STK_BASE_NUM + 0x005) /**< Invalid role. */
#define BLE_ERROR_BLOCKED_BY_OTHER_LINKS \
(NRF_ERROR_STK_BASE_NUM + 0x006) /**< The attempt to change link settings failed due to the scheduling of other links. */
#define BLE_ERROR_BLOCKED_BY_OTHER_LINKS \
(NRF_ERROR_STK_BASE_NUM + 0x006) /**< The attempt to change link settings failed due to the scheduling of other links. */
/** @} */
/** @defgroup BLE_ERROR_SUBRANGES Module specific error code subranges
File diff suppressed because it is too large Load Diff
+42 -42
View File
@@ -112,34 +112,34 @@ extern "C" {
#define BLE_GATT_STATUS_ATTERR_INSUF_AUTHENTICATION 0x0105 /**< ATT Error: Authenticated link required. */
#define BLE_GATT_STATUS_ATTERR_REQUEST_NOT_SUPPORTED 0x0106 /**< ATT Error: Used in ATT as Request Not Supported. */
#define BLE_GATT_STATUS_ATTERR_INVALID_OFFSET 0x0107 /**< ATT Error: Offset specified was past the end of the attribute. */
#define BLE_GATT_STATUS_ATTERR_INSUF_AUTHORIZATION \
0x0108 /**< ATT Error: Used in ATT as Insufficient Authorization. \
*/
#define BLE_GATT_STATUS_ATTERR_PREPARE_QUEUE_FULL 0x0109 /**< ATT Error: Used in ATT as Prepare Queue Full. */
#define BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_FOUND 0x010A /**< ATT Error: Used in ATT as Attribute not found. */
#define BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_LONG 0x010B /**< ATT Error: Attribute cannot be read or written using read/write blob requests. */
#define BLE_GATT_STATUS_ATTERR_INSUF_AUTHORIZATION 0x0108 /**< ATT Error: Used in ATT as Insufficient Authorization. */
#define BLE_GATT_STATUS_ATTERR_PREPARE_QUEUE_FULL 0x0109 /**< ATT Error: Used in ATT as Prepare Queue Full. */
#define BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_FOUND 0x010A /**< ATT Error: Used in ATT as Attribute not found. */
#define BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_LONG \
0x010B /**< ATT Error: Attribute cannot be read or written using read/write blob requests. */
#define BLE_GATT_STATUS_ATTERR_INSUF_ENC_KEY_SIZE 0x010C /**< ATT Error: Encryption key size used is insufficient. */
#define BLE_GATT_STATUS_ATTERR_INVALID_ATT_VAL_LENGTH 0x010D /**< ATT Error: Invalid value size. */
#define BLE_GATT_STATUS_ATTERR_UNLIKELY_ERROR 0x010E /**< ATT Error: Very unlikely error. */
#define BLE_GATT_STATUS_ATTERR_INSUF_ENCRYPTION 0x010F /**< ATT Error: Encrypted link required. */
#define BLE_GATT_STATUS_ATTERR_UNSUPPORTED_GROUP_TYPE 0x0110 /**< ATT Error: Attribute type is not a supported grouping attribute. */
#define BLE_GATT_STATUS_ATTERR_INSUF_RESOURCES 0x0111 /**< ATT Error: Insufficient resources. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE1_BEGIN 0x0112 /**< ATT Error: Reserved for Future Use range #1 begin. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE1_END 0x017F /**< ATT Error: Reserved for Future Use range #1 end. */
#define BLE_GATT_STATUS_ATTERR_APP_BEGIN 0x0180 /**< ATT Error: Application range begin. */
#define BLE_GATT_STATUS_ATTERR_APP_END 0x019F /**< ATT Error: Application range end. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE2_BEGIN 0x01A0 /**< ATT Error: Reserved for Future Use range #2 begin. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE2_END 0x01DF /**< ATT Error: Reserved for Future Use range #2 end. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE3_BEGIN 0x01E0 /**< ATT Error: Reserved for Future Use range #3 begin. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE3_END 0x01FC /**< ATT Error: Reserved for Future Use range #3 end. */
#define BLE_GATT_STATUS_ATTERR_CPS_WRITE_REQ_REJECTED \
0x01FC /**< ATT Common Profile and Service Error: Write request rejected. \
*/
#define BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR \
0x01FD /**< ATT Common Profile and Service Error: Client Characteristic Configuration Descriptor improperly \
configured. */
#define BLE_GATT_STATUS_ATTERR_CPS_PROC_ALR_IN_PROG 0x01FE /**< ATT Common Profile and Service Error: Procedure Already in Progress. */
#define BLE_GATT_STATUS_ATTERR_CPS_OUT_OF_RANGE 0x01FF /**< ATT Common Profile and Service Error: Out Of Range. */
#define BLE_GATT_STATUS_ATTERR_UNSUPPORTED_GROUP_TYPE \
0x0110 /**< ATT Error: Attribute type is not a supported grouping attribute. */
#define BLE_GATT_STATUS_ATTERR_INSUF_RESOURCES 0x0111 /**< ATT Error: Insufficient resources. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE1_BEGIN 0x0112 /**< ATT Error: Reserved for Future Use range #1 begin. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE1_END 0x017F /**< ATT Error: Reserved for Future Use range #1 end. */
#define BLE_GATT_STATUS_ATTERR_APP_BEGIN 0x0180 /**< ATT Error: Application range begin. */
#define BLE_GATT_STATUS_ATTERR_APP_END 0x019F /**< ATT Error: Application range end. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE2_BEGIN 0x01A0 /**< ATT Error: Reserved for Future Use range #2 begin. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE2_END 0x01DF /**< ATT Error: Reserved for Future Use range #2 end. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE3_BEGIN 0x01E0 /**< ATT Error: Reserved for Future Use range #3 begin. */
#define BLE_GATT_STATUS_ATTERR_RFU_RANGE3_END 0x01FC /**< ATT Error: Reserved for Future Use range #3 end. */
#define BLE_GATT_STATUS_ATTERR_CPS_WRITE_REQ_REJECTED \
0x01FC /**< ATT Common Profile and Service Error: Write request rejected. \
*/
#define BLE_GATT_STATUS_ATTERR_CPS_CCCD_CONFIG_ERROR \
0x01FD /**< ATT Common Profile and Service Error: Client Characteristic Configuration Descriptor improperly configured. */
#define BLE_GATT_STATUS_ATTERR_CPS_PROC_ALR_IN_PROG \
0x01FE /**< ATT Common Profile and Service Error: Procedure Already in Progress. */
#define BLE_GATT_STATUS_ATTERR_CPS_OUT_OF_RANGE 0x01FF /**< ATT Common Profile and Service Error: Out Of Range. */
/** @} */
/** @defgroup BLE_GATT_CPF_FORMATS Characteristic Presentation Formats
@@ -194,32 +194,32 @@ extern "C" {
* @retval ::NRF_ERROR_INVALID_PARAM att_mtu is smaller than @ref BLE_GATT_ATT_MTU_DEFAULT.
*/
typedef struct {
uint16_t att_mtu; /**< Maximum size of ATT packet the SoftDevice can send or receive.
The default and minimum value is @ref BLE_GATT_ATT_MTU_DEFAULT.
@mscs
@mmsc{@ref BLE_GATTC_MTU_EXCHANGE}
@mmsc{@ref BLE_GATTS_MTU_EXCHANGE}
@endmscs
*/
uint16_t att_mtu; /**< Maximum size of ATT packet the SoftDevice can send or receive.
The default and minimum value is @ref BLE_GATT_ATT_MTU_DEFAULT.
@mscs
@mmsc{@ref BLE_GATTC_MTU_EXCHANGE}
@mmsc{@ref BLE_GATTS_MTU_EXCHANGE}
@endmscs
*/
} ble_gatt_conn_cfg_t;
/**@brief GATT Characteristic Properties. */
typedef struct {
/* Standard properties */
uint8_t broadcast : 1; /**< Broadcasting of the value permitted. */
uint8_t read : 1; /**< Reading the value permitted. */
uint8_t write_wo_resp : 1; /**< Writing the value with Write Command permitted. */
uint8_t write : 1; /**< Writing the value with Write Request permitted. */
uint8_t notify : 1; /**< Notification of the value permitted. */
uint8_t indicate : 1; /**< Indications of the value permitted. */
uint8_t auth_signed_wr : 1; /**< Writing the value with Signed Write Command permitted. */
/* Standard properties */
uint8_t broadcast : 1; /**< Broadcasting of the value permitted. */
uint8_t read : 1; /**< Reading the value permitted. */
uint8_t write_wo_resp : 1; /**< Writing the value with Write Command permitted. */
uint8_t write : 1; /**< Writing the value with Write Request permitted. */
uint8_t notify : 1; /**< Notification of the value permitted. */
uint8_t indicate : 1; /**< Indications of the value permitted. */
uint8_t auth_signed_wr : 1; /**< Writing the value with Signed Write Command permitted. */
} ble_gatt_char_props_t;
/**@brief GATT Characteristic Extended Properties. */
typedef struct {
/* Extended properties */
uint8_t reliable_wr : 1; /**< Writing the value with Queued Write operations permitted. */
uint8_t wr_aux : 1; /**< Writing the Characteristic User Description descriptor permitted. */
/* Extended properties */
uint8_t reliable_wr : 1; /**< Writing the value with Queued Write operations permitted. */
uint8_t wr_aux : 1; /**< Writing the Characteristic User Description descriptor permitted. */
} ble_gatt_char_ext_props_t;
/** @} */
+196 -194
View File
@@ -63,56 +63,53 @@ extern "C" {
/**@brief GATTC API SVC numbers. */
enum BLE_GATTC_SVCS {
SD_BLE_GATTC_PRIMARY_SERVICES_DISCOVER = BLE_GATTC_SVC_BASE, /**< Primary Service Discovery. */
SD_BLE_GATTC_RELATIONSHIPS_DISCOVER, /**< Relationship Discovery. */
SD_BLE_GATTC_CHARACTERISTICS_DISCOVER, /**< Characteristic Discovery. */
SD_BLE_GATTC_DESCRIPTORS_DISCOVER, /**< Characteristic Descriptor Discovery. */
SD_BLE_GATTC_ATTR_INFO_DISCOVER, /**< Attribute Information Discovery. */
SD_BLE_GATTC_CHAR_VALUE_BY_UUID_READ, /**< Read Characteristic Value by UUID. */
SD_BLE_GATTC_READ, /**< Generic read. */
SD_BLE_GATTC_CHAR_VALUES_READ, /**< Read multiple Characteristic Values. */
SD_BLE_GATTC_WRITE, /**< Generic write. */
SD_BLE_GATTC_HV_CONFIRM, /**< Handle Value Confirmation. */
SD_BLE_GATTC_EXCHANGE_MTU_REQUEST, /**< Exchange MTU Request. */
SD_BLE_GATTC_PRIMARY_SERVICES_DISCOVER = BLE_GATTC_SVC_BASE, /**< Primary Service Discovery. */
SD_BLE_GATTC_RELATIONSHIPS_DISCOVER, /**< Relationship Discovery. */
SD_BLE_GATTC_CHARACTERISTICS_DISCOVER, /**< Characteristic Discovery. */
SD_BLE_GATTC_DESCRIPTORS_DISCOVER, /**< Characteristic Descriptor Discovery. */
SD_BLE_GATTC_ATTR_INFO_DISCOVER, /**< Attribute Information Discovery. */
SD_BLE_GATTC_CHAR_VALUE_BY_UUID_READ, /**< Read Characteristic Value by UUID. */
SD_BLE_GATTC_READ, /**< Generic read. */
SD_BLE_GATTC_CHAR_VALUES_READ, /**< Read multiple Characteristic Values. */
SD_BLE_GATTC_WRITE, /**< Generic write. */
SD_BLE_GATTC_HV_CONFIRM, /**< Handle Value Confirmation. */
SD_BLE_GATTC_EXCHANGE_MTU_REQUEST, /**< Exchange MTU Request. */
};
/**
* @brief GATT Client Event IDs.
*/
enum BLE_GATTC_EVTS {
BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP = BLE_GATTC_EVT_BASE, /**< Primary Service Discovery Response event. \n See
@ref ble_gattc_evt_prim_srvc_disc_rsp_t. */
BLE_GATTC_EVT_REL_DISC_RSP, /**< Relationship Discovery Response event. \n See @ref
* ble_gattc_evt_rel_disc_rsp_t.
*/
BLE_GATTC_EVT_CHAR_DISC_RSP, /**< Characteristic Discovery Response event. \n See @ref
ble_gattc_evt_char_disc_rsp_t. */
BLE_GATTC_EVT_DESC_DISC_RSP, /**< Descriptor Discovery Response event. \n See @ref
ble_gattc_evt_desc_disc_rsp_t. */
BLE_GATTC_EVT_ATTR_INFO_DISC_RSP, /**< Attribute Information Response event. \n See @ref
ble_gattc_evt_attr_info_disc_rsp_t. */
BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP, /**< Read By UUID Response event. \n See @ref
ble_gattc_evt_char_val_by_uuid_read_rsp_t. */
BLE_GATTC_EVT_READ_RSP, /**< Read Response event. \n See @ref ble_gattc_evt_read_rsp_t.
*/
BLE_GATTC_EVT_CHAR_VALS_READ_RSP, /**< Read multiple Response event. \n See @ref
ble_gattc_evt_char_vals_read_rsp_t. */
BLE_GATTC_EVT_WRITE_RSP, /**< Write Response event. \n See @ref
ble_gattc_evt_write_rsp_t. */
BLE_GATTC_EVT_HVX, /**< Handle Value Notification or Indication event. \n Confirm indication with @ref
sd_ble_gattc_hv_confirm. \n See @ref ble_gattc_evt_hvx_t. */
BLE_GATTC_EVT_EXCHANGE_MTU_RSP, /**< Exchange MTU Response event. \n See @ref
ble_gattc_evt_exchange_mtu_rsp_t. */
BLE_GATTC_EVT_TIMEOUT, /**< Timeout event. \n See @ref ble_gattc_evt_timeout_t. */
BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE /**< Write without Response transmission complete. \n See @ref
ble_gattc_evt_write_cmd_tx_complete_t. */
BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP = BLE_GATTC_EVT_BASE, /**< Primary Service Discovery Response event. \n See @ref
ble_gattc_evt_prim_srvc_disc_rsp_t. */
BLE_GATTC_EVT_REL_DISC_RSP, /**< Relationship Discovery Response event. \n See @ref ble_gattc_evt_rel_disc_rsp_t.
*/
BLE_GATTC_EVT_CHAR_DISC_RSP, /**< Characteristic Discovery Response event. \n See @ref
ble_gattc_evt_char_disc_rsp_t. */
BLE_GATTC_EVT_DESC_DISC_RSP, /**< Descriptor Discovery Response event. \n See @ref
ble_gattc_evt_desc_disc_rsp_t. */
BLE_GATTC_EVT_ATTR_INFO_DISC_RSP, /**< Attribute Information Response event. \n See @ref
ble_gattc_evt_attr_info_disc_rsp_t. */
BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP, /**< Read By UUID Response event. \n See @ref
ble_gattc_evt_char_val_by_uuid_read_rsp_t. */
BLE_GATTC_EVT_READ_RSP, /**< Read Response event. \n See @ref ble_gattc_evt_read_rsp_t. */
BLE_GATTC_EVT_CHAR_VALS_READ_RSP, /**< Read multiple Response event. \n See @ref
ble_gattc_evt_char_vals_read_rsp_t. */
BLE_GATTC_EVT_WRITE_RSP, /**< Write Response event. \n See @ref ble_gattc_evt_write_rsp_t. */
BLE_GATTC_EVT_HVX, /**< Handle Value Notification or Indication event. \n Confirm indication with @ref
sd_ble_gattc_hv_confirm. \n See @ref ble_gattc_evt_hvx_t. */
BLE_GATTC_EVT_EXCHANGE_MTU_RSP, /**< Exchange MTU Response event. \n See @ref
ble_gattc_evt_exchange_mtu_rsp_t. */
BLE_GATTC_EVT_TIMEOUT, /**< Timeout event. \n See @ref ble_gattc_evt_timeout_t. */
BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE /**< Write without Response transmission complete. \n See @ref
ble_gattc_evt_write_cmd_tx_complete_t. */
};
/**@brief GATTC Option IDs.
* IDs that uniquely identify a GATTC option.
*/
enum BLE_GATTC_OPTS {
BLE_GATTC_OPT_UUID_DISC = BLE_GATTC_OPT_BASE, /**< UUID discovery. @ref ble_gattc_opt_uuid_disc_t */
BLE_GATTC_OPT_UUID_DISC = BLE_GATTC_OPT_BASE, /**< UUID discovery. @ref ble_gattc_opt_uuid_disc_t */
};
/** @} */
@@ -133,7 +130,8 @@ enum BLE_GATTC_OPTS {
/** @defgroup BLE_GATTC_DEFAULTS GATT Client defaults
* @{ */
#define BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT 1 /**< Default number of Write without Response that can be queued for transmission. */
#define BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT \
1 /**< Default number of Write without Response that can be queued for transmission. */
/** @} */
/** @} */
@@ -145,206 +143,208 @@ enum BLE_GATTC_OPTS {
* @brief BLE GATTC connection configuration parameters, set with @ref sd_ble_cfg_set.
*/
typedef struct {
uint8_t write_cmd_tx_queue_size; /**< The guaranteed minimum number of Write without Response that can be queued for
transmission. The default value is @ref BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT */
uint8_t write_cmd_tx_queue_size; /**< The guaranteed minimum number of Write without Response that can be queued for
transmission. The default value is @ref BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT */
} ble_gattc_conn_cfg_t;
/**@brief Operation Handle Range. */
typedef struct {
uint16_t start_handle; /**< Start Handle. */
uint16_t end_handle; /**< End Handle. */
uint16_t start_handle; /**< Start Handle. */
uint16_t end_handle; /**< End Handle. */
} ble_gattc_handle_range_t;
/**@brief GATT service. */
typedef struct {
ble_uuid_t uuid; /**< Service UUID. */
ble_gattc_handle_range_t handle_range; /**< Service Handle Range. */
ble_uuid_t uuid; /**< Service UUID. */
ble_gattc_handle_range_t handle_range; /**< Service Handle Range. */
} ble_gattc_service_t;
/**@brief GATT include. */
typedef struct {
uint16_t handle; /**< Include Handle. */
ble_gattc_service_t included_srvc; /**< Handle of the included service. */
uint16_t handle; /**< Include Handle. */
ble_gattc_service_t included_srvc; /**< Handle of the included service. */
} ble_gattc_include_t;
/**@brief GATT characteristic. */
typedef struct {
ble_uuid_t uuid; /**< Characteristic UUID. */
ble_gatt_char_props_t char_props; /**< Characteristic Properties. */
uint8_t char_ext_props : 1; /**< Extended properties present. */
uint16_t handle_decl; /**< Handle of the Characteristic Declaration. */
uint16_t handle_value; /**< Handle of the Characteristic Value. */
ble_uuid_t uuid; /**< Characteristic UUID. */
ble_gatt_char_props_t char_props; /**< Characteristic Properties. */
uint8_t char_ext_props : 1; /**< Extended properties present. */
uint16_t handle_decl; /**< Handle of the Characteristic Declaration. */
uint16_t handle_value; /**< Handle of the Characteristic Value. */
} ble_gattc_char_t;
/**@brief GATT descriptor. */
typedef struct {
uint16_t handle; /**< Descriptor Handle. */
ble_uuid_t uuid; /**< Descriptor UUID. */
uint16_t handle; /**< Descriptor Handle. */
ble_uuid_t uuid; /**< Descriptor UUID. */
} ble_gattc_desc_t;
/**@brief Write Parameters. */
typedef struct {
uint8_t write_op; /**< Write Operation to be performed, see @ref BLE_GATT_WRITE_OPS. */
uint8_t flags; /**< Flags, see @ref BLE_GATT_EXEC_WRITE_FLAGS. */
uint16_t handle; /**< Handle to the attribute to be written. */
uint16_t offset; /**< Offset in bytes. @note For WRITE_CMD and WRITE_REQ, offset must be 0. */
uint16_t len; /**< Length of data in bytes. */
uint8_t const *p_value; /**< Pointer to the value data. */
uint8_t write_op; /**< Write Operation to be performed, see @ref BLE_GATT_WRITE_OPS. */
uint8_t flags; /**< Flags, see @ref BLE_GATT_EXEC_WRITE_FLAGS. */
uint16_t handle; /**< Handle to the attribute to be written. */
uint16_t offset; /**< Offset in bytes. @note For WRITE_CMD and WRITE_REQ, offset must be 0. */
uint16_t len; /**< Length of data in bytes. */
uint8_t const *p_value; /**< Pointer to the value data. */
} ble_gattc_write_params_t;
/**@brief Attribute Information for 16-bit Attribute UUID. */
typedef struct {
uint16_t handle; /**< Attribute handle. */
ble_uuid_t uuid; /**< 16-bit Attribute UUID. */
uint16_t handle; /**< Attribute handle. */
ble_uuid_t uuid; /**< 16-bit Attribute UUID. */
} ble_gattc_attr_info16_t;
/**@brief Attribute Information for 128-bit Attribute UUID. */
typedef struct {
uint16_t handle; /**< Attribute handle. */
ble_uuid128_t uuid; /**< 128-bit Attribute UUID. */
uint16_t handle; /**< Attribute handle. */
ble_uuid128_t uuid; /**< 128-bit Attribute UUID. */
} ble_gattc_attr_info128_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP. */
typedef struct {
uint16_t count; /**< Service count. */
ble_gattc_service_t services[1]; /**< Service data. @note This is a variable length array. The size of 1 indicated is
only a placeholder for compilation. See @ref sd_ble_evt_get for more information
on how to use event structures with variable length array members. */
uint16_t count; /**< Service count. */
ble_gattc_service_t services[1]; /**< Service data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use
event structures with variable length array members. */
} ble_gattc_evt_prim_srvc_disc_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_REL_DISC_RSP. */
typedef struct {
uint16_t count; /**< Include count. */
ble_gattc_include_t includes[1]; /**< Include data. @note This is a variable length array. The size of 1 indicated is
only a placeholder for compilation. See @ref sd_ble_evt_get for more information
on how to use event structures with variable length array members. */
uint16_t count; /**< Include count. */
ble_gattc_include_t includes[1]; /**< Include data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use
event structures with variable length array members. */
} ble_gattc_evt_rel_disc_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_CHAR_DISC_RSP. */
typedef struct {
uint16_t count; /**< Characteristic count. */
ble_gattc_char_t chars[1]; /**< Characteristic data. @note This is a variable length array. The size of 1 indicated is
only a placeholder for compilation. See @ref sd_ble_evt_get for more information on how
to use event structures with variable length array members. */
uint16_t count; /**< Characteristic count. */
ble_gattc_char_t chars[1]; /**< Characteristic data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use event
structures with variable length array members. */
} ble_gattc_evt_char_disc_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_DESC_DISC_RSP. */
typedef struct {
uint16_t count; /**< Descriptor count. */
ble_gattc_desc_t descs[1]; /**< Descriptor data. @note This is a variable length array. The size of 1 indicated is
only a placeholder for compilation. See @ref sd_ble_evt_get for more information on how
to use event structures with variable length array members. */
uint16_t count; /**< Descriptor count. */
ble_gattc_desc_t descs[1]; /**< Descriptor data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use event
structures with variable length array members. */
} ble_gattc_evt_desc_disc_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_ATTR_INFO_DISC_RSP. */
typedef struct {
uint16_t count; /**< Attribute count. */
uint8_t format; /**< Attribute information format, see @ref BLE_GATTC_ATTR_INFO_FORMAT. */
union {
ble_gattc_attr_info16_t attr_info16[1]; /**< Attribute information for 16-bit Attribute UUID.
@note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on
how to use event structures with variable length array members. */
ble_gattc_attr_info128_t attr_info128[1]; /**< Attribute information for 128-bit Attribute UUID.
@note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on
how to use event structures with variable length array members. */
} info; /**< Attribute information union. */
uint16_t count; /**< Attribute count. */
uint8_t format; /**< Attribute information format, see @ref BLE_GATTC_ATTR_INFO_FORMAT. */
union {
ble_gattc_attr_info16_t attr_info16[1]; /**< Attribute information for 16-bit Attribute UUID.
@note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on
how to use event structures with variable length array members. */
ble_gattc_attr_info128_t attr_info128[1]; /**< Attribute information for 128-bit Attribute UUID.
@note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on
how to use event structures with variable length array members. */
} info; /**< Attribute information union. */
} ble_gattc_evt_attr_info_disc_rsp_t;
/**@brief GATT read by UUID handle value pair. */
typedef struct {
uint16_t handle; /**< Attribute Handle. */
uint8_t *p_value; /**< Pointer to the Attribute Value, length is available in @ref
ble_gattc_evt_char_val_by_uuid_read_rsp_t::value_len. */
uint16_t handle; /**< Attribute Handle. */
uint8_t *p_value; /**< Pointer to the Attribute Value, length is available in @ref
ble_gattc_evt_char_val_by_uuid_read_rsp_t::value_len. */
} ble_gattc_handle_value_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP. */
typedef struct {
uint16_t count; /**< Handle-Value Pair Count. */
uint16_t value_len; /**< Length of the value in Handle-Value(s) list. */
uint8_t handle_value[1]; /**< Handle-Value(s) list. To iterate through the list use @ref
sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter.
@note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures
with variable length array members. */
uint16_t count; /**< Handle-Value Pair Count. */
uint16_t value_len; /**< Length of the value in Handle-Value(s) list. */
uint8_t handle_value[1]; /**< Handle-Value(s) list. To iterate through the list use @ref
sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter.
@note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures with
variable length array members. */
} ble_gattc_evt_char_val_by_uuid_read_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_READ_RSP. */
typedef struct {
uint16_t handle; /**< Attribute Handle. */
uint16_t offset; /**< Offset of the attribute data. */
uint16_t len; /**< Attribute data length. */
uint8_t data[1]; /**< Attribute data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use event
structures with variable length array members. */
uint16_t handle; /**< Attribute Handle. */
uint16_t offset; /**< Offset of the attribute data. */
uint16_t len; /**< Attribute data length. */
uint8_t data[1]; /**< Attribute data. @note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures with variable
length array members. */
} ble_gattc_evt_read_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_CHAR_VALS_READ_RSP. */
typedef struct {
uint16_t len; /**< Concatenated Attribute values length. */
uint8_t values[1]; /**< Attribute values. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use event
structures with variable length array members. */
uint16_t len; /**< Concatenated Attribute values length. */
uint8_t values[1]; /**< Attribute values. @note This is a variable length array. The size of 1 indicated is only a placeholder
for compilation. See @ref sd_ble_evt_get for more information on how to use event structures with
variable length array members. */
} ble_gattc_evt_char_vals_read_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_WRITE_RSP. */
typedef struct {
uint16_t handle; /**< Attribute Handle. */
uint8_t write_op; /**< Type of write operation, see @ref BLE_GATT_WRITE_OPS. */
uint16_t offset; /**< Data offset. */
uint16_t len; /**< Data length. */
uint8_t data[1]; /**< Data. @note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures with
variable length array members. */
uint16_t handle; /**< Attribute Handle. */
uint8_t write_op; /**< Type of write operation, see @ref BLE_GATT_WRITE_OPS. */
uint16_t offset; /**< Data offset. */
uint16_t len; /**< Data length. */
uint8_t data[1]; /**< Data. @note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures with variable
length array members. */
} ble_gattc_evt_write_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_HVX. */
typedef struct {
uint16_t handle; /**< Handle to which the HVx operation applies. */
uint8_t type; /**< Indication or Notification, see @ref BLE_GATT_HVX_TYPES. */
uint16_t len; /**< Attribute data length. */
uint8_t data[1]; /**< Attribute data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use event
structures with variable length array members. */
uint16_t handle; /**< Handle to which the HVx operation applies. */
uint8_t type; /**< Indication or Notification, see @ref BLE_GATT_HVX_TYPES. */
uint16_t len; /**< Attribute data length. */
uint8_t data[1]; /**< Attribute data. @note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures with variable
length array members. */
} ble_gattc_evt_hvx_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_EXCHANGE_MTU_RSP. */
typedef struct {
uint16_t server_rx_mtu; /**< Server RX MTU size. */
uint16_t server_rx_mtu; /**< Server RX MTU size. */
} ble_gattc_evt_exchange_mtu_rsp_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_TIMEOUT. */
typedef struct {
uint8_t src; /**< Timeout source, see @ref BLE_GATT_TIMEOUT_SOURCES. */
uint8_t src; /**< Timeout source, see @ref BLE_GATT_TIMEOUT_SOURCES. */
} ble_gattc_evt_timeout_t;
/**@brief Event structure for @ref BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE. */
typedef struct {
uint8_t count; /**< Number of write without response transmissions completed. */
uint8_t count; /**< Number of write without response transmissions completed. */
} ble_gattc_evt_write_cmd_tx_complete_t;
/**@brief GATTC event structure. */
typedef struct {
uint16_t conn_handle; /**< Connection Handle on which event occurred. */
uint16_t gatt_status; /**< GATT status code for the operation, see @ref BLE_GATT_STATUS_CODES. */
uint16_t error_handle; /**< In case of error: The handle causing the error. In all other cases @ref
BLE_GATT_HANDLE_INVALID. */
union {
ble_gattc_evt_prim_srvc_disc_rsp_t prim_srvc_disc_rsp; /**< Primary Service Discovery Response Event Parameters. */
ble_gattc_evt_rel_disc_rsp_t rel_disc_rsp; /**< Relationship Discovery Response Event Parameters. */
ble_gattc_evt_char_disc_rsp_t char_disc_rsp; /**< Characteristic Discovery Response Event Parameters. */
ble_gattc_evt_desc_disc_rsp_t desc_disc_rsp; /**< Descriptor Discovery Response Event Parameters. */
ble_gattc_evt_char_val_by_uuid_read_rsp_t char_val_by_uuid_read_rsp; /**< Characteristic Value Read by UUID Response Event Parameters. */
ble_gattc_evt_read_rsp_t read_rsp; /**< Read Response Event Parameters. */
ble_gattc_evt_char_vals_read_rsp_t char_vals_read_rsp; /**< Characteristic Values Read Response Event Parameters. */
ble_gattc_evt_write_rsp_t write_rsp; /**< Write Response Event Parameters. */
ble_gattc_evt_hvx_t hvx; /**< Handle Value Notification/Indication Event Parameters. */
ble_gattc_evt_exchange_mtu_rsp_t exchange_mtu_rsp; /**< Exchange MTU Response Event Parameters. */
ble_gattc_evt_timeout_t timeout; /**< Timeout Event Parameters. */
ble_gattc_evt_attr_info_disc_rsp_t attr_info_disc_rsp; /**< Attribute Information Discovery Event Parameters. */
ble_gattc_evt_write_cmd_tx_complete_t write_cmd_tx_complete; /**< Write without Response transmission complete Event Parameters. */
} params; /**< Event Parameters. @note Only valid if @ref gatt_status == @ref BLE_GATT_STATUS_SUCCESS. */
uint16_t conn_handle; /**< Connection Handle on which event occurred. */
uint16_t gatt_status; /**< GATT status code for the operation, see @ref BLE_GATT_STATUS_CODES. */
uint16_t
error_handle; /**< In case of error: The handle causing the error. In all other cases @ref BLE_GATT_HANDLE_INVALID. */
union {
ble_gattc_evt_prim_srvc_disc_rsp_t prim_srvc_disc_rsp; /**< Primary Service Discovery Response Event Parameters. */
ble_gattc_evt_rel_disc_rsp_t rel_disc_rsp; /**< Relationship Discovery Response Event Parameters. */
ble_gattc_evt_char_disc_rsp_t char_disc_rsp; /**< Characteristic Discovery Response Event Parameters. */
ble_gattc_evt_desc_disc_rsp_t desc_disc_rsp; /**< Descriptor Discovery Response Event Parameters. */
ble_gattc_evt_char_val_by_uuid_read_rsp_t
char_val_by_uuid_read_rsp; /**< Characteristic Value Read by UUID Response Event Parameters. */
ble_gattc_evt_read_rsp_t read_rsp; /**< Read Response Event Parameters. */
ble_gattc_evt_char_vals_read_rsp_t char_vals_read_rsp; /**< Characteristic Values Read Response Event Parameters. */
ble_gattc_evt_write_rsp_t write_rsp; /**< Write Response Event Parameters. */
ble_gattc_evt_hvx_t hvx; /**< Handle Value Notification/Indication Event Parameters. */
ble_gattc_evt_exchange_mtu_rsp_t exchange_mtu_rsp; /**< Exchange MTU Response Event Parameters. */
ble_gattc_evt_timeout_t timeout; /**< Timeout Event Parameters. */
ble_gattc_evt_attr_info_disc_rsp_t attr_info_disc_rsp; /**< Attribute Information Discovery Event Parameters. */
ble_gattc_evt_write_cmd_tx_complete_t
write_cmd_tx_complete; /**< Write without Response transmission complete Event Parameters. */
} params; /**< Event Parameters. @note Only valid if @ref gatt_status == @ref BLE_GATT_STATUS_SUCCESS. */
} ble_gattc_evt_t;
/**@brief UUID discovery option.
@@ -370,13 +370,12 @@ typedef struct {
*
*/
typedef struct {
uint8_t auto_add_vs_enable : 1; /**< Set to 1 to enable (or 0 to disable) automatic insertion of discovered 128-bit
UUIDs. */
uint8_t auto_add_vs_enable : 1; /**< Set to 1 to enable (or 0 to disable) automatic insertion of discovered 128-bit UUIDs. */
} ble_gattc_opt_uuid_disc_t;
/**@brief Option structure for GATTC options. */
typedef union {
ble_gattc_opt_uuid_disc_t uuid_disc; /**< Parameters for the UUID discovery option. */
ble_gattc_opt_uuid_disc_t uuid_disc; /**< Parameters for the UUID discovery option. */
} ble_gattc_opt_t;
/** @} */
@@ -387,8 +386,8 @@ typedef union {
/**@brief Initiate or continue a GATT Primary Service Discovery procedure.
*
* @details This function initiates or resumes a Primary Service discovery procedure, starting from the supplied handle.
* If the last service has not been reached, this function must be called again with an updated start handle
* value to continue the search. See also @ref ble_gattc_opt_uuid_disc_t.
* If the last service has not been reached, this function must be called again with an updated start handle value to
* continue the search. See also @ref ble_gattc_opt_uuid_disc_t.
*
* @events
* @event{@ref BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP}
@@ -415,8 +414,8 @@ SVCALL(SD_BLE_GATTC_PRIMARY_SERVICES_DISCOVER, uint32_t,
/**@brief Initiate or continue a GATT Relationship Discovery procedure.
*
* @details This function initiates or resumes the Find Included Services sub-procedure. If the last included service
* has not been reached, this must be called again with an updated handle range to continue the search. See also @ref
* @details This function initiates or resumes the Find Included Services sub-procedure. If the last included service has not been
* reached, this must be called again with an updated handle range to continue the search. See also @ref
* ble_gattc_opt_uuid_disc_t.
*
* @events
@@ -444,8 +443,8 @@ SVCALL(SD_BLE_GATTC_RELATIONSHIPS_DISCOVER, uint32_t,
/**@brief Initiate or continue a GATT Characteristic Discovery procedure.
*
* @details This function initiates or resumes a Characteristic discovery procedure. If the last Characteristic has not
* been reached, this must be called again with an updated handle range to continue the discovery. See also @ref
* @details This function initiates or resumes a Characteristic discovery procedure. If the last Characteristic has not been
* reached, this must be called again with an updated handle range to continue the discovery. See also @ref
* ble_gattc_opt_uuid_disc_t.
*
* @events
@@ -472,8 +471,8 @@ SVCALL(SD_BLE_GATTC_CHARACTERISTICS_DISCOVER, uint32_t,
/**@brief Initiate or continue a GATT Characteristic Descriptor Discovery procedure.
*
* @details This function initiates or resumes a Characteristic Descriptor discovery procedure. If the last Descriptor
* has not been reached, this must be called again with an updated handle range to continue the discovery. See also @ref
* @details This function initiates or resumes a Characteristic Descriptor discovery procedure. If the last Descriptor has not
* been reached, this must be called again with an updated handle range to continue the discovery. See also @ref
* ble_gattc_opt_uuid_disc_t.
*
* @events
@@ -500,8 +499,8 @@ SVCALL(SD_BLE_GATTC_DESCRIPTORS_DISCOVER, uint32_t,
/**@brief Initiate or continue a GATT Read using Characteristic UUID procedure.
*
* @details This function initiates or resumes a Read using Characteristic UUID procedure. If the last Characteristic
* has not been reached, this must be called again with an updated handle range to continue the discovery.
* @details This function initiates or resumes a Read using Characteristic UUID procedure. If the last Characteristic has not been
* reached, this must be called again with an updated handle range to continue the discovery.
*
* @events
* @event{@ref BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP}
@@ -524,13 +523,14 @@ SVCALL(SD_BLE_GATTC_DESCRIPTORS_DISCOVER, uint32_t,
* reestablishing the connection.
*/
SVCALL(SD_BLE_GATTC_CHAR_VALUE_BY_UUID_READ, uint32_t,
sd_ble_gattc_char_value_by_uuid_read(uint16_t conn_handle, ble_uuid_t const *p_uuid, ble_gattc_handle_range_t const *p_handle_range));
sd_ble_gattc_char_value_by_uuid_read(uint16_t conn_handle, ble_uuid_t const *p_uuid,
ble_gattc_handle_range_t const *p_handle_range));
/**@brief Initiate or continue a GATT Read (Long) Characteristic or Descriptor procedure.
*
* @details This function initiates or resumes a GATT Read (Long) Characteristic or Descriptor procedure. If the
* Characteristic or Descriptor to be read is longer than ATT_MTU - 1, this function must be called multiple times with
* appropriate offset to read the complete value.
* @details This function initiates or resumes a GATT Read (Long) Characteristic or Descriptor procedure. If the Characteristic or
* Descriptor to be read is longer than ATT_MTU - 1, this function must be called multiple times with appropriate offset to read
* the complete value.
*
* @events
* @event{@ref BLE_GATTC_EVT_READ_RSP}
@@ -580,8 +580,8 @@ SVCALL(SD_BLE_GATTC_READ, uint32_t, sd_ble_gattc_read(uint16_t conn_handle, uint
SVCALL(SD_BLE_GATTC_CHAR_VALUES_READ, uint32_t,
sd_ble_gattc_char_values_read(uint16_t conn_handle, uint16_t const *p_handles, uint16_t handle_count));
/**@brief Perform a Write (Characteristic Value or Descriptor, with or without response, signed or not, long or
* reliable) procedure.
/**@brief Perform a Write (Characteristic Value or Descriptor, with or without response, signed or not, long or reliable)
* procedure.
*
* @details This function can perform all write procedures described in GATT.
*
@@ -591,17 +591,17 @@ SVCALL(SD_BLE_GATTC_CHAR_VALUES_READ, uint32_t,
* A @ref BLE_GATTC_EVT_WRITE_RSP event will be issued as soon as the write response arrives from the peer.
*
* @note The number of Write without Response that can be queued is configured by @ref
* ble_gattc_conn_cfg_t::write_cmd_tx_queue_size When the queue is full, the function call will return @ref
* NRF_ERROR_RESOURCES. A @ref BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event will be issued as soon as the transmission of
* the write without response is complete.
* ble_gattc_conn_cfg_t::write_cmd_tx_queue_size When the queue is full, the function call will return @ref NRF_ERROR_RESOURCES.
* A @ref BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event will be issued as soon as the transmission of the write without
* response is complete.
*
* @note The application can keep track of the available queue element count for writes without responses by
* following the procedure below:
* @note The application can keep track of the available queue element count for writes without responses by following the
* procedure below:
* - Store initial queue element count in a variable.
* - Decrement the variable, which stores the currently available queue element count, by one when a call to
* this function returns @ref NRF_SUCCESS.
* - Increment the variable, which stores the current available queue element count, by the count variable in
* @ref BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event.
* - Decrement the variable, which stores the currently available queue element count, by one when a call to this
* function returns @ref NRF_SUCCESS.
* - Increment the variable, which stores the current available queue element count, by the count variable in @ref
* BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event.
*
* @events
* @event{@ref BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE, Write without response transmission complete.}
@@ -624,8 +624,8 @@ SVCALL(SD_BLE_GATTC_CHAR_VALUES_READ, uint32_t,
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied.
* @retval ::NRF_ERROR_BUSY For write with response, procedure already in progress. Wait for a @ref
* BLE_GATTC_EVT_WRITE_RSP event and retry.
* @retval ::NRF_ERROR_BUSY For write with response, procedure already in progress. Wait for a @ref BLE_GATTC_EVT_WRITE_RSP event
* and retry.
* @retval ::NRF_ERROR_RESOURCES Too many writes without responses queued.
* Wait for a @ref BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE event and retry.
* @retval ::NRF_ERROR_TIMEOUT There has been a GATT procedure timeout. No new GATT procedure can be performed without
@@ -654,8 +654,7 @@ SVCALL(SD_BLE_GATTC_HV_CONFIRM, uint32_t, sd_ble_gattc_hv_confirm(uint16_t conn_
/**@brief Discovers information about a range of attributes on a GATT server.
*
* @events
* @event{@ref BLE_GATTC_EVT_ATTR_INFO_DISC_RSP, Generated when information about a range of attributes has been
* received.}
* @event{@ref BLE_GATTC_EVT_ATTR_INFO_DISC_RSP, Generated when information about a range of attributes has been received.}
* @endevents
*
* @param[in] conn_handle The connection handle identifying the connection to perform this procedure on.
@@ -693,8 +692,7 @@ SVCALL(SD_BLE_GATTC_ATTR_INFO_DISCOVER, uint32_t,
* - The minimum value is @ref BLE_GATT_ATT_MTU_DEFAULT.
* - The maximum value is @ref ble_gatt_conn_cfg_t::att_mtu in the connection configuration
used for this connection.
* - The value must be equal to Server RX MTU size given in @ref
sd_ble_gatts_exchange_mtu_reply
* - The value must be equal to Server RX MTU size given in @ref sd_ble_gatts_exchange_mtu_reply
* if an ATT_MTU exchange has already been performed in the other direction.
*
* @retval ::NRF_SUCCESS Successfully sent request to the server.
@@ -705,7 +703,8 @@ SVCALL(SD_BLE_GATTC_ATTR_INFO_DISCOVER, uint32_t,
* @retval ::NRF_ERROR_TIMEOUT There has been a GATT procedure timeout. No new GATT procedure can be performed without
reestablishing the connection.
*/
SVCALL(SD_BLE_GATTC_EXCHANGE_MTU_REQUEST, uint32_t, sd_ble_gattc_exchange_mtu_request(uint16_t conn_handle, uint16_t client_rx_mtu));
SVCALL(SD_BLE_GATTC_EXCHANGE_MTU_REQUEST, uint32_t,
sd_ble_gattc_exchange_mtu_request(uint16_t conn_handle, uint16_t client_rx_mtu));
/**@brief Iterate through Handle-Value(s) list in @ref BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP event.
*
@@ -730,24 +729,27 @@ SVCALL(SD_BLE_GATTC_EXCHANGE_MTU_REQUEST, uint32_t, sd_ble_gattc_exchange_mtu_re
* @retval ::NRF_SUCCESS Successfully retrieved the next Handle-Value pair.
* @retval ::NRF_ERROR_NOT_FOUND No more Handle-Value pairs available in the list.
*/
__STATIC_INLINE uint32_t sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter(ble_gattc_evt_t *p_gattc_evt, ble_gattc_handle_value_t *p_iter);
__STATIC_INLINE uint32_t sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter(ble_gattc_evt_t *p_gattc_evt,
ble_gattc_handle_value_t *p_iter);
/** @} */
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE uint32_t sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter(ble_gattc_evt_t *p_gattc_evt, ble_gattc_handle_value_t *p_iter) {
uint32_t value_len = p_gattc_evt->params.char_val_by_uuid_read_rsp.value_len;
uint8_t *p_first = p_gattc_evt->params.char_val_by_uuid_read_rsp.handle_value;
uint8_t *p_next = p_iter->p_value ? p_iter->p_value + value_len : p_first;
__STATIC_INLINE uint32_t sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter(ble_gattc_evt_t *p_gattc_evt,
ble_gattc_handle_value_t *p_iter)
{
uint32_t value_len = p_gattc_evt->params.char_val_by_uuid_read_rsp.value_len;
uint8_t *p_first = p_gattc_evt->params.char_val_by_uuid_read_rsp.handle_value;
uint8_t *p_next = p_iter->p_value ? p_iter->p_value + value_len : p_first;
if ((p_next - p_first) / (sizeof(uint16_t) + value_len) < p_gattc_evt->params.char_val_by_uuid_read_rsp.count) {
p_iter->handle = (uint16_t)p_next[1] << 8 | p_next[0];
p_iter->p_value = p_next + sizeof(uint16_t);
return NRF_SUCCESS;
} else {
return NRF_ERROR_NOT_FOUND;
}
if ((p_next - p_first) / (sizeof(uint16_t) + value_len) < p_gattc_evt->params.char_val_by_uuid_read_rsp.count) {
p_iter->handle = (uint16_t)p_next[1] << 8 | p_next[0];
p_iter->p_value = p_next + sizeof(uint16_t);
return NRF_SUCCESS;
} else {
return NRF_ERROR_NOT_FOUND;
}
}
#endif /* SUPPRESS_INLINE_IMPLEMENTATION */
+263 -273
View File
@@ -66,51 +66,45 @@ extern "C" {
* @brief GATTS API SVC numbers.
*/
enum BLE_GATTS_SVCS {
SD_BLE_GATTS_SERVICE_ADD = BLE_GATTS_SVC_BASE, /**< Add a service. */
SD_BLE_GATTS_INCLUDE_ADD, /**< Add an included service. */
SD_BLE_GATTS_CHARACTERISTIC_ADD, /**< Add a characteristic. */
SD_BLE_GATTS_DESCRIPTOR_ADD, /**< Add a generic attribute. */
SD_BLE_GATTS_VALUE_SET, /**< Set an attribute value. */
SD_BLE_GATTS_VALUE_GET, /**< Get an attribute value. */
SD_BLE_GATTS_HVX, /**< Handle Value Notification or Indication. */
SD_BLE_GATTS_SERVICE_CHANGED, /**< Perform a Service Changed Indication to one or more peers. */
SD_BLE_GATTS_RW_AUTHORIZE_REPLY, /**< Reply to an authorization request for a read or write operation on one or more
attributes. */
SD_BLE_GATTS_SYS_ATTR_SET, /**< Set the persistent system attributes for a connection. */
SD_BLE_GATTS_SYS_ATTR_GET, /**< Retrieve the persistent system attributes. */
SD_BLE_GATTS_INITIAL_USER_HANDLE_GET, /**< Retrieve the first valid user handle. */
SD_BLE_GATTS_ATTR_GET, /**< Retrieve the UUID and/or metadata of an attribute. */
SD_BLE_GATTS_EXCHANGE_MTU_REPLY /**< Reply to Exchange MTU Request. */
SD_BLE_GATTS_SERVICE_ADD = BLE_GATTS_SVC_BASE, /**< Add a service. */
SD_BLE_GATTS_INCLUDE_ADD, /**< Add an included service. */
SD_BLE_GATTS_CHARACTERISTIC_ADD, /**< Add a characteristic. */
SD_BLE_GATTS_DESCRIPTOR_ADD, /**< Add a generic attribute. */
SD_BLE_GATTS_VALUE_SET, /**< Set an attribute value. */
SD_BLE_GATTS_VALUE_GET, /**< Get an attribute value. */
SD_BLE_GATTS_HVX, /**< Handle Value Notification or Indication. */
SD_BLE_GATTS_SERVICE_CHANGED, /**< Perform a Service Changed Indication to one or more peers. */
SD_BLE_GATTS_RW_AUTHORIZE_REPLY, /**< Reply to an authorization request for a read or write operation on one or more
attributes. */
SD_BLE_GATTS_SYS_ATTR_SET, /**< Set the persistent system attributes for a connection. */
SD_BLE_GATTS_SYS_ATTR_GET, /**< Retrieve the persistent system attributes. */
SD_BLE_GATTS_INITIAL_USER_HANDLE_GET, /**< Retrieve the first valid user handle. */
SD_BLE_GATTS_ATTR_GET, /**< Retrieve the UUID and/or metadata of an attribute. */
SD_BLE_GATTS_EXCHANGE_MTU_REPLY /**< Reply to Exchange MTU Request. */
};
/**
* @brief GATT Server Event IDs.
*/
enum BLE_GATTS_EVTS {
BLE_GATTS_EVT_WRITE = BLE_GATTS_EVT_BASE, /**< Write operation performed. \n See
@ref ble_gatts_evt_write_t. */
BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST, /**< Read/Write Authorization request. \n Reply
with
@ref sd_ble_gatts_rw_authorize_reply. \n See @ref
ble_gatts_evt_rw_authorize_request_t.
*/
BLE_GATTS_EVT_SYS_ATTR_MISSING, /**< A persistent system attribute access is pending. \n Respond
with @ref sd_ble_gatts_sys_attr_set. \n See @ref
ble_gatts_evt_sys_attr_missing_t. */
BLE_GATTS_EVT_HVC, /**< Handle Value Confirmation. \n See @ref
* ble_gatts_evt_hvc_t.
*/
BLE_GATTS_EVT_SC_CONFIRM, /**< Service Changed Confirmation. \n No additional
event structure applies. */
BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST, /**< Exchange MTU Request. \n Reply
with
@ref sd_ble_gatts_exchange_mtu_reply. \n See @ref
ble_gatts_evt_exchange_mtu_request_t.
*/
BLE_GATTS_EVT_TIMEOUT, /**< Peer failed to respond to an ATT request in time. \n See @ref
ble_gatts_evt_timeout_t. */
BLE_GATTS_EVT_HVN_TX_COMPLETE /**< Handle Value Notification transmission complete. \n See @ref
ble_gatts_evt_hvn_tx_complete_t. */
BLE_GATTS_EVT_WRITE = BLE_GATTS_EVT_BASE, /**< Write operation performed. \n See
@ref ble_gatts_evt_write_t. */
BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST, /**< Read/Write Authorization request. \n Reply with
@ref sd_ble_gatts_rw_authorize_reply. \n See @ref ble_gatts_evt_rw_authorize_request_t.
*/
BLE_GATTS_EVT_SYS_ATTR_MISSING, /**< A persistent system attribute access is pending. \n Respond with @ref
sd_ble_gatts_sys_attr_set. \n See @ref ble_gatts_evt_sys_attr_missing_t. */
BLE_GATTS_EVT_HVC, /**< Handle Value Confirmation. \n See @ref ble_gatts_evt_hvc_t.
*/
BLE_GATTS_EVT_SC_CONFIRM, /**< Service Changed Confirmation. \n No additional event
structure applies. */
BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST, /**< Exchange MTU Request. \n Reply with
@ref sd_ble_gatts_exchange_mtu_reply. \n See @ref ble_gatts_evt_exchange_mtu_request_t.
*/
BLE_GATTS_EVT_TIMEOUT, /**< Peer failed to respond to an ATT request in time. \n See @ref
ble_gatts_evt_timeout_t. */
BLE_GATTS_EVT_HVN_TX_COMPLETE /**< Handle Value Notification transmission complete. \n See @ref
ble_gatts_evt_hvn_tx_complete_t. */
};
/**@brief GATTS Configuration IDs.
@@ -118,9 +112,9 @@ enum BLE_GATTS_EVTS {
* IDs that uniquely identify a GATTS configuration.
*/
enum BLE_GATTS_CFGS {
BLE_GATTS_CFG_SERVICE_CHANGED = BLE_GATTS_CFG_BASE, /**< Service changed configuration. */
BLE_GATTS_CFG_ATTR_TAB_SIZE, /**< Attribute table size configuration. */
BLE_GATTS_CFG_SERVICE_CHANGED_CCCD_PERM, /**< Service changed CCCD permission configuration. */
BLE_GATTS_CFG_SERVICE_CHANGED = BLE_GATTS_CFG_BASE, /**< Service changed configuration. */
BLE_GATTS_CFG_ATTR_TAB_SIZE, /**< Attribute table size configuration. */
BLE_GATTS_CFG_SERVICE_CHANGED_CCCD_PERM, /**< Service changed CCCD permission configuration. */
};
/** @} */
@@ -174,10 +168,10 @@ enum BLE_GATTS_CFGS {
* @{ */
#define BLE_GATTS_VLOC_INVALID 0x00 /**< Invalid Location. */
#define BLE_GATTS_VLOC_STACK 0x01 /**< Attribute Value is located in stack memory, no user memory is required. */
#define BLE_GATTS_VLOC_USER \
0x02 /**< Attribute Value is located in user memory. This requires the user to maintain a valid buffer through the \
lifetime of the attribute, since the stack will read and write directly to the memory using the pointer \
provided in the APIs. There are no alignment requirements for the buffer. */
#define BLE_GATTS_VLOC_USER \
0x02 /**< Attribute Value is located in user memory. This requires the user to maintain a valid buffer through the lifetime \
of the attribute, since the stack will read and write directly to the memory using the pointer provided in the APIs. \
There are no alignment requirements for the buffer. */
/** @} */
/** @defgroup BLE_GATTS_AUTHORIZE_TYPES GATT Server Authorization Types
@@ -196,7 +190,8 @@ enum BLE_GATTS_CFGS {
/** @defgroup BLE_GATTS_SERVICE_CHANGED Service Changed Inclusion Values
* @{
*/
#define BLE_GATTS_SERVICE_CHANGED_DEFAULT (1) /**< Default is to include the Service Changed characteristic in the Attribute Table. */
#define BLE_GATTS_SERVICE_CHANGED_DEFAULT \
(1) /**< Default is to include the Service Changed characteristic in the Attribute Table. */
/** @} */
/** @defgroup BLE_GATTS_ATTR_TAB_SIZE Attribute Table size
@@ -209,7 +204,8 @@ enum BLE_GATTS_CFGS {
/** @defgroup BLE_GATTS_DEFAULTS GATT Server defaults
* @{
*/
#define BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT 1 /**< Default number of Handle Value Notifications that can be queued for transmission. */
#define BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT \
1 /**< Default number of Handle Value Notifications that can be queued for transmission. */
/** @} */
/** @} */
@@ -221,115 +217,115 @@ enum BLE_GATTS_CFGS {
* @brief BLE GATTS connection configuration parameters, set with @ref sd_ble_cfg_set.
*/
typedef struct {
uint8_t hvn_tx_queue_size; /**< Minimum guaranteed number of Handle Value Notifications that can be queued for
transmission. The default value is @ref BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT */
uint8_t hvn_tx_queue_size; /**< Minimum guaranteed number of Handle Value Notifications that can be queued for transmission.
The default value is @ref BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT */
} ble_gatts_conn_cfg_t;
/**@brief Attribute metadata. */
typedef struct {
ble_gap_conn_sec_mode_t read_perm; /**< Read permissions. */
ble_gap_conn_sec_mode_t write_perm; /**< Write permissions. */
uint8_t vlen : 1; /**< Variable length attribute. */
uint8_t vloc : 2; /**< Value location, see @ref BLE_GATTS_VLOCS.*/
uint8_t rd_auth : 1; /**< Read authorization and value will be requested from the application on every read operation. */
uint8_t wr_auth : 1; /**< Write authorization will be requested from the application on every Write Request operation
(but not Write Command). */
ble_gap_conn_sec_mode_t read_perm; /**< Read permissions. */
ble_gap_conn_sec_mode_t write_perm; /**< Write permissions. */
uint8_t vlen : 1; /**< Variable length attribute. */
uint8_t vloc : 2; /**< Value location, see @ref BLE_GATTS_VLOCS.*/
uint8_t rd_auth : 1; /**< Read authorization and value will be requested from the application on every read operation. */
uint8_t wr_auth : 1; /**< Write authorization will be requested from the application on every Write Request operation (but not
Write Command). */
} ble_gatts_attr_md_t;
/**@brief GATT Attribute. */
typedef struct {
ble_uuid_t const *p_uuid; /**< Pointer to the attribute UUID. */
ble_gatts_attr_md_t const *p_attr_md; /**< Pointer to the attribute metadata structure. */
uint16_t init_len; /**< Initial attribute value length in bytes. */
uint16_t init_offs; /**< Initial attribute value offset in bytes. If different from zero, the first init_offs bytes of
the attribute value will be left uninitialized. */
uint16_t max_len; /**< Maximum attribute value length in bytes, see @ref BLE_GATTS_ATTR_LENS_MAX for maximum values. */
uint8_t *p_value; /**< Pointer to the attribute data. Please note that if the @ref BLE_GATTS_VLOC_USER value location is
selected in the attribute metadata, this will have to point to a buffer that remains valid through
the lifetime of the attribute. This excludes usage of automatic variables that may go out of scope or
any other temporary location. The stack may access that memory directly without the application's
knowledge. For writable characteristics, this value must not be a location in flash memory.*/
ble_uuid_t const *p_uuid; /**< Pointer to the attribute UUID. */
ble_gatts_attr_md_t const *p_attr_md; /**< Pointer to the attribute metadata structure. */
uint16_t init_len; /**< Initial attribute value length in bytes. */
uint16_t init_offs; /**< Initial attribute value offset in bytes. If different from zero, the first init_offs bytes of the
attribute value will be left uninitialized. */
uint16_t max_len; /**< Maximum attribute value length in bytes, see @ref BLE_GATTS_ATTR_LENS_MAX for maximum values. */
uint8_t *p_value; /**< Pointer to the attribute data. Please note that if the @ref BLE_GATTS_VLOC_USER value location is
selected in the attribute metadata, this will have to point to a buffer that remains valid through the
lifetime of the attribute. This excludes usage of automatic variables that may go out of scope or any
other temporary location. The stack may access that memory directly without the application's
knowledge. For writable characteristics, this value must not be a location in flash memory.*/
} ble_gatts_attr_t;
/**@brief GATT Attribute Value. */
typedef struct {
uint16_t len; /**< Length in bytes to be written or read. Length in bytes written or read after successful return.*/
uint16_t offset; /**< Attribute value offset. */
uint8_t *p_value; /**< Pointer to where value is stored or will be stored.
If value is stored in user memory, only the attribute length is updated when p_value == NULL.
Set to NULL when reading to obtain the complete length of the attribute value */
uint16_t len; /**< Length in bytes to be written or read. Length in bytes written or read after successful return.*/
uint16_t offset; /**< Attribute value offset. */
uint8_t *p_value; /**< Pointer to where value is stored or will be stored.
If value is stored in user memory, only the attribute length is updated when p_value == NULL.
Set to NULL when reading to obtain the complete length of the attribute value */
} ble_gatts_value_t;
/**@brief GATT Characteristic Presentation Format. */
typedef struct {
uint8_t format; /**< Format of the value, see @ref BLE_GATT_CPF_FORMATS. */
int8_t exponent; /**< Exponent for integer data types. */
uint16_t unit; /**< Unit from Bluetooth Assigned Numbers. */
uint8_t name_space; /**< Namespace from Bluetooth Assigned Numbers, see @ref BLE_GATT_CPF_NAMESPACES. */
uint16_t desc; /**< Namespace description from Bluetooth Assigned Numbers, see @ref BLE_GATT_CPF_NAMESPACES. */
uint8_t format; /**< Format of the value, see @ref BLE_GATT_CPF_FORMATS. */
int8_t exponent; /**< Exponent for integer data types. */
uint16_t unit; /**< Unit from Bluetooth Assigned Numbers. */
uint8_t name_space; /**< Namespace from Bluetooth Assigned Numbers, see @ref BLE_GATT_CPF_NAMESPACES. */
uint16_t desc; /**< Namespace description from Bluetooth Assigned Numbers, see @ref BLE_GATT_CPF_NAMESPACES. */
} ble_gatts_char_pf_t;
/**@brief GATT Characteristic metadata. */
typedef struct {
ble_gatt_char_props_t char_props; /**< Characteristic Properties. */
ble_gatt_char_ext_props_t char_ext_props; /**< Characteristic Extended Properties. */
uint8_t const *p_char_user_desc; /**< Pointer to a UTF-8 encoded string (non-NULL terminated), NULL if the descriptor
is not required. */
uint16_t char_user_desc_max_size; /**< The maximum size in bytes of the user description descriptor. */
uint16_t char_user_desc_size; /**< The size of the user description, must be smaller or equal to
char_user_desc_max_size. */
ble_gatts_char_pf_t const *p_char_pf; /**< Pointer to a presentation format structure or NULL if the CPF descriptor is not required. */
ble_gatts_attr_md_t const *p_user_desc_md; /**< Attribute metadata for the User Description descriptor, or NULL for default values. */
ble_gatts_attr_md_t const *p_cccd_md; /**< Attribute metadata for the Client Characteristic Configuration Descriptor,
or NULL for default values. */
ble_gatts_attr_md_t const *p_sccd_md; /**< Attribute metadata for the Server Characteristic Configuration Descriptor,
or NULL for default values. */
ble_gatt_char_props_t char_props; /**< Characteristic Properties. */
ble_gatt_char_ext_props_t char_ext_props; /**< Characteristic Extended Properties. */
uint8_t const *
p_char_user_desc; /**< Pointer to a UTF-8 encoded string (non-NULL terminated), NULL if the descriptor is not required. */
uint16_t char_user_desc_max_size; /**< The maximum size in bytes of the user description descriptor. */
uint16_t char_user_desc_size; /**< The size of the user description, must be smaller or equal to char_user_desc_max_size. */
ble_gatts_char_pf_t const
*p_char_pf; /**< Pointer to a presentation format structure or NULL if the CPF descriptor is not required. */
ble_gatts_attr_md_t const
*p_user_desc_md; /**< Attribute metadata for the User Description descriptor, or NULL for default values. */
ble_gatts_attr_md_t const
*p_cccd_md; /**< Attribute metadata for the Client Characteristic Configuration Descriptor, or NULL for default values. */
ble_gatts_attr_md_t const
*p_sccd_md; /**< Attribute metadata for the Server Characteristic Configuration Descriptor, or NULL for default values. */
} ble_gatts_char_md_t;
/**@brief GATT Characteristic Definition Handles. */
typedef struct {
uint16_t value_handle; /**< Handle to the characteristic value. */
uint16_t user_desc_handle; /**< Handle to the User Description descriptor, or @ref BLE_GATT_HANDLE_INVALID if not
present. */
uint16_t cccd_handle; /**< Handle to the Client Characteristic Configuration Descriptor, or @ref
BLE_GATT_HANDLE_INVALID if not present. */
uint16_t sccd_handle; /**< Handle to the Server Characteristic Configuration Descriptor, or @ref
BLE_GATT_HANDLE_INVALID if not present. */
uint16_t value_handle; /**< Handle to the characteristic value. */
uint16_t user_desc_handle; /**< Handle to the User Description descriptor, or @ref BLE_GATT_HANDLE_INVALID if not present. */
uint16_t cccd_handle; /**< Handle to the Client Characteristic Configuration Descriptor, or @ref BLE_GATT_HANDLE_INVALID if
not present. */
uint16_t sccd_handle; /**< Handle to the Server Characteristic Configuration Descriptor, or @ref BLE_GATT_HANDLE_INVALID if
not present. */
} ble_gatts_char_handles_t;
/**@brief GATT HVx parameters. */
typedef struct {
uint16_t handle; /**< Characteristic Value Handle. */
uint8_t type; /**< Indication or Notification, see @ref BLE_GATT_HVX_TYPES. */
uint16_t offset; /**< Offset within the attribute value. */
uint16_t *p_len; /**< Length in bytes to be written, length in bytes written after return. */
uint8_t const *p_data; /**< Actual data content, use NULL to use the current attribute value. */
uint16_t handle; /**< Characteristic Value Handle. */
uint8_t type; /**< Indication or Notification, see @ref BLE_GATT_HVX_TYPES. */
uint16_t offset; /**< Offset within the attribute value. */
uint16_t *p_len; /**< Length in bytes to be written, length in bytes written after return. */
uint8_t const *p_data; /**< Actual data content, use NULL to use the current attribute value. */
} ble_gatts_hvx_params_t;
/**@brief GATT Authorization parameters. */
typedef struct {
uint16_t gatt_status; /**< GATT status code for the operation, see @ref BLE_GATT_STATUS_CODES. */
uint8_t update : 1; /**< If set, data supplied in p_data will be used to update the attribute value.
Please note that for @ref BLE_GATTS_AUTHORIZE_TYPE_WRITE operations this bit must always be
set, as the data to be written needs to be stored and later provided by the application. */
uint16_t offset; /**< Offset of the attribute value being updated. */
uint16_t len; /**< Length in bytes of the value in p_data pointer, see @ref BLE_GATTS_ATTR_LENS_MAX. */
uint8_t const *p_data; /**< Pointer to new value used to update the attribute value. */
uint16_t gatt_status; /**< GATT status code for the operation, see @ref BLE_GATT_STATUS_CODES. */
uint8_t update : 1; /**< If set, data supplied in p_data will be used to update the attribute value.
Please note that for @ref BLE_GATTS_AUTHORIZE_TYPE_WRITE operations this bit must always be set,
as the data to be written needs to be stored and later provided by the application. */
uint16_t offset; /**< Offset of the attribute value being updated. */
uint16_t len; /**< Length in bytes of the value in p_data pointer, see @ref BLE_GATTS_ATTR_LENS_MAX. */
uint8_t const *p_data; /**< Pointer to new value used to update the attribute value. */
} ble_gatts_authorize_params_t;
/**@brief GATT Read or Write Authorize Reply parameters. */
typedef struct {
uint8_t type; /**< Type of authorize operation, see @ref BLE_GATTS_AUTHORIZE_TYPES. */
union {
ble_gatts_authorize_params_t read; /**< Read authorization parameters. */
ble_gatts_authorize_params_t write; /**< Write authorization parameters. */
} params; /**< Reply Parameters. */
uint8_t type; /**< Type of authorize operation, see @ref BLE_GATTS_AUTHORIZE_TYPES. */
union {
ble_gatts_authorize_params_t read; /**< Read authorization parameters. */
ble_gatts_authorize_params_t write; /**< Write authorization parameters. */
} params; /**< Reply Parameters. */
} ble_gatts_rw_authorize_reply_params_t;
/**@brief Service Changed Inclusion configuration parameters, set with @ref sd_ble_cfg_set. */
typedef struct {
uint8_t service_changed : 1; /**< If 1, include the Service Changed characteristic in the Attribute Table. Default is
@ref BLE_GATTS_SERVICE_CHANGED_DEFAULT. */
uint8_t service_changed : 1; /**< If 1, include the Service Changed characteristic in the Attribute Table. Default is @ref
BLE_GATTS_SERVICE_CHANGED_DEFAULT. */
} ble_gatts_cfg_service_changed_t;
/**@brief Service Changed CCCD permission configuration parameters, set with @ref sd_ble_cfg_set.
@@ -338,16 +334,16 @@ typedef struct {
*
* @retval ::NRF_ERROR_INVALID_PARAM One or more of the following is true:
* - @ref ble_gatts_attr_md_t::write_perm is out of range.
* - @ref ble_gatts_attr_md_t::write_perm is @ref BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS,
* that is not allowed by the Bluetooth Specification.
* - wrong @ref ble_gatts_attr_md_t::read_perm, only @ref
* BLE_GAP_CONN_SEC_MODE_SET_OPEN is allowed by the Bluetooth Specification.
* - @ref ble_gatts_attr_md_t::write_perm is @ref BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS, that is
* not allowed by the Bluetooth Specification.
* - wrong @ref ble_gatts_attr_md_t::read_perm, only @ref BLE_GAP_CONN_SEC_MODE_SET_OPEN is
* allowed by the Bluetooth Specification.
* - wrong @ref ble_gatts_attr_md_t::vloc, only @ref BLE_GATTS_VLOC_STACK is allowed.
* @retval ::NRF_ERROR_NOT_SUPPORTED Security Mode 2 not supported
*/
typedef struct {
ble_gatts_attr_md_t perm; /**< Permission for Service Changed CCCD. Default is @ref BLE_GAP_CONN_SEC_MODE_SET_OPEN, no
authorization. */
ble_gatts_attr_md_t
perm; /**< Permission for Service Changed CCCD. Default is @ref BLE_GAP_CONN_SEC_MODE_SET_OPEN, no authorization. */
} ble_gatts_cfg_service_changed_cccd_perm_t;
/**@brief Attribute table size configuration parameters, set with @ref sd_ble_cfg_set.
@@ -358,85 +354,86 @@ typedef struct {
* - The specified Attribute Table size is not a multiple of 4.
*/
typedef struct {
uint32_t attr_tab_size; /**< Attribute table size. Default is @ref BLE_GATTS_ATTR_TAB_SIZE_DEFAULT, minimum is @ref
BLE_GATTS_ATTR_TAB_SIZE_MIN. */
uint32_t attr_tab_size; /**< Attribute table size. Default is @ref BLE_GATTS_ATTR_TAB_SIZE_DEFAULT, minimum is @ref
BLE_GATTS_ATTR_TAB_SIZE_MIN. */
} ble_gatts_cfg_attr_tab_size_t;
/**@brief Config structure for GATTS configurations. */
typedef union {
ble_gatts_cfg_service_changed_t service_changed; /**< Include service changed characteristic, cfg_id is @ref BLE_GATTS_CFG_SERVICE_CHANGED. */
ble_gatts_cfg_service_changed_cccd_perm_t service_changed_cccd_perm; /**< Service changed CCCD permission, cfg_id is @ref
BLE_GATTS_CFG_SERVICE_CHANGED_CCCD_PERM. */
ble_gatts_cfg_attr_tab_size_t attr_tab_size; /**< Attribute table size, cfg_id is @ref BLE_GATTS_CFG_ATTR_TAB_SIZE. */
ble_gatts_cfg_service_changed_t
service_changed; /**< Include service changed characteristic, cfg_id is @ref BLE_GATTS_CFG_SERVICE_CHANGED. */
ble_gatts_cfg_service_changed_cccd_perm_t service_changed_cccd_perm; /**< Service changed CCCD permission, cfg_id is @ref
BLE_GATTS_CFG_SERVICE_CHANGED_CCCD_PERM. */
ble_gatts_cfg_attr_tab_size_t attr_tab_size; /**< Attribute table size, cfg_id is @ref BLE_GATTS_CFG_ATTR_TAB_SIZE. */
} ble_gatts_cfg_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_WRITE. */
typedef struct {
uint16_t handle; /**< Attribute Handle. */
ble_uuid_t uuid; /**< Attribute UUID. */
uint8_t op; /**< Type of write operation, see @ref BLE_GATTS_OPS. */
uint8_t auth_required; /**< Writing operation deferred due to authorization requirement. Application may use @ref
sd_ble_gatts_value_set to finalize the writing operation. */
uint16_t offset; /**< Offset for the write operation. */
uint16_t len; /**< Length of the received data. */
uint8_t data[1]; /**< Received data. @note This is a variable length array. The size of 1 indicated is only a
placeholder for compilation. See @ref sd_ble_evt_get for more information on how to use
event structures with variable length array members. */
uint16_t handle; /**< Attribute Handle. */
ble_uuid_t uuid; /**< Attribute UUID. */
uint8_t op; /**< Type of write operation, see @ref BLE_GATTS_OPS. */
uint8_t auth_required; /**< Writing operation deferred due to authorization requirement. Application may use @ref
sd_ble_gatts_value_set to finalize the writing operation. */
uint16_t offset; /**< Offset for the write operation. */
uint16_t len; /**< Length of the received data. */
uint8_t data[1]; /**< Received data. @note This is a variable length array. The size of 1 indicated is only a placeholder for
compilation. See @ref sd_ble_evt_get for more information on how to use event structures with variable
length array members. */
} ble_gatts_evt_write_t;
/**@brief Event substructure for authorized read requests, see @ref ble_gatts_evt_rw_authorize_request_t. */
typedef struct {
uint16_t handle; /**< Attribute Handle. */
ble_uuid_t uuid; /**< Attribute UUID. */
uint16_t offset; /**< Offset for the read operation. */
uint16_t handle; /**< Attribute Handle. */
ble_uuid_t uuid; /**< Attribute UUID. */
uint16_t offset; /**< Offset for the read operation. */
} ble_gatts_evt_read_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST. */
typedef struct {
uint8_t type; /**< Type of authorize operation, see @ref BLE_GATTS_AUTHORIZE_TYPES. */
union {
ble_gatts_evt_read_t read; /**< Attribute Read Parameters. */
ble_gatts_evt_write_t write; /**< Attribute Write Parameters. */
} request; /**< Request Parameters. */
uint8_t type; /**< Type of authorize operation, see @ref BLE_GATTS_AUTHORIZE_TYPES. */
union {
ble_gatts_evt_read_t read; /**< Attribute Read Parameters. */
ble_gatts_evt_write_t write; /**< Attribute Write Parameters. */
} request; /**< Request Parameters. */
} ble_gatts_evt_rw_authorize_request_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_SYS_ATTR_MISSING. */
typedef struct {
uint8_t hint; /**< Hint (currently unused). */
uint8_t hint; /**< Hint (currently unused). */
} ble_gatts_evt_sys_attr_missing_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_HVC. */
typedef struct {
uint16_t handle; /**< Attribute Handle. */
uint16_t handle; /**< Attribute Handle. */
} ble_gatts_evt_hvc_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST. */
typedef struct {
uint16_t client_rx_mtu; /**< Client RX MTU size. */
uint16_t client_rx_mtu; /**< Client RX MTU size. */
} ble_gatts_evt_exchange_mtu_request_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_TIMEOUT. */
typedef struct {
uint8_t src; /**< Timeout source, see @ref BLE_GATT_TIMEOUT_SOURCES. */
uint8_t src; /**< Timeout source, see @ref BLE_GATT_TIMEOUT_SOURCES. */
} ble_gatts_evt_timeout_t;
/**@brief Event structure for @ref BLE_GATTS_EVT_HVN_TX_COMPLETE. */
typedef struct {
uint8_t count; /**< Number of notification transmissions completed. */
uint8_t count; /**< Number of notification transmissions completed. */
} ble_gatts_evt_hvn_tx_complete_t;
/**@brief GATTS event structure. */
typedef struct {
uint16_t conn_handle; /**< Connection Handle on which the event occurred. */
union {
ble_gatts_evt_write_t write; /**< Write Event Parameters. */
ble_gatts_evt_rw_authorize_request_t authorize_request; /**< Read or Write Authorize Request Parameters. */
ble_gatts_evt_sys_attr_missing_t sys_attr_missing; /**< System attributes missing. */
ble_gatts_evt_hvc_t hvc; /**< Handle Value Confirmation Event Parameters. */
ble_gatts_evt_exchange_mtu_request_t exchange_mtu_request; /**< Exchange MTU Request Event Parameters. */
ble_gatts_evt_timeout_t timeout; /**< Timeout Event. */
ble_gatts_evt_hvn_tx_complete_t hvn_tx_complete; /**< Handle Value Notification transmission complete Event Parameters. */
} params; /**< Event Parameters. */
uint16_t conn_handle; /**< Connection Handle on which the event occurred. */
union {
ble_gatts_evt_write_t write; /**< Write Event Parameters. */
ble_gatts_evt_rw_authorize_request_t authorize_request; /**< Read or Write Authorize Request Parameters. */
ble_gatts_evt_sys_attr_missing_t sys_attr_missing; /**< System attributes missing. */
ble_gatts_evt_hvc_t hvc; /**< Handle Value Confirmation Event Parameters. */
ble_gatts_evt_exchange_mtu_request_t exchange_mtu_request; /**< Exchange MTU Request Event Parameters. */
ble_gatts_evt_timeout_t timeout; /**< Timeout Event. */
ble_gatts_evt_hvn_tx_complete_t hvn_tx_complete; /**< Handle Value Notification transmission complete Event Parameters. */
} params; /**< Event Parameters. */
} ble_gatts_evt_t;
/** @} */
@@ -446,9 +443,8 @@ typedef struct {
/**@brief Add a service declaration to the Attribute Table.
*
* @note Secondary Services are only relevant in the context of the entity that references them, it is therefore
* forbidden to add a secondary service declaration that is not referenced by another service later in the Attribute
* Table.
* @note Secondary Services are only relevant in the context of the entity that references them, it is therefore forbidden to
* add a secondary service declaration that is not referenced by another service later in the Attribute Table.
*
* @mscs
* @mmsc{@ref BLE_GATTS_ATT_TABLE_POP_MSC}
@@ -460,8 +456,7 @@ typedef struct {
*
* @retval ::NRF_SUCCESS Successfully added a service declaration.
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied, Vendor Specific UUIDs need to be present in the
* table.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied, Vendor Specific UUIDs need to be present in the table.
* @retval ::NRF_ERROR_FORBIDDEN Forbidden value supplied, certain UUIDs are reserved for the stack.
* @retval ::NRF_ERROR_NO_MEM Not enough memory to complete operation.
*/
@@ -469,8 +464,8 @@ SVCALL(SD_BLE_GATTS_SERVICE_ADD, uint32_t, sd_ble_gatts_service_add(uint8_t type
/**@brief Add an include declaration to the Attribute Table.
*
* @note It is currently only possible to add an include declaration to the last added service (i.e. only sequential
* population is supported at this time).
* @note It is currently only possible to add an include declaration to the last added service (i.e. only sequential population is
* supported at this time).
*
* @note The included service must already be present in the Attribute Table prior to this call.
*
@@ -478,42 +473,42 @@ SVCALL(SD_BLE_GATTS_SERVICE_ADD, uint32_t, sd_ble_gatts_service_add(uint8_t type
* @mmsc{@ref BLE_GATTS_ATT_TABLE_POP_MSC}
* @endmscs
*
* @param[in] service_handle Handle of the service where the included service is to be placed, if @ref
* BLE_GATT_HANDLE_INVALID is used, it will be placed sequentially.
* @param[in] service_handle Handle of the service where the included service is to be placed, if @ref BLE_GATT_HANDLE_INVALID
* is used, it will be placed sequentially.
* @param[in] inc_srvc_handle Handle of the included service.
* @param[out] p_include_handle Pointer to a 16-bit word where the assigned handle will be stored.
*
* @retval ::NRF_SUCCESS Successfully added an include declaration.
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied, handle values need to match previously added
* services.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied, handle values need to match previously added services.
* @retval ::NRF_ERROR_INVALID_STATE Invalid state to perform operation, a service context is required.
* @retval ::NRF_ERROR_NOT_SUPPORTED Feature is not supported, service_handle must be that of the last added service.
* @retval ::NRF_ERROR_FORBIDDEN Forbidden value supplied, self inclusions are not allowed.
* @retval ::NRF_ERROR_NO_MEM Not enough memory to complete operation.
* @retval ::NRF_ERROR_NOT_FOUND Attribute not found.
*/
SVCALL(SD_BLE_GATTS_INCLUDE_ADD, uint32_t, sd_ble_gatts_include_add(uint16_t service_handle, uint16_t inc_srvc_handle, uint16_t *p_include_handle));
SVCALL(SD_BLE_GATTS_INCLUDE_ADD, uint32_t,
sd_ble_gatts_include_add(uint16_t service_handle, uint16_t inc_srvc_handle, uint16_t *p_include_handle));
/**@brief Add a characteristic declaration, a characteristic value declaration and optional characteristic descriptor
* declarations to the Attribute Table.
/**@brief Add a characteristic declaration, a characteristic value declaration and optional characteristic descriptor declarations
* to the Attribute Table.
*
* @note It is currently only possible to add a characteristic to the last added service (i.e. only sequential
* population is supported at this time).
* @note It is currently only possible to add a characteristic to the last added service (i.e. only sequential population is
* supported at this time).
*
* @note Several restrictions apply to the parameters, such as matching permissions between the user description
* descriptor and the writable auxiliaries bits, readable (no security) and writable (selectable) CCCDs and SCCDs and
* valid presentation format values.
* @note Several restrictions apply to the parameters, such as matching permissions between the user description descriptor and
* the writable auxiliaries bits, readable (no security) and writable (selectable) CCCDs and SCCDs and valid presentation format
* values.
*
* @note If no metadata is provided for the optional descriptors, their permissions will be derived from the
* characteristic permissions.
* @note If no metadata is provided for the optional descriptors, their permissions will be derived from the characteristic
* permissions.
*
* @mscs
* @mmsc{@ref BLE_GATTS_ATT_TABLE_POP_MSC}
* @endmscs
*
* @param[in] service_handle Handle of the service where the characteristic is to be placed, if @ref
* BLE_GATT_HANDLE_INVALID is used, it will be placed sequentially.
* @param[in] service_handle Handle of the service where the characteristic is to be placed, if @ref BLE_GATT_HANDLE_INVALID is
* used, it will be placed sequentially.
* @param[in] p_char_md Characteristic metadata.
* @param[in] p_attr_char_value Pointer to the attribute structure corresponding to the characteristic value.
* @param[out] p_handles Pointer to the structure where the assigned handles will be stored.
@@ -525,38 +520,37 @@ SVCALL(SD_BLE_GATTS_INCLUDE_ADD, uint32_t, sd_ble_gatts_include_add(uint16_t ser
* @retval ::NRF_ERROR_INVALID_STATE Invalid state to perform operation, a service context is required.
* @retval ::NRF_ERROR_FORBIDDEN Forbidden value supplied, certain UUIDs are reserved for the stack.
* @retval ::NRF_ERROR_NO_MEM Not enough memory to complete operation.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied, attribute lengths are restricted by @ref
* BLE_GATTS_ATTR_LENS_MAX.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied, attribute lengths are restricted by @ref BLE_GATTS_ATTR_LENS_MAX.
*/
SVCALL(SD_BLE_GATTS_CHARACTERISTIC_ADD, uint32_t,
sd_ble_gatts_characteristic_add(uint16_t service_handle, ble_gatts_char_md_t const *p_char_md, ble_gatts_attr_t const *p_attr_char_value,
ble_gatts_char_handles_t *p_handles));
sd_ble_gatts_characteristic_add(uint16_t service_handle, ble_gatts_char_md_t const *p_char_md,
ble_gatts_attr_t const *p_attr_char_value, ble_gatts_char_handles_t *p_handles));
/**@brief Add a descriptor to the Attribute Table.
*
* @note It is currently only possible to add a descriptor to the last added characteristic (i.e. only sequential
* population is supported at this time).
* @note It is currently only possible to add a descriptor to the last added characteristic (i.e. only sequential population is
* supported at this time).
*
* @mscs
* @mmsc{@ref BLE_GATTS_ATT_TABLE_POP_MSC}
* @endmscs
*
* @param[in] char_handle Handle of the characteristic where the descriptor is to be placed, if @ref
* BLE_GATT_HANDLE_INVALID is used, it will be placed sequentially.
* @param[in] char_handle Handle of the characteristic where the descriptor is to be placed, if @ref BLE_GATT_HANDLE_INVALID is
* used, it will be placed sequentially.
* @param[in] p_attr Pointer to the attribute structure.
* @param[out] p_handle Pointer to a 16-bit word where the assigned handle will be stored.
*
* @retval ::NRF_SUCCESS Successfully added a descriptor.
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied, characteristic handle, Vendor Specific UUIDs,
* lengths, and permissions need to adhere to the constraints.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied, characteristic handle, Vendor Specific UUIDs, lengths, and
* permissions need to adhere to the constraints.
* @retval ::NRF_ERROR_INVALID_STATE Invalid state to perform operation, a characteristic context is required.
* @retval ::NRF_ERROR_FORBIDDEN Forbidden value supplied, certain UUIDs are reserved for the stack.
* @retval ::NRF_ERROR_NO_MEM Not enough memory to complete operation.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied, attribute lengths are restricted by @ref
* BLE_GATTS_ATTR_LENS_MAX.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied, attribute lengths are restricted by @ref BLE_GATTS_ATTR_LENS_MAX.
*/
SVCALL(SD_BLE_GATTS_DESCRIPTOR_ADD, uint32_t, sd_ble_gatts_descriptor_add(uint16_t char_handle, ble_gatts_attr_t const *p_attr, uint16_t *p_handle));
SVCALL(SD_BLE_GATTS_DESCRIPTOR_ADD, uint32_t,
sd_ble_gatts_descriptor_add(uint16_t char_handle, ble_gatts_attr_t const *p_attr, uint16_t *p_handle));
/**@brief Set the value of a given attribute.
*
@@ -576,11 +570,11 @@ SVCALL(SD_BLE_GATTS_DESCRIPTOR_ADD, uint32_t, sd_ble_gatts_descriptor_add(uint16
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied.
* @retval ::NRF_ERROR_NOT_FOUND Attribute not found.
* @retval ::NRF_ERROR_FORBIDDEN Forbidden handle supplied, certain attributes are not modifiable by the application.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied, attribute lengths are restricted by @ref
* BLE_GATTS_ATTR_LENS_MAX.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied, attribute lengths are restricted by @ref BLE_GATTS_ATTR_LENS_MAX.
* @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid connection handle supplied on a system attribute.
*/
SVCALL(SD_BLE_GATTS_VALUE_SET, uint32_t, sd_ble_gatts_value_set(uint16_t conn_handle, uint16_t handle, ble_gatts_value_t *p_value));
SVCALL(SD_BLE_GATTS_VALUE_SET, uint32_t,
sd_ble_gatts_value_set(uint16_t conn_handle, uint16_t handle, ble_gatts_value_t *p_value));
/**@brief Get the value of a given attribute.
*
@@ -602,21 +596,21 @@ SVCALL(SD_BLE_GATTS_VALUE_SET, uint32_t, sd_ble_gatts_value_set(uint16_t conn_ha
* @retval ::NRF_ERROR_NOT_FOUND Attribute not found.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid attribute offset supplied.
* @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid connection handle supplied on a system attribute.
* @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them
* to a known value.
* @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them to a known
* value.
*/
SVCALL(SD_BLE_GATTS_VALUE_GET, uint32_t, sd_ble_gatts_value_get(uint16_t conn_handle, uint16_t handle, ble_gatts_value_t *p_value));
SVCALL(SD_BLE_GATTS_VALUE_GET, uint32_t,
sd_ble_gatts_value_get(uint16_t conn_handle, uint16_t handle, ble_gatts_value_t *p_value));
/**@brief Notify or Indicate an attribute value.
*
* @details This function checks for the relevant Client Characteristic Configuration descriptor value to verify that
* the relevant operation (notification or indication) has been enabled by the client. It is also able to update the
* attribute value before issuing the PDU, so that the application can atomically perform a value update and a server
* initiated transaction with a single API call.
* @details This function checks for the relevant Client Characteristic Configuration descriptor value to verify that the relevant
* operation (notification or indication) has been enabled by the client. It is also able to update the attribute value before
* issuing the PDU, so that the application can atomically perform a value update and a server initiated transaction with a single
* API call.
*
* @note The local attribute value may be updated even if an outgoing packet is not sent to the peer due to an error
* during execution. The Attribute Table has been updated if one of the following error codes is returned: @ref
* NRF_ERROR_INVALID_STATE,
* @note The local attribute value may be updated even if an outgoing packet is not sent to the peer due to an error during
* execution. The Attribute Table has been updated if one of the following error codes is returned: @ref NRF_ERROR_INVALID_STATE,
* @ref NRF_ERROR_BUSY,
* @ref NRF_ERROR_FORBIDDEN, @ref BLE_ERROR_GATTS_SYS_ATTR_MISSING and @ref NRF_ERROR_RESOURCES.
* The caller can check whether the value has been updated by looking at the contents of *(@ref
@@ -628,17 +622,16 @@ SVCALL(SD_BLE_GATTS_VALUE_GET, uint32_t, sd_ble_gatts_value_get(uint16_t conn_ha
* A @ref BLE_GATTS_EVT_HVC event will be issued as soon as the confirmation arrives from the peer.
*
* @note The number of Handle Value Notifications that can be queued is configured by @ref
* ble_gatts_conn_cfg_t::hvn_tx_queue_size When the queue is full, the function call will return @ref
* NRF_ERROR_RESOURCES. A @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event will be issued as soon as the transmission of the
* notification is complete.
* ble_gatts_conn_cfg_t::hvn_tx_queue_size When the queue is full, the function call will return @ref NRF_ERROR_RESOURCES. A @ref
* BLE_GATTS_EVT_HVN_TX_COMPLETE event will be issued as soon as the transmission of the notification is complete.
*
* @note The application can keep track of the available queue element count for notifications by following the
* procedure below:
* @note The application can keep track of the available queue element count for notifications by following the procedure
* below:
* - Store initial queue element count in a variable.
* - Decrement the variable, which stores the currently available queue element count, by one when a call to
* this function returns @ref NRF_SUCCESS.
* - Increment the variable, which stores the current available queue element count, by the count variable in
* @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event.
* - Decrement the variable, which stores the currently available queue element count, by one when a call to this
* function returns @ref NRF_SUCCESS.
* - Increment the variable, which stores the current available queue element count, by the count variable in @ref
* BLE_GATTS_EVT_HVN_TX_COMPLETE event.
*
* @events
* @event{@ref BLE_GATTS_EVT_HVN_TX_COMPLETE, Notification transmission complete.}
@@ -659,8 +652,8 @@ SVCALL(SD_BLE_GATTS_VALUE_GET, uint32_t, sd_ble_gatts_value_get(uint16_t conn_ha
* is updated, @ref ble_gatts_hvx_params_t::p_len is updated by the SoftDevice to
* contain the number of actual bytes written, else it will be set to 0.
*
* @retval ::NRF_SUCCESS Successfully queued a notification or indication for transmission, and optionally updated the
* attribute value.
* @retval ::NRF_SUCCESS Successfully queued a notification or indication for transmission, and optionally updated the attribute
* value.
* @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid Connection Handle.
* @retval ::NRF_ERROR_INVALID_STATE One or more of the following is true:
* - Invalid Connection State
@@ -668,18 +661,18 @@ SVCALL(SD_BLE_GATTS_VALUE_GET, uint32_t, sd_ble_gatts_value_get(uint16_t conn_ha
* - An ATT_MTU exchange is ongoing
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied.
* @retval ::BLE_ERROR_INVALID_ATTR_HANDLE Invalid attribute handle(s) supplied. Only attributes added directly by the
* application are available to notify and indicate.
* @retval ::BLE_ERROR_GATTS_INVALID_ATTR_TYPE Invalid attribute type(s) supplied, only characteristic values may be
* notified and indicated.
* @retval ::BLE_ERROR_INVALID_ATTR_HANDLE Invalid attribute handle(s) supplied. Only attributes added directly by the application
* are available to notify and indicate.
* @retval ::BLE_ERROR_GATTS_INVALID_ATTR_TYPE Invalid attribute type(s) supplied, only characteristic values may be notified and
* indicated.
* @retval ::NRF_ERROR_NOT_FOUND Attribute not found.
* @retval ::NRF_ERROR_FORBIDDEN The connection's current security level is lower than the one required by the write
* permissions of the CCCD associated with this characteristic.
* @retval ::NRF_ERROR_FORBIDDEN The connection's current security level is lower than the one required by the write permissions
* of the CCCD associated with this characteristic.
* @retval ::NRF_ERROR_DATA_SIZE Invalid data size(s) supplied.
* @retval ::NRF_ERROR_BUSY For @ref BLE_GATT_HVX_INDICATION Procedure already in progress. Wait for a @ref
* BLE_GATTS_EVT_HVC event and retry.
* @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them
* to a known value.
* @retval ::NRF_ERROR_BUSY For @ref BLE_GATT_HVX_INDICATION Procedure already in progress. Wait for a @ref BLE_GATTS_EVT_HVC
* event and retry.
* @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them to a known
* value.
* @retval ::NRF_ERROR_RESOURCES Too many notifications queued.
* Wait for a @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event and retry.
* @retval ::NRF_ERROR_TIMEOUT There has been a GATT procedure timeout. No new GATT procedure can be performed without
@@ -689,9 +682,9 @@ SVCALL(SD_BLE_GATTS_HVX, uint32_t, sd_ble_gatts_hvx(uint16_t conn_handle, ble_ga
/**@brief Indicate the Service Changed attribute value.
*
* @details This call will send a Handle Value Indication to one or more peers connected to inform them that the
* Attribute Table layout has changed. As soon as the peer has confirmed the indication, a @ref BLE_GATTS_EVT_SC_CONFIRM
* event will be issued.
* @details This call will send a Handle Value Indication to one or more peers connected to inform them that the Attribute
* Table layout has changed. As soon as the peer has confirmed the indication, a @ref BLE_GATTS_EVT_SC_CONFIRM event will
* be issued.
*
* @note Some of the restrictions and limitations that apply to @ref sd_ble_gatts_hvx also apply here.
*
@@ -716,20 +709,20 @@ SVCALL(SD_BLE_GATTS_HVX, uint32_t, sd_ble_gatts_hvx(uint16_t conn_handle, ble_ga
* - Notifications and/or indications not enabled in the CCCD
* - An ATT_MTU exchange is ongoing
* @retval ::NRF_ERROR_INVALID_PARAM Invalid parameter(s) supplied.
* @retval ::BLE_ERROR_INVALID_ATTR_HANDLE Invalid attribute handle(s) supplied, handles must be in the range populated
* by the application.
* @retval ::BLE_ERROR_INVALID_ATTR_HANDLE Invalid attribute handle(s) supplied, handles must be in the range populated by the
* application.
* @retval ::NRF_ERROR_BUSY Procedure already in progress.
* @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them
* to a known value.
* @retval ::BLE_ERROR_GATTS_SYS_ATTR_MISSING System attributes missing, use @ref sd_ble_gatts_sys_attr_set to set them to a known
* value.
* @retval ::NRF_ERROR_TIMEOUT There has been a GATT procedure timeout. No new GATT procedure can be performed without
* reestablishing the connection.
*/
SVCALL(SD_BLE_GATTS_SERVICE_CHANGED, uint32_t, sd_ble_gatts_service_changed(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle));
SVCALL(SD_BLE_GATTS_SERVICE_CHANGED, uint32_t,
sd_ble_gatts_service_changed(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle));
/**@brief Respond to a Read/Write authorization request.
*
* @note This call should only be used as a response to a @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST event issued to the
* application.
* @note This call should only be used as a response to a @ref BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST event issued to the application.
*
* @mscs
* @mmsc{@ref BLE_GATTS_QUEUED_WRITE_NOBUF_AUTH_MSC}
@@ -748,8 +741,8 @@ SVCALL(SD_BLE_GATTS_SERVICE_CHANGED, uint32_t, sd_ble_gatts_service_changed(uint
* to a @ref BLE_GATTS_AUTHORIZE_TYPE_READ event if @ref ble_gatts_authorize_params_t::update
* is set to 0.
*
* @retval ::NRF_SUCCESS Successfully queued a response to the peer, and in the case of a write operation,
* Attribute Table updated.
* @retval ::NRF_SUCCESS Successfully queued a response to the peer, and in the case of a write operation, Attribute
* Table updated.
* @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid Connection Handle.
* @retval ::NRF_ERROR_BUSY The stack is busy, process pending events and retry.
* @retval ::NRF_ERROR_INVALID_ADDR Invalid pointer supplied.
@@ -761,7 +754,8 @@ SVCALL(SD_BLE_GATTS_SERVICE_CHANGED, uint32_t, sd_ble_gatts_service_changed(uint
* reestablishing the connection.
*/
SVCALL(SD_BLE_GATTS_RW_AUTHORIZE_REPLY, uint32_t,
sd_ble_gatts_rw_authorize_reply(uint16_t conn_handle, ble_gatts_rw_authorize_reply_params_t const *p_rw_authorize_reply_params));
sd_ble_gatts_rw_authorize_reply(uint16_t conn_handle,
ble_gatts_rw_authorize_reply_params_t const *p_rw_authorize_reply_params));
/**@brief Update persistent system attribute information.
*
@@ -776,18 +770,16 @@ SVCALL(SD_BLE_GATTS_RW_AUTHORIZE_REPLY, uint32_t,
* If the pointer is NULL, the system attribute info is initialized, assuming that
* the application does not have any previously saved system attribute data for this device.
*
* @note The state of persistent system attributes is reset upon connection establishment and then remembered for its
* duration.
* @note The state of persistent system attributes is reset upon connection establishment and then remembered for its duration.
*
* @note If this call returns with an error code different from @ref NRF_SUCCESS, the storage of persistent system
* attributes may have been completed only partially. This means that the state of the attribute table is undefined, and
* the application should either provide a new set of attributes using this same call or reset the SoftDevice to return
* to a known state.
* @note If this call returns with an error code different from @ref NRF_SUCCESS, the storage of persistent system attributes may
* have been completed only partially. This means that the state of the attribute table is undefined, and the application should
* either provide a new set of attributes using this same call or reset the SoftDevice to return to a known state.
*
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS is used with this function, only the system attributes included
* in system services will be modified.
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS is used with this function, only the system attributes included
* in user services will be modified.
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS is used with this function, only the system attributes included in system
* services will be modified.
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS is used with this function, only the system attributes included in user
* services will be modified.
*
* @mscs
* @mmsc{@ref BLE_GATTS_HVX_SYS_ATTRS_MISSING_MSC}
@@ -815,29 +807,27 @@ SVCALL(SD_BLE_GATTS_SYS_ATTR_SET, uint32_t,
/**@brief Retrieve persistent system attribute information from the stack.
*
* @details This call is used to retrieve information about values to be stored persistently by the application
* during the lifetime of a connection or after it has been terminated. When a new connection is established
* with the same bonded device, the system attribute information retrieved with this function should be restored using
* using @ref sd_ble_gatts_sys_attr_set. If retrieved after disconnection, the data should be read before a new
* connection established. The connection handle for the previous, now disconnected, connection will remain valid until
* a new one is created to allow this API call to refer to it. Connection handles belonging to active connections can be
* used as well, but care should be taken since the system attributes may be written to at any time by the peer during a
* connection's lifetime.
* during the lifetime of a connection or after it has been terminated. When a new connection is established with the
* same bonded device, the system attribute information retrieved with this function should be restored using using @ref
* sd_ble_gatts_sys_attr_set. If retrieved after disconnection, the data should be read before a new connection established. The
* connection handle for the previous, now disconnected, connection will remain valid until a new one is created to allow this API
* call to refer to it. Connection handles belonging to active connections can be used as well, but care should be taken since the
* system attributes may be written to at any time by the peer during a connection's lifetime.
*
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS is used with this function, only the system attributes included
* in system services will be returned.
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS is used with this function, only the system attributes included
* in user services will be returned.
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS is used with this function, only the system attributes included in system
* services will be returned.
* @note When the @ref BLE_GATTS_SYS_ATTR_FLAG_USR_SRVCS is used with this function, only the system attributes included in user
* services will be returned.
*
* @mscs
* @mmsc{@ref BLE_GATTS_SYS_ATTRS_BONDED_PEER_MSC}
* @endmscs
*
* @param[in] conn_handle Connection handle of the recently terminated connection.
* @param[out] p_sys_attr_data Pointer to a buffer where updated information about system attributes will be filled
* in. The format of the data is described in @ref BLE_GATTS_SYS_ATTRS_FORMAT. NULL can be provided to obtain the length
* of the data.
* @param[in,out] p_len Size of application buffer if p_sys_attr_data is not NULL. Unconditionally updated
* to actual length of system attribute data.
* @param[out] p_sys_attr_data Pointer to a buffer where updated information about system attributes will be filled in. The
* format of the data is described in @ref BLE_GATTS_SYS_ATTRS_FORMAT. NULL can be provided to obtain the length of the data.
* @param[in,out] p_len Size of application buffer if p_sys_attr_data is not NULL. Unconditionally updated to actual
* length of system attribute data.
* @param[in] flags Optional additional flags, see @ref BLE_GATTS_SYS_ATTR_FLAGS
*
* @retval ::NRF_SUCCESS Successfully retrieved the system attribute information.
@@ -891,8 +881,8 @@ SVCALL(SD_BLE_GATTS_ATTR_GET, uint32_t, sd_ble_gatts_attr_get(uint16_t handle, b
* - The minimum value is @ref BLE_GATT_ATT_MTU_DEFAULT.
* - The maximum value is @ref ble_gatt_conn_cfg_t::att_mtu in the connection configuration
* used for this connection.
* - The value must be equal to Client RX MTU size given in @ref
* sd_ble_gattc_exchange_mtu_request if an ATT_MTU exchange has already been performed in the other direction.
* - The value must be equal to Client RX MTU size given in @ref sd_ble_gattc_exchange_mtu_request
* if an ATT_MTU exchange has already been performed in the other direction.
*
* @retval ::NRF_SUCCESS Successfully sent response to the client.
* @retval ::BLE_ERROR_INVALID_CONN_HANDLE Invalid Connection Handle.
+2 -2
View File
@@ -71,8 +71,8 @@ extern "C" {
0x11 Unsupported Feature or Parameter Value*/
#define BLE_HCI_STATUS_CODE_INVALID_BTLE_COMMAND_PARAMETERS 0x12 /**< Invalid BLE Command Parameters. */
#define BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION 0x13 /**< Remote User Terminated Connection. */
#define BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES \
0x14 /**< Remote Device Terminated Connection due to low \
#define BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES \
0x14 /**< Remote Device Terminated Connection due to low \
resources.*/
#define BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF 0x15 /**< Remote Device Terminated Connection due to power off. */
#define BLE_HCI_LOCAL_HOST_TERMINATED_CONNECTION 0x16 /**< Local Host Terminated Connection. */
+95 -94
View File
@@ -83,32 +83,32 @@ extern "C" {
/**@brief L2CAP API SVC numbers. */
enum BLE_L2CAP_SVCS {
SD_BLE_L2CAP_CH_SETUP = BLE_L2CAP_SVC_BASE + 0, /**< Set up an L2CAP channel. */
SD_BLE_L2CAP_CH_RELEASE = BLE_L2CAP_SVC_BASE + 1, /**< Release an L2CAP channel. */
SD_BLE_L2CAP_CH_RX = BLE_L2CAP_SVC_BASE + 2, /**< Receive an SDU on an L2CAP channel. */
SD_BLE_L2CAP_CH_TX = BLE_L2CAP_SVC_BASE + 3, /**< Transmit an SDU on an L2CAP channel. */
SD_BLE_L2CAP_CH_FLOW_CONTROL = BLE_L2CAP_SVC_BASE + 4, /**< Advanced SDU reception flow control. */
SD_BLE_L2CAP_CH_SETUP = BLE_L2CAP_SVC_BASE + 0, /**< Set up an L2CAP channel. */
SD_BLE_L2CAP_CH_RELEASE = BLE_L2CAP_SVC_BASE + 1, /**< Release an L2CAP channel. */
SD_BLE_L2CAP_CH_RX = BLE_L2CAP_SVC_BASE + 2, /**< Receive an SDU on an L2CAP channel. */
SD_BLE_L2CAP_CH_TX = BLE_L2CAP_SVC_BASE + 3, /**< Transmit an SDU on an L2CAP channel. */
SD_BLE_L2CAP_CH_FLOW_CONTROL = BLE_L2CAP_SVC_BASE + 4, /**< Advanced SDU reception flow control. */
};
/**@brief L2CAP Event IDs. */
enum BLE_L2CAP_EVTS {
BLE_L2CAP_EVT_CH_SETUP_REQUEST = BLE_L2CAP_EVT_BASE + 0, /**< L2CAP Channel Setup Request event.
\n Reply with @ref sd_ble_l2cap_ch_setup.
\n See @ref ble_l2cap_evt_ch_setup_request_t. */
BLE_L2CAP_EVT_CH_SETUP_REFUSED = BLE_L2CAP_EVT_BASE + 1, /**< L2CAP Channel Setup Refused event.
\n See @ref ble_l2cap_evt_ch_setup_refused_t. */
BLE_L2CAP_EVT_CH_SETUP = BLE_L2CAP_EVT_BASE + 2, /**< L2CAP Channel Setup Completed event.
\n See @ref ble_l2cap_evt_ch_setup_t. */
BLE_L2CAP_EVT_CH_RELEASED = BLE_L2CAP_EVT_BASE + 3, /**< L2CAP Channel Released event.
\n No additional event structure applies. */
BLE_L2CAP_EVT_CH_SDU_BUF_RELEASED = BLE_L2CAP_EVT_BASE + 4, /**< L2CAP Channel SDU data buffer released event.
\n See @ref ble_l2cap_evt_ch_sdu_buf_released_t. */
BLE_L2CAP_EVT_CH_CREDIT = BLE_L2CAP_EVT_BASE + 5, /**< L2CAP Channel Credit received.
\n See @ref ble_l2cap_evt_ch_credit_t. */
BLE_L2CAP_EVT_CH_RX = BLE_L2CAP_EVT_BASE + 6, /**< L2CAP Channel SDU received.
\n See @ref ble_l2cap_evt_ch_rx_t. */
BLE_L2CAP_EVT_CH_TX = BLE_L2CAP_EVT_BASE + 7, /**< L2CAP Channel SDU transmitted.
\n See @ref ble_l2cap_evt_ch_tx_t. */
BLE_L2CAP_EVT_CH_SETUP_REQUEST = BLE_L2CAP_EVT_BASE + 0, /**< L2CAP Channel Setup Request event.
\n Reply with @ref sd_ble_l2cap_ch_setup.
\n See @ref ble_l2cap_evt_ch_setup_request_t. */
BLE_L2CAP_EVT_CH_SETUP_REFUSED = BLE_L2CAP_EVT_BASE + 1, /**< L2CAP Channel Setup Refused event.
\n See @ref ble_l2cap_evt_ch_setup_refused_t. */
BLE_L2CAP_EVT_CH_SETUP = BLE_L2CAP_EVT_BASE + 2, /**< L2CAP Channel Setup Completed event.
\n See @ref ble_l2cap_evt_ch_setup_t. */
BLE_L2CAP_EVT_CH_RELEASED = BLE_L2CAP_EVT_BASE + 3, /**< L2CAP Channel Released event.
\n No additional event structure applies. */
BLE_L2CAP_EVT_CH_SDU_BUF_RELEASED = BLE_L2CAP_EVT_BASE + 4, /**< L2CAP Channel SDU data buffer released event.
\n See @ref ble_l2cap_evt_ch_sdu_buf_released_t. */
BLE_L2CAP_EVT_CH_CREDIT = BLE_L2CAP_EVT_BASE + 5, /**< L2CAP Channel Credit received.
\n See @ref ble_l2cap_evt_ch_credit_t. */
BLE_L2CAP_EVT_CH_RX = BLE_L2CAP_EVT_BASE + 6, /**< L2CAP Channel SDU received.
\n See @ref ble_l2cap_evt_ch_rx_t. */
BLE_L2CAP_EVT_CH_TX = BLE_L2CAP_EVT_BASE + 7, /**< L2CAP Channel SDU transmitted.
\n See @ref ble_l2cap_evt_ch_tx_t. */
};
/** @} */
@@ -149,8 +149,9 @@ enum BLE_L2CAP_EVTS {
#define BLE_L2CAP_CH_STATUS_CODE_INVALID_SCID (0x0009) /**< Invalid Source CID. */
#define BLE_L2CAP_CH_STATUS_CODE_SCID_ALLOCATED (0x000A) /**< Source CID already allocated. */
#define BLE_L2CAP_CH_STATUS_CODE_UNACCEPTABLE_PARAMS (0x000B) /**< Unacceptable parameters. */
#define BLE_L2CAP_CH_STATUS_CODE_NOT_UNDERSTOOD (0x8000) /**< Command Reject received instead of LE Credit Based Connection Response. */
#define BLE_L2CAP_CH_STATUS_CODE_TIMEOUT (0xC000) /**< Operation timed out. */
#define BLE_L2CAP_CH_STATUS_CODE_NOT_UNDERSTOOD \
(0x8000) /**< Command Reject received instead of LE Credit Based Connection Response. */
#define BLE_L2CAP_CH_STATUS_CODE_TIMEOUT (0xC000) /**< Operation timed out. */
/** @} */
/** @} */
@@ -171,120 +172,120 @@ enum BLE_L2CAP_EVTS {
* @retval ::NRF_ERROR_NO_MEM rx_mps or tx_mps is set too high.
*/
typedef struct {
uint16_t rx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP shall
be able to receive on L2CAP channels on connections with this
configuration. The minimum value is @ref BLE_L2CAP_MPS_MIN. */
uint16_t tx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP shall
be able to transmit on L2CAP channels on connections with this
configuration. The minimum value is @ref BLE_L2CAP_MPS_MIN. */
uint8_t rx_queue_size; /**< Number of SDU data buffers that can be queued for reception per
L2CAP channel. The minimum value is one. */
uint8_t tx_queue_size; /**< Number of SDU data buffers that can be queued for transmission
per L2CAP channel. The minimum value is one. */
uint8_t ch_count; /**< Number of L2CAP channels the application can create per connection
with this configuration. The default value is zero, the maximum
value is @ref BLE_L2CAP_CH_COUNT_MAX.
@note if this parameter is set to zero, all other parameters in
@ref ble_l2cap_conn_cfg_t are ignored. */
uint16_t rx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP shall
be able to receive on L2CAP channels on connections with this
configuration. The minimum value is @ref BLE_L2CAP_MPS_MIN. */
uint16_t tx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP shall
be able to transmit on L2CAP channels on connections with this
configuration. The minimum value is @ref BLE_L2CAP_MPS_MIN. */
uint8_t rx_queue_size; /**< Number of SDU data buffers that can be queued for reception per
L2CAP channel. The minimum value is one. */
uint8_t tx_queue_size; /**< Number of SDU data buffers that can be queued for transmission
per L2CAP channel. The minimum value is one. */
uint8_t ch_count; /**< Number of L2CAP channels the application can create per connection
with this configuration. The default value is zero, the maximum
value is @ref BLE_L2CAP_CH_COUNT_MAX.
@note if this parameter is set to zero, all other parameters in
@ref ble_l2cap_conn_cfg_t are ignored. */
} ble_l2cap_conn_cfg_t;
/**@brief L2CAP channel RX parameters. */
typedef struct {
uint16_t rx_mtu; /**< The maximum L2CAP SDU size, in bytes, that L2CAP shall be able to
receive on this L2CAP channel.
- Must be equal to or greater than @ref BLE_L2CAP_MTU_MIN. */
uint16_t rx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP shall be
able to receive on this L2CAP channel.
- Must be equal to or greater than @ref BLE_L2CAP_MPS_MIN.
- Must be equal to or less than @ref ble_l2cap_conn_cfg_t::rx_mps. */
ble_data_t sdu_buf; /**< SDU data buffer for reception.
- If @ref ble_data_t::p_data is non-NULL, initial credits are
issued to the peer.
- If @ref ble_data_t::p_data is NULL, no initial credits are
issued to the peer. */
uint16_t rx_mtu; /**< The maximum L2CAP SDU size, in bytes, that L2CAP shall be able to
receive on this L2CAP channel.
- Must be equal to or greater than @ref BLE_L2CAP_MTU_MIN. */
uint16_t rx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP shall be
able to receive on this L2CAP channel.
- Must be equal to or greater than @ref BLE_L2CAP_MPS_MIN.
- Must be equal to or less than @ref ble_l2cap_conn_cfg_t::rx_mps. */
ble_data_t sdu_buf; /**< SDU data buffer for reception.
- If @ref ble_data_t::p_data is non-NULL, initial credits are
issued to the peer.
- If @ref ble_data_t::p_data is NULL, no initial credits are
issued to the peer. */
} ble_l2cap_ch_rx_params_t;
/**@brief L2CAP channel setup parameters. */
typedef struct {
ble_l2cap_ch_rx_params_t rx_params; /**< L2CAP channel RX parameters. */
uint16_t le_psm; /**< LE Protocol/Service Multiplexer. Used when requesting
setup of an L2CAP channel, ignored otherwise. */
uint16_t status; /**< Status code, see @ref BLE_L2CAP_CH_STATUS_CODES.
Used when replying to a setup request of an L2CAP
channel, ignored otherwise. */
ble_l2cap_ch_rx_params_t rx_params; /**< L2CAP channel RX parameters. */
uint16_t le_psm; /**< LE Protocol/Service Multiplexer. Used when requesting
setup of an L2CAP channel, ignored otherwise. */
uint16_t status; /**< Status code, see @ref BLE_L2CAP_CH_STATUS_CODES.
Used when replying to a setup request of an L2CAP
channel, ignored otherwise. */
} ble_l2cap_ch_setup_params_t;
/**@brief L2CAP channel TX parameters. */
typedef struct {
uint16_t tx_mtu; /**< The maximum L2CAP SDU size, in bytes, that L2CAP is able to
transmit on this L2CAP channel. */
uint16_t peer_mps; /**< The maximum L2CAP PDU payload size, in bytes, that the peer is
able to receive on this L2CAP channel. */
uint16_t tx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP is able
to transmit on this L2CAP channel. This is effective tx_mps,
selected by the SoftDevice as
MIN( @ref ble_l2cap_ch_tx_params_t::peer_mps, @ref ble_l2cap_conn_cfg_t::tx_mps ) */
uint16_t credits; /**< Initial credits given by the peer. */
uint16_t tx_mtu; /**< The maximum L2CAP SDU size, in bytes, that L2CAP is able to
transmit on this L2CAP channel. */
uint16_t peer_mps; /**< The maximum L2CAP PDU payload size, in bytes, that the peer is
able to receive on this L2CAP channel. */
uint16_t tx_mps; /**< The maximum L2CAP PDU payload size, in bytes, that L2CAP is able
to transmit on this L2CAP channel. This is effective tx_mps,
selected by the SoftDevice as
MIN( @ref ble_l2cap_ch_tx_params_t::peer_mps, @ref ble_l2cap_conn_cfg_t::tx_mps ) */
uint16_t credits; /**< Initial credits given by the peer. */
} ble_l2cap_ch_tx_params_t;
/**@brief L2CAP Channel Setup Request event. */
typedef struct {
ble_l2cap_ch_tx_params_t tx_params; /**< L2CAP channel TX parameters. */
uint16_t le_psm; /**< LE Protocol/Service Multiplexer. */
ble_l2cap_ch_tx_params_t tx_params; /**< L2CAP channel TX parameters. */
uint16_t le_psm; /**< LE Protocol/Service Multiplexer. */
} ble_l2cap_evt_ch_setup_request_t;
/**@brief L2CAP Channel Setup Refused event. */
typedef struct {
uint8_t source; /**< Source, see @ref BLE_L2CAP_CH_SETUP_REFUSED_SRCS */
uint16_t status; /**< Status code, see @ref BLE_L2CAP_CH_STATUS_CODES */
uint8_t source; /**< Source, see @ref BLE_L2CAP_CH_SETUP_REFUSED_SRCS */
uint16_t status; /**< Status code, see @ref BLE_L2CAP_CH_STATUS_CODES */
} ble_l2cap_evt_ch_setup_refused_t;
/**@brief L2CAP Channel Setup Completed event. */
typedef struct {
ble_l2cap_ch_tx_params_t tx_params; /**< L2CAP channel TX parameters. */
ble_l2cap_ch_tx_params_t tx_params; /**< L2CAP channel TX parameters. */
} ble_l2cap_evt_ch_setup_t;
/**@brief L2CAP Channel SDU Data Buffer Released event. */
typedef struct {
ble_data_t sdu_buf; /**< Returned reception or transmission SDU data buffer. The SoftDevice
returns SDU data buffers supplied by the application, which have
not yet been returned previously via a @ref BLE_L2CAP_EVT_CH_RX or
@ref BLE_L2CAP_EVT_CH_TX event. */
ble_data_t sdu_buf; /**< Returned reception or transmission SDU data buffer. The SoftDevice
returns SDU data buffers supplied by the application, which have
not yet been returned previously via a @ref BLE_L2CAP_EVT_CH_RX or
@ref BLE_L2CAP_EVT_CH_TX event. */
} ble_l2cap_evt_ch_sdu_buf_released_t;
/**@brief L2CAP Channel Credit received event. */
typedef struct {
uint16_t credits; /**< Additional credits given by the peer. */
uint16_t credits; /**< Additional credits given by the peer. */
} ble_l2cap_evt_ch_credit_t;
/**@brief L2CAP Channel received SDU event. */
typedef struct {
uint16_t sdu_len; /**< Total SDU length, in bytes. */
ble_data_t sdu_buf; /**< SDU data buffer.
@note If there is not enough space in the buffer
(sdu_buf.len < sdu_len) then the rest of the SDU will be
silently discarded by the SoftDevice. */
uint16_t sdu_len; /**< Total SDU length, in bytes. */
ble_data_t sdu_buf; /**< SDU data buffer.
@note If there is not enough space in the buffer
(sdu_buf.len < sdu_len) then the rest of the SDU will be
silently discarded by the SoftDevice. */
} ble_l2cap_evt_ch_rx_t;
/**@brief L2CAP Channel transmitted SDU event. */
typedef struct {
ble_data_t sdu_buf; /**< SDU data buffer. */
ble_data_t sdu_buf; /**< SDU data buffer. */
} ble_l2cap_evt_ch_tx_t;
/**@brief L2CAP event structure. */
typedef struct {
uint16_t conn_handle; /**< Connection Handle on which the event occured. */
uint16_t local_cid; /**< Local Channel ID of the L2CAP channel, or
@ref BLE_L2CAP_CID_INVALID if not present. */
union {
ble_l2cap_evt_ch_setup_request_t ch_setup_request; /**< L2CAP Channel Setup Request Event Parameters. */
ble_l2cap_evt_ch_setup_refused_t ch_setup_refused; /**< L2CAP Channel Setup Refused Event Parameters. */
ble_l2cap_evt_ch_setup_t ch_setup; /**< L2CAP Channel Setup Completed Event Parameters. */
ble_l2cap_evt_ch_sdu_buf_released_t ch_sdu_buf_released; /**< L2CAP Channel SDU Data Buffer Released Event Parameters. */
ble_l2cap_evt_ch_credit_t credit; /**< L2CAP Channel Credit Received Event Parameters. */
ble_l2cap_evt_ch_rx_t rx; /**< L2CAP Channel SDU Received Event Parameters. */
ble_l2cap_evt_ch_tx_t tx; /**< L2CAP Channel SDU Transmitted Event Parameters. */
} params; /**< Event Parameters. */
uint16_t conn_handle; /**< Connection Handle on which the event occured. */
uint16_t local_cid; /**< Local Channel ID of the L2CAP channel, or
@ref BLE_L2CAP_CID_INVALID if not present. */
union {
ble_l2cap_evt_ch_setup_request_t ch_setup_request; /**< L2CAP Channel Setup Request Event Parameters. */
ble_l2cap_evt_ch_setup_refused_t ch_setup_refused; /**< L2CAP Channel Setup Refused Event Parameters. */
ble_l2cap_evt_ch_setup_t ch_setup; /**< L2CAP Channel Setup Completed Event Parameters. */
ble_l2cap_evt_ch_sdu_buf_released_t ch_sdu_buf_released; /**< L2CAP Channel SDU Data Buffer Released Event Parameters. */
ble_l2cap_evt_ch_credit_t credit; /**< L2CAP Channel Credit Received Event Parameters. */
ble_l2cap_evt_ch_rx_t rx; /**< L2CAP Channel SDU Received Event Parameters. */
ble_l2cap_evt_ch_tx_t tx; /**< L2CAP Channel SDU Transmitted Event Parameters. */
} params; /**< Event Parameters. */
} ble_l2cap_evt_t;
/** @} */
+72 -70
View File
@@ -101,77 +101,79 @@ extern "C" {
* @note Retrieved from
* http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
* @{ */
#define BLE_APPEARANCE_UNKNOWN 0 /**< Unknown. */
#define BLE_APPEARANCE_GENERIC_PHONE 64 /**< Generic Phone. */
#define BLE_APPEARANCE_GENERIC_COMPUTER 128 /**< Generic Computer. */
#define BLE_APPEARANCE_GENERIC_WATCH 192 /**< Generic Watch. */
#define BLE_APPEARANCE_WATCH_SPORTS_WATCH 193 /**< Watch: Sports Watch. */
#define BLE_APPEARANCE_GENERIC_CLOCK 256 /**< Generic Clock. */
#define BLE_APPEARANCE_GENERIC_DISPLAY 320 /**< Generic Display. */
#define BLE_APPEARANCE_GENERIC_REMOTE_CONTROL 384 /**< Generic Remote Control. */
#define BLE_APPEARANCE_GENERIC_EYE_GLASSES 448 /**< Generic Eye-glasses. */
#define BLE_APPEARANCE_GENERIC_TAG 512 /**< Generic Tag. */
#define BLE_APPEARANCE_GENERIC_KEYRING 576 /**< Generic Keyring. */
#define BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 640 /**< Generic Media Player. */
#define BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 704 /**< Generic Barcode Scanner. */
#define BLE_APPEARANCE_GENERIC_THERMOMETER 768 /**< Generic Thermometer. */
#define BLE_APPEARANCE_THERMOMETER_EAR 769 /**< Thermometer: Ear. */
#define BLE_APPEARANCE_GENERIC_HEART_RATE_SENSOR 832 /**< Generic Heart rate Sensor. */
#define BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT 833 /**< Heart Rate Sensor: Heart Rate Belt. */
#define BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 896 /**< Generic Blood Pressure. */
#define BLE_APPEARANCE_BLOOD_PRESSURE_ARM 897 /**< Blood Pressure: Arm. */
#define BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 898 /**< Blood Pressure: Wrist. */
#define BLE_APPEARANCE_GENERIC_HID 960 /**< Human Interface Device (HID). */
#define BLE_APPEARANCE_HID_KEYBOARD 961 /**< Keyboard (HID Subtype). */
#define BLE_APPEARANCE_HID_MOUSE 962 /**< Mouse (HID Subtype). */
#define BLE_APPEARANCE_HID_JOYSTICK 963 /**< Joystick (HID Subtype). */
#define BLE_APPEARANCE_HID_GAMEPAD 964 /**< Gamepad (HID Subtype). */
#define BLE_APPEARANCE_HID_DIGITIZERSUBTYPE 965 /**< Digitizer Tablet (HID Subtype). */
#define BLE_APPEARANCE_HID_CARD_READER 966 /**< Card Reader (HID Subtype). */
#define BLE_APPEARANCE_HID_DIGITAL_PEN 967 /**< Digital Pen (HID Subtype). */
#define BLE_APPEARANCE_HID_BARCODE 968 /**< Barcode Scanner (HID Subtype). */
#define BLE_APPEARANCE_GENERIC_GLUCOSE_METER 1024 /**< Generic Glucose Meter. */
#define BLE_APPEARANCE_GENERIC_RUNNING_WALKING_SENSOR 1088 /**< Generic Running Walking Sensor. */
#define BLE_APPEARANCE_RUNNING_WALKING_SENSOR_IN_SHOE 1089 /**< Running Walking Sensor: In-Shoe. */
#define BLE_APPEARANCE_RUNNING_WALKING_SENSOR_ON_SHOE 1090 /**< Running Walking Sensor: On-Shoe. */
#define BLE_APPEARANCE_RUNNING_WALKING_SENSOR_ON_HIP 1091 /**< Running Walking Sensor: On-Hip. */
#define BLE_APPEARANCE_GENERIC_CYCLING 1152 /**< Generic Cycling. */
#define BLE_APPEARANCE_CYCLING_CYCLING_COMPUTER 1153 /**< Cycling: Cycling Computer. */
#define BLE_APPEARANCE_CYCLING_SPEED_SENSOR 1154 /**< Cycling: Speed Sensor. */
#define BLE_APPEARANCE_CYCLING_CADENCE_SENSOR 1155 /**< Cycling: Cadence Sensor. */
#define BLE_APPEARANCE_CYCLING_POWER_SENSOR 1156 /**< Cycling: Power Sensor. */
#define BLE_APPEARANCE_CYCLING_SPEED_CADENCE_SENSOR 1157 /**< Cycling: Speed and Cadence Sensor. */
#define BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 3136 /**< Generic Pulse Oximeter. */
#define BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 3137 /**< Fingertip (Pulse Oximeter subtype). */
#define BLE_APPEARANCE_PULSE_OXIMETER_WRIST_WORN 3138 /**< Wrist Worn(Pulse Oximeter subtype). */
#define BLE_APPEARANCE_GENERIC_WEIGHT_SCALE 3200 /**< Generic Weight Scale. */
#define BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS_ACT 5184 /**< Generic Outdoor Sports Activity. */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_DISP 5185 /**< Location Display Device (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_AND_NAV_DISP 5186 /**< Location and Navigation Display Device (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_POD 5187 /**< Location Pod (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_AND_NAV_POD 5188 /**< Location and Navigation Pod (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_UNKNOWN 0 /**< Unknown. */
#define BLE_APPEARANCE_GENERIC_PHONE 64 /**< Generic Phone. */
#define BLE_APPEARANCE_GENERIC_COMPUTER 128 /**< Generic Computer. */
#define BLE_APPEARANCE_GENERIC_WATCH 192 /**< Generic Watch. */
#define BLE_APPEARANCE_WATCH_SPORTS_WATCH 193 /**< Watch: Sports Watch. */
#define BLE_APPEARANCE_GENERIC_CLOCK 256 /**< Generic Clock. */
#define BLE_APPEARANCE_GENERIC_DISPLAY 320 /**< Generic Display. */
#define BLE_APPEARANCE_GENERIC_REMOTE_CONTROL 384 /**< Generic Remote Control. */
#define BLE_APPEARANCE_GENERIC_EYE_GLASSES 448 /**< Generic Eye-glasses. */
#define BLE_APPEARANCE_GENERIC_TAG 512 /**< Generic Tag. */
#define BLE_APPEARANCE_GENERIC_KEYRING 576 /**< Generic Keyring. */
#define BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 640 /**< Generic Media Player. */
#define BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 704 /**< Generic Barcode Scanner. */
#define BLE_APPEARANCE_GENERIC_THERMOMETER 768 /**< Generic Thermometer. */
#define BLE_APPEARANCE_THERMOMETER_EAR 769 /**< Thermometer: Ear. */
#define BLE_APPEARANCE_GENERIC_HEART_RATE_SENSOR 832 /**< Generic Heart rate Sensor. */
#define BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT 833 /**< Heart Rate Sensor: Heart Rate Belt. */
#define BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 896 /**< Generic Blood Pressure. */
#define BLE_APPEARANCE_BLOOD_PRESSURE_ARM 897 /**< Blood Pressure: Arm. */
#define BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 898 /**< Blood Pressure: Wrist. */
#define BLE_APPEARANCE_GENERIC_HID 960 /**< Human Interface Device (HID). */
#define BLE_APPEARANCE_HID_KEYBOARD 961 /**< Keyboard (HID Subtype). */
#define BLE_APPEARANCE_HID_MOUSE 962 /**< Mouse (HID Subtype). */
#define BLE_APPEARANCE_HID_JOYSTICK 963 /**< Joystick (HID Subtype). */
#define BLE_APPEARANCE_HID_GAMEPAD 964 /**< Gamepad (HID Subtype). */
#define BLE_APPEARANCE_HID_DIGITIZERSUBTYPE 965 /**< Digitizer Tablet (HID Subtype). */
#define BLE_APPEARANCE_HID_CARD_READER 966 /**< Card Reader (HID Subtype). */
#define BLE_APPEARANCE_HID_DIGITAL_PEN 967 /**< Digital Pen (HID Subtype). */
#define BLE_APPEARANCE_HID_BARCODE 968 /**< Barcode Scanner (HID Subtype). */
#define BLE_APPEARANCE_GENERIC_GLUCOSE_METER 1024 /**< Generic Glucose Meter. */
#define BLE_APPEARANCE_GENERIC_RUNNING_WALKING_SENSOR 1088 /**< Generic Running Walking Sensor. */
#define BLE_APPEARANCE_RUNNING_WALKING_SENSOR_IN_SHOE 1089 /**< Running Walking Sensor: In-Shoe. */
#define BLE_APPEARANCE_RUNNING_WALKING_SENSOR_ON_SHOE 1090 /**< Running Walking Sensor: On-Shoe. */
#define BLE_APPEARANCE_RUNNING_WALKING_SENSOR_ON_HIP 1091 /**< Running Walking Sensor: On-Hip. */
#define BLE_APPEARANCE_GENERIC_CYCLING 1152 /**< Generic Cycling. */
#define BLE_APPEARANCE_CYCLING_CYCLING_COMPUTER 1153 /**< Cycling: Cycling Computer. */
#define BLE_APPEARANCE_CYCLING_SPEED_SENSOR 1154 /**< Cycling: Speed Sensor. */
#define BLE_APPEARANCE_CYCLING_CADENCE_SENSOR 1155 /**< Cycling: Cadence Sensor. */
#define BLE_APPEARANCE_CYCLING_POWER_SENSOR 1156 /**< Cycling: Power Sensor. */
#define BLE_APPEARANCE_CYCLING_SPEED_CADENCE_SENSOR 1157 /**< Cycling: Speed and Cadence Sensor. */
#define BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 3136 /**< Generic Pulse Oximeter. */
#define BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 3137 /**< Fingertip (Pulse Oximeter subtype). */
#define BLE_APPEARANCE_PULSE_OXIMETER_WRIST_WORN 3138 /**< Wrist Worn(Pulse Oximeter subtype). */
#define BLE_APPEARANCE_GENERIC_WEIGHT_SCALE 3200 /**< Generic Weight Scale. */
#define BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS_ACT 5184 /**< Generic Outdoor Sports Activity. */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_DISP 5185 /**< Location Display Device (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_AND_NAV_DISP \
5186 /**< Location and Navigation Display Device (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_POD 5187 /**< Location Pod (Outdoor Sports Activity subtype). */
#define BLE_APPEARANCE_OUTDOOR_SPORTS_ACT_LOC_AND_NAV_POD \
5188 /**< Location and Navigation Pod (Outdoor Sports Activity subtype). */
/** @} */
/** @brief Set .type and .uuid fields of ble_uuid_struct to specified UUID value. */
#define BLE_UUID_BLE_ASSIGN(instance, value) \
do { \
instance.type = BLE_UUID_TYPE_BLE; \
instance.uuid = value; \
} while (0)
#define BLE_UUID_BLE_ASSIGN(instance, value) \
do { \
instance.type = BLE_UUID_TYPE_BLE; \
instance.uuid = value; \
} while (0)
/** @brief Copy type and uuid members from src to dst ble_uuid_t pointer. Both pointers must be valid/non-null. */
#define BLE_UUID_COPY_PTR(dst, src) \
do { \
(dst)->type = (src)->type; \
(dst)->uuid = (src)->uuid; \
} while (0)
#define BLE_UUID_COPY_PTR(dst, src) \
do { \
(dst)->type = (src)->type; \
(dst)->uuid = (src)->uuid; \
} while (0)
/** @brief Copy type and uuid members from src to dst ble_uuid_t struct. */
#define BLE_UUID_COPY_INST(dst, src) \
do { \
(dst).type = (src).type; \
(dst).uuid = (src).uuid; \
} while (0)
#define BLE_UUID_COPY_INST(dst, src) \
do { \
(dst).type = (src).type; \
(dst).uuid = (src).uuid; \
} while (0)
/** @brief Compare for equality both type and uuid members of two (valid, non-null) ble_uuid_t pointers. */
#define BLE_UUID_EQ(p_uuid1, p_uuid2) (((p_uuid1)->type == (p_uuid2)->type) && ((p_uuid1)->uuid == (p_uuid2)->uuid))
@@ -186,20 +188,20 @@ extern "C" {
/** @brief 128 bit UUID values. */
typedef struct {
uint8_t uuid128[16]; /**< Little-Endian UUID bytes. */
uint8_t uuid128[16]; /**< Little-Endian UUID bytes. */
} ble_uuid128_t;
/** @brief Bluetooth Low Energy UUID type, encapsulates both 16-bit and 128-bit UUIDs. */
typedef struct {
uint16_t uuid; /**< 16-bit UUID value or octets 12-13 of 128-bit UUID. */
uint8_t type; /**< UUID type, see @ref BLE_UUID_TYPES. If type is @ref BLE_UUID_TYPE_UNKNOWN, the value of uuid is
undefined. */
uint16_t uuid; /**< 16-bit UUID value or octets 12-13 of 128-bit UUID. */
uint8_t
type; /**< UUID type, see @ref BLE_UUID_TYPES. If type is @ref BLE_UUID_TYPE_UNKNOWN, the value of uuid is undefined. */
} ble_uuid_t;
/**@brief Data structure. */
typedef struct {
uint8_t *p_data; /**< Pointer to the data buffer provided to/from the application. */
uint16_t len; /**< Length of the data buffer, in bytes. */
uint8_t *p_data; /**< Pointer to the data buffer provided to/from the application. */
uint16_t len; /**< Length of the data buffer, in bytes. */
} ble_data_t;
/** @} */
+30 -31
View File
@@ -86,21 +86,21 @@ This is the offset where the first byte of the SoftDevice hex file is written. *
/**@brief nRF Master Boot Record API SVC numbers. */
enum NRF_MBR_SVCS {
SD_MBR_COMMAND = MBR_SVC_BASE, /**< ::sd_mbr_command */
SD_MBR_COMMAND = MBR_SVC_BASE, /**< ::sd_mbr_command */
};
/**@brief Possible values for ::sd_mbr_command_t.command */
enum NRF_MBR_COMMANDS {
SD_MBR_COMMAND_COPY_BL, /**< Copy a new BootLoader. @see ::sd_mbr_command_copy_bl_t*/
SD_MBR_COMMAND_COPY_SD, /**< Copy a new SoftDevice. @see ::sd_mbr_command_copy_sd_t*/
SD_MBR_COMMAND_INIT_SD, /**< Initialize forwarding interrupts to SD, and run reset function in SD. Does not require
any parameters in ::sd_mbr_command_t params.*/
SD_MBR_COMMAND_COMPARE, /**< This command works like memcmp. @see ::sd_mbr_command_compare_t*/
SD_MBR_COMMAND_VECTOR_TABLE_BASE_SET, /**< Change the address the MBR starts after a reset. @see
::sd_mbr_command_vector_table_base_set_t*/
SD_MBR_COMMAND_RESERVED,
SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET, /**< Start forwarding all interrupts to this address. @see
::sd_mbr_command_irq_forward_address_set_t*/
SD_MBR_COMMAND_COPY_BL, /**< Copy a new BootLoader. @see ::sd_mbr_command_copy_bl_t*/
SD_MBR_COMMAND_COPY_SD, /**< Copy a new SoftDevice. @see ::sd_mbr_command_copy_sd_t*/
SD_MBR_COMMAND_INIT_SD, /**< Initialize forwarding interrupts to SD, and run reset function in SD. Does not require any
parameters in ::sd_mbr_command_t params.*/
SD_MBR_COMMAND_COMPARE, /**< This command works like memcmp. @see ::sd_mbr_command_compare_t*/
SD_MBR_COMMAND_VECTOR_TABLE_BASE_SET, /**< Change the address the MBR starts after a reset. @see
::sd_mbr_command_vector_table_base_set_t*/
SD_MBR_COMMAND_RESERVED,
SD_MBR_COMMAND_IRQ_FORWARD_ADDRESS_SET, /**< Start forwarding all interrupts to this address. @see
::sd_mbr_command_irq_forward_address_set_t*/
};
/** @} */
@@ -117,13 +117,12 @@ enum NRF_MBR_COMMANDS {
* The user of this function is responsible for setting the BPROT registers.
*
* @retval ::NRF_SUCCESS indicates that the contents of the memory blocks where copied correctly.
* @retval ::NRF_ERROR_INTERNAL indicates that the contents of the memory blocks where not verified correctly after
* copying.
* @retval ::NRF_ERROR_INTERNAL indicates that the contents of the memory blocks where not verified correctly after copying.
*/
typedef struct {
uint32_t *src; /**< Pointer to the source of data to be copied.*/
uint32_t *dst; /**< Pointer to the destination where the content is to be copied.*/
uint32_t len; /**< Number of 32 bit words to copy. Must be a multiple of @ref MBR_PAGE_SIZE_IN_WORDS words.*/
uint32_t *src; /**< Pointer to the source of data to be copied.*/
uint32_t *dst; /**< Pointer to the destination where the content is to be copied.*/
uint32_t len; /**< Number of 32 bit words to copy. Must be a multiple of @ref MBR_PAGE_SIZE_IN_WORDS words.*/
} sd_mbr_command_copy_sd_t;
/**@brief This command works like memcmp, but takes the length in words.
@@ -132,9 +131,9 @@ typedef struct {
* @retval ::NRF_ERROR_NULL indicates that the contents of the memory blocks are not equal.
*/
typedef struct {
uint32_t *ptr1; /**< Pointer to block of memory. */
uint32_t *ptr2; /**< Pointer to block of memory. */
uint32_t len; /**< Number of 32 bit words to compare.*/
uint32_t *ptr1; /**< Pointer to block of memory. */
uint32_t *ptr2; /**< Pointer to block of memory. */
uint32_t len; /**< Number of 32 bit words to compare.*/
} sd_mbr_command_compare_t;
/**@brief This command copies a new BootLoader.
@@ -160,8 +159,8 @@ typedef struct {
* @retval ::NRF_ERROR_NO_MEM No MBR parameter page is provided. See @ref sd_mbr_command.
*/
typedef struct {
uint32_t *bl_src; /**< Pointer to the source of the bootloader to be be copied.*/
uint32_t bl_len; /**< Number of 32 bit words to copy for BootLoader. */
uint32_t *bl_src; /**< Pointer to the source of the bootloader to be be copied.*/
uint32_t bl_len; /**< Number of 32 bit words to copy for BootLoader. */
} sd_mbr_command_copy_bl_t;
/**@brief Change the address the MBR starts after a reset
@@ -187,7 +186,7 @@ typedef struct {
* @retval ::NRF_ERROR_NO_MEM No MBR parameter page is provided. See @ref sd_mbr_command.
*/
typedef struct {
uint32_t address; /**< The base address of the interrupt vector table for forwarded interrupts.*/
uint32_t address; /**< The base address of the interrupt vector table for forwarded interrupts.*/
} sd_mbr_command_vector_table_base_set_t;
/**@brief Sets the base address of the interrupt vector table for interrupts forwarded from the MBR
@@ -198,7 +197,7 @@ typedef struct {
* @retval ::NRF_SUCCESS
*/
typedef struct {
uint32_t address; /**< The base address of the interrupt vector table for forwarded interrupts.*/
uint32_t address; /**< The base address of the interrupt vector table for forwarded interrupts.*/
} sd_mbr_command_irq_forward_address_set_t;
/**@brief Input structure containing data used when calling ::sd_mbr_command
@@ -208,14 +207,14 @@ typedef struct {
* @ref SD_MBR_COMMAND_INIT_SD is set, it is not necessary to set any values under params.
*/
typedef struct {
uint32_t command; /**< Type of command to be issued. See @ref NRF_MBR_COMMANDS. */
union {
sd_mbr_command_copy_sd_t copy_sd; /**< Parameters for copy SoftDevice.*/
sd_mbr_command_compare_t compare; /**< Parameters for verify.*/
sd_mbr_command_copy_bl_t copy_bl; /**< Parameters for copy BootLoader. Requires parameter page. */
sd_mbr_command_vector_table_base_set_t base_set; /**< Parameters for vector table base set. Requires parameter page.*/
sd_mbr_command_irq_forward_address_set_t irq_forward_address_set; /**< Parameters for irq forward address set*/
} params; /**< Command parameters. */
uint32_t command; /**< Type of command to be issued. See @ref NRF_MBR_COMMANDS. */
union {
sd_mbr_command_copy_sd_t copy_sd; /**< Parameters for copy SoftDevice.*/
sd_mbr_command_compare_t compare; /**< Parameters for verify.*/
sd_mbr_command_copy_bl_t copy_bl; /**< Parameters for copy BootLoader. Requires parameter page. */
sd_mbr_command_vector_table_base_set_t base_set; /**< Parameters for vector table base set. Requires parameter page.*/
sd_mbr_command_irq_forward_address_set_t irq_forward_address_set; /**< Parameters for irq forward address set*/
} params; /**< Command parameters. */
} sd_mbr_command_t;
/** @} */
@@ -56,10 +56,11 @@ extern "C" {
#endif
#define NRF_ERROR_SDM_LFCLK_SOURCE_UNKNOWN (NRF_ERROR_SDM_BASE_NUM + 0) ///< Unknown LFCLK source.
#define NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION \
(NRF_ERROR_SDM_BASE_NUM + 1) ///< Incorrect interrupt configuration (can be caused by using illegal priority levels,
///< or having enabled SoftDevice interrupts).
#define NRF_ERROR_SDM_INCORRECT_CLENR0 (NRF_ERROR_SDM_BASE_NUM + 2) ///< Incorrect CLENR0 (can be caused by erroneous SoftDevice flashing).
#define NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION \
(NRF_ERROR_SDM_BASE_NUM + 1) ///< Incorrect interrupt configuration (can be caused by using illegal priority levels, or having
///< enabled SoftDevice interrupts).
#define NRF_ERROR_SDM_INCORRECT_CLENR0 \
(NRF_ERROR_SDM_BASE_NUM + 2) ///< Incorrect CLENR0 (can be caused by erroneous SoftDevice flashing).
#ifdef __cplusplus
}
+168 -152
View File
@@ -71,26 +71,27 @@ extern "C" {
/**@defgroup NRF_NVIC_ISER_DEFINES SoftDevice NVIC internal definitions
* @{ */
#define __NRF_NVIC_NVMC_IRQn \
(30) /**< The peripheral ID of the NVMC. IRQ numbers are used to identify peripherals, but the NVMC doesn't have an \
IRQ number in the MDK. */
#define __NRF_NVIC_NVMC_IRQn \
(30) /**< The peripheral ID of the NVMC. IRQ numbers are used to identify peripherals, but the NVMC doesn't have an IRQ \
number in the MDK. */
#define __NRF_NVIC_ISER_COUNT (2) /**< The number of ISER/ICER registers in the NVIC that are used. */
/**@brief Interrupt priority levels used by the SoftDevice. */
#define __NRF_NVIC_SD_IRQ_PRIOS \
((uint8_t)((1U << 0) /**< Priority level high .*/ \
| (1U << 1) /**< Priority level medium. */ \
| (1U << 4) /**< Priority level low. */ \
))
#define __NRF_NVIC_SD_IRQ_PRIOS \
((uint8_t)((1U << 0) /**< Priority level high .*/ \
| (1U << 1) /**< Priority level medium. */ \
| (1U << 4) /**< Priority level low. */ \
))
/**@brief Interrupt priority levels available to the application. */
#define __NRF_NVIC_APP_IRQ_PRIOS ((uint8_t)~__NRF_NVIC_SD_IRQ_PRIOS)
/**@brief Interrupts used by the SoftDevice, with IRQn in the range 0-31. */
#define __NRF_NVIC_SD_IRQS_0 \
((uint32_t)((1U << POWER_CLOCK_IRQn) | (1U << RADIO_IRQn) | (1U << RTC0_IRQn) | (1U << TIMER0_IRQn) | (1U << RNG_IRQn) | (1U << ECB_IRQn) | \
(1U << CCM_AAR_IRQn) | (1U << TEMP_IRQn) | (1U << __NRF_NVIC_NVMC_IRQn) | (1U << (uint32_t)SWI5_IRQn)))
#define __NRF_NVIC_SD_IRQS_0 \
((uint32_t)((1U << POWER_CLOCK_IRQn) | (1U << RADIO_IRQn) | (1U << RTC0_IRQn) | (1U << TIMER0_IRQn) | (1U << RNG_IRQn) | \
(1U << ECB_IRQn) | (1U << CCM_AAR_IRQn) | (1U << TEMP_IRQn) | (1U << __NRF_NVIC_NVMC_IRQn) | \
(1U << (uint32_t)SWI5_IRQn)))
/**@brief Interrupts used by the SoftDevice, with IRQn in the range 32-63. */
#define __NRF_NVIC_SD_IRQS_1 ((uint32_t)0)
@@ -110,8 +111,8 @@ extern "C" {
/**@brief Type representing the state struct for the SoftDevice NVIC module. */
typedef struct {
uint32_t volatile __irq_masks[__NRF_NVIC_ISER_COUNT]; /**< IRQs enabled by the application in the NVIC. */
uint32_t volatile __cr_flag; /**< Non-zero if already in a critical region */
uint32_t volatile __irq_masks[__NRF_NVIC_ISER_COUNT]; /**< IRQs enabled by the application in the NVIC. */
uint32_t volatile __cr_flag; /**< Non-zero if already in a critical region */
} nrf_nvic_state_t;
/**@brief Variable keeping the state for the SoftDevice NVIC module. This must be declared in an
@@ -161,8 +162,7 @@ __STATIC_INLINE uint32_t __sd_nvic_is_app_accessible_priority(uint32_t priority)
*
* @retval ::NRF_SUCCESS The interrupt was enabled.
* @retval ::NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE The interrupt is not available for the application.
* @retval ::NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED The interrupt has a priority not available for the
* application.
* @retval ::NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED The interrupt has a priority not available for the application.
*/
__STATIC_INLINE uint32_t sd_nvic_EnableIRQ(IRQn_Type IRQn);
@@ -226,8 +226,7 @@ __STATIC_INLINE uint32_t sd_nvic_ClearPendingIRQ(IRQn_Type IRQn);
*
* @retval ::NRF_SUCCESS The interrupt and priority level is available for the application.
* @retval ::NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE IRQn is not available for the application.
* @retval ::NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED The interrupt priority is not available for the
* application.
* @retval ::NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED The interrupt priority is not available for the application.
*/
__STATIC_INLINE uint32_t sd_nvic_SetPriority(IRQn_Type IRQn, uint32_t priority);
@@ -254,8 +253,8 @@ __STATIC_INLINE uint32_t sd_nvic_SystemReset(void);
/**@brief Enter critical region.
*
* @post Application interrupts will be disabled.
* @note sd_nvic_critical_region_enter() and ::sd_nvic_critical_region_exit() must be called in matching pairs inside
* each execution context
* @note sd_nvic_critical_region_enter() and ::sd_nvic_critical_region_exit() must be called in matching pairs inside each
* execution context
* @sa sd_nvic_critical_region_exit
*
* @param[out] p_is_nested_critical_region If 1, the application is now in a nested critical region.
@@ -281,145 +280,162 @@ __STATIC_INLINE uint32_t sd_nvic_critical_region_exit(uint8_t is_nested_critical
#ifndef SUPPRESS_INLINE_IMPLEMENTATION
__STATIC_INLINE int __sd_nvic_irq_disable(void) {
int pm = __get_PRIMASK();
__disable_irq();
return pm;
__STATIC_INLINE int __sd_nvic_irq_disable(void)
{
int pm = __get_PRIMASK();
__disable_irq();
return pm;
}
__STATIC_INLINE void __sd_nvic_irq_enable(void) { __enable_irq(); }
__STATIC_INLINE uint32_t __sd_nvic_app_accessible_irq(IRQn_Type IRQn) {
if (IRQn < 32) {
return ((1UL << IRQn) & __NRF_NVIC_APP_IRQS_0) != 0;
} else if (IRQn < 64) {
return ((1UL << (IRQn - 32)) & __NRF_NVIC_APP_IRQS_1) != 0;
} else {
return 1;
}
__STATIC_INLINE void __sd_nvic_irq_enable(void)
{
__enable_irq();
}
__STATIC_INLINE uint32_t __sd_nvic_is_app_accessible_priority(uint32_t priority) {
if ((priority >= (1 << __NVIC_PRIO_BITS)) || (((1 << priority) & __NRF_NVIC_APP_IRQ_PRIOS) == 0)) {
return 0;
}
return 1;
}
__STATIC_INLINE uint32_t sd_nvic_EnableIRQ(IRQn_Type IRQn) {
if (!__sd_nvic_app_accessible_irq(IRQn)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
if (!__sd_nvic_is_app_accessible_priority(NVIC_GetPriority(IRQn))) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED;
}
if (nrf_nvic_state.__cr_flag) {
nrf_nvic_state.__irq_masks[(uint32_t)((int32_t)IRQn) >> 5] |= (uint32_t)(1 << ((uint32_t)((int32_t)IRQn) & (uint32_t)0x1F));
} else {
NVIC_EnableIRQ(IRQn);
}
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_DisableIRQ(IRQn_Type IRQn) {
if (!__sd_nvic_app_accessible_irq(IRQn)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
if (nrf_nvic_state.__cr_flag) {
nrf_nvic_state.__irq_masks[(uint32_t)((int32_t)IRQn) >> 5] &= ~(1UL << ((uint32_t)(IRQn)&0x1F));
} else {
NVIC_DisableIRQ(IRQn);
}
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_GetPendingIRQ(IRQn_Type IRQn, uint32_t *p_pending_irq) {
if (__sd_nvic_app_accessible_irq(IRQn)) {
*p_pending_irq = NVIC_GetPendingIRQ(IRQn);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_SetPendingIRQ(IRQn_Type IRQn) {
if (__sd_nvic_app_accessible_irq(IRQn)) {
NVIC_SetPendingIRQ(IRQn);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_ClearPendingIRQ(IRQn_Type IRQn) {
if (__sd_nvic_app_accessible_irq(IRQn)) {
NVIC_ClearPendingIRQ(IRQn);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_SetPriority(IRQn_Type IRQn, uint32_t priority) {
if (!__sd_nvic_app_accessible_irq(IRQn)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
if (!__sd_nvic_is_app_accessible_priority(priority)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED;
}
NVIC_SetPriority(IRQn, (uint32_t)priority);
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_GetPriority(IRQn_Type IRQn, uint32_t *p_priority) {
if (__sd_nvic_app_accessible_irq(IRQn)) {
*p_priority = (NVIC_GetPriority(IRQn) & 0xFF);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_SystemReset(void) {
NVIC_SystemReset();
return NRF_ERROR_SOC_NVIC_SHOULD_NOT_RETURN;
}
__STATIC_INLINE uint32_t sd_nvic_critical_region_enter(uint8_t *p_is_nested_critical_region) {
int was_masked = __sd_nvic_irq_disable();
if (!nrf_nvic_state.__cr_flag) {
nrf_nvic_state.__cr_flag = 1;
nrf_nvic_state.__irq_masks[0] = (NVIC->ICER[0] & __NRF_NVIC_APP_IRQS_0);
NVIC->ICER[0] = __NRF_NVIC_APP_IRQS_0;
nrf_nvic_state.__irq_masks[1] = (NVIC->ICER[1] & __NRF_NVIC_APP_IRQS_1);
NVIC->ICER[1] = __NRF_NVIC_APP_IRQS_1;
*p_is_nested_critical_region = 0;
} else {
*p_is_nested_critical_region = 1;
}
if (!was_masked) {
__sd_nvic_irq_enable();
}
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_critical_region_exit(uint8_t is_nested_critical_region) {
if (nrf_nvic_state.__cr_flag && (is_nested_critical_region == 0)) {
int was_masked = __sd_nvic_irq_disable();
NVIC->ISER[0] = nrf_nvic_state.__irq_masks[0];
NVIC->ISER[1] = nrf_nvic_state.__irq_masks[1];
nrf_nvic_state.__cr_flag = 0;
if (!was_masked) {
__sd_nvic_irq_enable();
__STATIC_INLINE uint32_t __sd_nvic_app_accessible_irq(IRQn_Type IRQn)
{
if (IRQn < 32) {
return ((1UL << IRQn) & __NRF_NVIC_APP_IRQS_0) != 0;
} else if (IRQn < 64) {
return ((1UL << (IRQn - 32)) & __NRF_NVIC_APP_IRQS_1) != 0;
} else {
return 1;
}
}
}
return NRF_SUCCESS;
__STATIC_INLINE uint32_t __sd_nvic_is_app_accessible_priority(uint32_t priority)
{
if ((priority >= (1 << __NVIC_PRIO_BITS)) || (((1 << priority) & __NRF_NVIC_APP_IRQ_PRIOS) == 0)) {
return 0;
}
return 1;
}
__STATIC_INLINE uint32_t sd_nvic_EnableIRQ(IRQn_Type IRQn)
{
if (!__sd_nvic_app_accessible_irq(IRQn)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
if (!__sd_nvic_is_app_accessible_priority(NVIC_GetPriority(IRQn))) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED;
}
if (nrf_nvic_state.__cr_flag) {
nrf_nvic_state.__irq_masks[(uint32_t)((int32_t)IRQn) >> 5] |=
(uint32_t)(1 << ((uint32_t)((int32_t)IRQn) & (uint32_t)0x1F));
} else {
NVIC_EnableIRQ(IRQn);
}
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_DisableIRQ(IRQn_Type IRQn)
{
if (!__sd_nvic_app_accessible_irq(IRQn)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
if (nrf_nvic_state.__cr_flag) {
nrf_nvic_state.__irq_masks[(uint32_t)((int32_t)IRQn) >> 5] &= ~(1UL << ((uint32_t)(IRQn)&0x1F));
} else {
NVIC_DisableIRQ(IRQn);
}
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_GetPendingIRQ(IRQn_Type IRQn, uint32_t *p_pending_irq)
{
if (__sd_nvic_app_accessible_irq(IRQn)) {
*p_pending_irq = NVIC_GetPendingIRQ(IRQn);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_SetPendingIRQ(IRQn_Type IRQn)
{
if (__sd_nvic_app_accessible_irq(IRQn)) {
NVIC_SetPendingIRQ(IRQn);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_ClearPendingIRQ(IRQn_Type IRQn)
{
if (__sd_nvic_app_accessible_irq(IRQn)) {
NVIC_ClearPendingIRQ(IRQn);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if (!__sd_nvic_app_accessible_irq(IRQn)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
if (!__sd_nvic_is_app_accessible_priority(priority)) {
return NRF_ERROR_SOC_NVIC_INTERRUPT_PRIORITY_NOT_ALLOWED;
}
NVIC_SetPriority(IRQn, (uint32_t)priority);
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_GetPriority(IRQn_Type IRQn, uint32_t *p_priority)
{
if (__sd_nvic_app_accessible_irq(IRQn)) {
*p_priority = (NVIC_GetPriority(IRQn) & 0xFF);
return NRF_SUCCESS;
} else {
return NRF_ERROR_SOC_NVIC_INTERRUPT_NOT_AVAILABLE;
}
}
__STATIC_INLINE uint32_t sd_nvic_SystemReset(void)
{
NVIC_SystemReset();
return NRF_ERROR_SOC_NVIC_SHOULD_NOT_RETURN;
}
__STATIC_INLINE uint32_t sd_nvic_critical_region_enter(uint8_t *p_is_nested_critical_region)
{
int was_masked = __sd_nvic_irq_disable();
if (!nrf_nvic_state.__cr_flag) {
nrf_nvic_state.__cr_flag = 1;
nrf_nvic_state.__irq_masks[0] = (NVIC->ICER[0] & __NRF_NVIC_APP_IRQS_0);
NVIC->ICER[0] = __NRF_NVIC_APP_IRQS_0;
nrf_nvic_state.__irq_masks[1] = (NVIC->ICER[1] & __NRF_NVIC_APP_IRQS_1);
NVIC->ICER[1] = __NRF_NVIC_APP_IRQS_1;
*p_is_nested_critical_region = 0;
} else {
*p_is_nested_critical_region = 1;
}
if (!was_masked) {
__sd_nvic_irq_enable();
}
return NRF_SUCCESS;
}
__STATIC_INLINE uint32_t sd_nvic_critical_region_exit(uint8_t is_nested_critical_region)
{
if (nrf_nvic_state.__cr_flag && (is_nested_critical_region == 0)) {
int was_masked = __sd_nvic_irq_disable();
NVIC->ISER[0] = nrf_nvic_state.__irq_masks[0];
NVIC->ISER[1] = nrf_nvic_state.__irq_masks[1];
nrf_nvic_state.__cr_flag = 0;
if (!was_masked) {
__sd_nvic_irq_enable();
}
}
return NRF_SUCCESS;
}
#endif /* SUPPRESS_INLINE_IMPLEMENTATION */
+60 -56
View File
@@ -151,23 +151,26 @@ the start of the SoftDevice (without MBR)*/
/** @brief Defines a macro for retrieving the actual SoftDevice ID from a given base address. Use
* @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR (the
* usual case). */
#define SD_ID_GET(baseaddr) \
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_ID_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) ? (*((uint32_t *)((baseaddr) + SD_ID_OFFSET))) \
: SDM_INFO_FIELD_INVALID)
#define SD_ID_GET(baseaddr) \
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_ID_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) \
? (*((uint32_t *)((baseaddr) + SD_ID_OFFSET))) \
: SDM_INFO_FIELD_INVALID)
/** @brief Defines a macro for retrieving the actual SoftDevice version from a given base address.
* Use @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR
* (the usual case). */
#define SD_VERSION_GET(baseaddr) \
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_VERSION_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) ? (*((uint32_t *)((baseaddr) + SD_VERSION_OFFSET))) \
: SDM_INFO_FIELD_INVALID)
#define SD_VERSION_GET(baseaddr) \
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_VERSION_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) \
? (*((uint32_t *)((baseaddr) + SD_VERSION_OFFSET))) \
: SDM_INFO_FIELD_INVALID)
/** @brief Defines a macro for retrieving the address of SoftDevice unique str based on a given base address.
* Use @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR
* (the usual case). */
#define SD_UNIQUE_STR_ADDR_GET(baseaddr) \
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_UNIQUE_STR_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) ? (((uint8_t *)((baseaddr) + SD_UNIQUE_STR_OFFSET))) \
: SDM_INFO_FIELD_INVALID)
#define SD_UNIQUE_STR_ADDR_GET(baseaddr) \
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_UNIQUE_STR_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) \
? (((uint8_t *)((baseaddr) + SD_UNIQUE_STR_OFFSET))) \
: SDM_INFO_FIELD_INVALID)
/**@defgroup NRF_FAULT_ID_RANGES Fault ID ranges
* @{ */
@@ -177,13 +180,14 @@ the start of the SoftDevice (without MBR)*/
/**@defgroup NRF_FAULT_IDS Fault ID types
* @{ */
#define NRF_FAULT_ID_SD_ASSERT (NRF_FAULT_ID_SD_RANGE_START + 1) /**< SoftDevice assertion. The info parameter is reserved for future used. */
#define NRF_FAULT_ID_APP_MEMACC \
(NRF_FAULT_ID_APP_RANGE_START + 1) /**< Application invalid memory access. The info parameter will contain \
0x00000000, in case of SoftDevice RAM access violation. In case of SoftDevice \
peripheral register violation the info parameter will contain the sub-region \
number of PREGION[0], on whose address range the disallowed write access \
caused the memory access fault. */
#define NRF_FAULT_ID_SD_ASSERT \
(NRF_FAULT_ID_SD_RANGE_START + 1) /**< SoftDevice assertion. The info parameter is reserved for future used. */
#define NRF_FAULT_ID_APP_MEMACC \
(NRF_FAULT_ID_APP_RANGE_START + 1) /**< Application invalid memory access. The info parameter will contain 0x00000000, \
in case of SoftDevice RAM access violation. In case of SoftDevice peripheral \
register violation the info parameter will contain the sub-region number of \
PREGION[0], on whose address range the disallowed write access caused the \
memory access fault. */
/**@} */
/** @} */
@@ -193,11 +197,11 @@ the start of the SoftDevice (without MBR)*/
/**@brief nRF SoftDevice Manager API SVC numbers. */
enum NRF_SD_SVCS {
SD_SOFTDEVICE_ENABLE = SDM_SVC_BASE, /**< ::sd_softdevice_enable */
SD_SOFTDEVICE_DISABLE, /**< ::sd_softdevice_disable */
SD_SOFTDEVICE_IS_ENABLED, /**< ::sd_softdevice_is_enabled */
SD_SOFTDEVICE_VECTOR_TABLE_BASE_SET, /**< ::sd_softdevice_vector_table_base_set */
SVC_SDM_LAST /**< Placeholder for last SDM SVC */
SD_SOFTDEVICE_ENABLE = SDM_SVC_BASE, /**< ::sd_softdevice_enable */
SD_SOFTDEVICE_DISABLE, /**< ::sd_softdevice_disable */
SD_SOFTDEVICE_IS_ENABLED, /**< ::sd_softdevice_is_enabled */
SD_SOFTDEVICE_VECTOR_TABLE_BASE_SET, /**< ::sd_softdevice_vector_table_base_set */
SVC_SDM_LAST /**< Placeholder for last SDM SVC */
};
/** @} */
@@ -239,34 +243,34 @@ enum NRF_SD_SVCS {
/**@brief Type representing LFCLK oscillator source. */
typedef struct {
uint8_t source; /**< LF oscillator clock source, see @ref NRF_CLOCK_LF_SRC. */
uint8_t rc_ctiv; /**< Only for ::NRF_CLOCK_LF_SRC_RC: Calibration timer interval in 1/4 second
units (nRF52: 1-32).
@note To avoid excessive clock drift, 0.5 degrees Celsius is the
maximum temperature change allowed in one calibration timer
interval. The interval should be selected to ensure this.
uint8_t source; /**< LF oscillator clock source, see @ref NRF_CLOCK_LF_SRC. */
uint8_t rc_ctiv; /**< Only for ::NRF_CLOCK_LF_SRC_RC: Calibration timer interval in 1/4 second
units (nRF52: 1-32).
@note To avoid excessive clock drift, 0.5 degrees Celsius is the
maximum temperature change allowed in one calibration timer
interval. The interval should be selected to ensure this.
@note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC. */
uint8_t rc_temp_ctiv; /**< Only for ::NRF_CLOCK_LF_SRC_RC: How often (in number of calibration
intervals) the RC oscillator shall be calibrated if the temperature
hasn't changed.
0: Always calibrate even if the temperature hasn't changed.
1: Only calibrate if the temperature has changed (legacy - nRF51 only).
2-33: Check the temperature and only calibrate if it has changed,
however calibration will take place every rc_temp_ctiv
intervals in any case.
@note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC. */
uint8_t rc_temp_ctiv; /**< Only for ::NRF_CLOCK_LF_SRC_RC: How often (in number of calibration
intervals) the RC oscillator shall be calibrated if the temperature
hasn't changed.
0: Always calibrate even if the temperature hasn't changed.
1: Only calibrate if the temperature has changed (legacy - nRF51 only).
2-33: Check the temperature and only calibrate if it has changed,
however calibration will take place every rc_temp_ctiv
intervals in any case.
@note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC.
@note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC.
@note For nRF52, the application must ensure calibration at least once
every 8 seconds to ensure +/-500 ppm clock stability. The
recommended configuration for ::NRF_CLOCK_LF_SRC_RC on nRF52 is
rc_ctiv=16 and rc_temp_ctiv=2. This will ensure calibration at
least once every 8 seconds and for temperature changes of 0.5
degrees Celsius every 4 seconds. See the Product Specification
for the nRF52 device being used for more information.*/
uint8_t accuracy; /**< External clock accuracy used in the LL to compute timing
windows, see @ref NRF_CLOCK_LF_ACCURACY.*/
@note For nRF52, the application must ensure calibration at least once
every 8 seconds to ensure +/-500 ppm clock stability. The
recommended configuration for ::NRF_CLOCK_LF_SRC_RC on nRF52 is
rc_ctiv=16 and rc_temp_ctiv=2. This will ensure calibration at
least once every 8 seconds and for temperature changes of 0.5
degrees Celsius every 4 seconds. See the Product Specification
for the nRF52 device being used for more information.*/
uint8_t accuracy; /**< External clock accuracy used in the LL to compute timing
windows, see @ref NRF_CLOCK_LF_ACCURACY.*/
} nrf_clock_lf_cfg_t;
/**@brief Fault Handler type.
@@ -286,9 +290,9 @@ typedef struct {
* @param[in] pc The program counter of the instruction that triggered the fault.
* @param[in] info Optional additional information regarding the fault. Refer to each Fault identifier for details.
*
* @note When id is set to @ref NRF_FAULT_ID_APP_MEMACC, pc will contain the address of the instruction being executed
* at the time when the fault is detected by the CPU. The CPU program counter may have advanced up to 2 instructions (no
* branching) after the one that triggered the fault.
* @note When id is set to @ref NRF_FAULT_ID_APP_MEMACC, pc will contain the address of the instruction being executed at the time
* when the fault is detected by the CPU. The CPU program counter may have advanced up to 2 instructions (no branching) after the
* one that triggered the fault.
*/
typedef void (*nrf_fault_handler_t)(uint32_t id, uint32_t pc, uint32_t info);
@@ -316,20 +320,20 @@ typedef void (*nrf_fault_handler_t)(uint32_t id, uint32_t pc, uint32_t info);
*
* @param p_clock_lf_cfg Low frequency clock source and accuracy.
If NULL the clock will be configured as an RC source with rc_ctiv = 16 and .rc_temp_ctiv = 2
In the case of XTAL source, the PPM accuracy of the chosen clock source must be greater than or
equal to the actual characteristics of your XTAL clock.
In the case of XTAL source, the PPM accuracy of the chosen clock source must be greater than or equal to
the actual characteristics of your XTAL clock.
* @param fault_handler Callback to be invoked in case of fault, cannot be NULL.
*
* @retval ::NRF_SUCCESS
* @retval ::NRF_ERROR_INVALID_ADDR Invalid or NULL pointer supplied.
* @retval ::NRF_ERROR_INVALID_STATE SoftDevice is already enabled, and the clock source and fault handler cannot be
updated.
* @retval ::NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION SoftDevice interrupt is already enabled, or an enabled
interrupt has an illegal priority level.
* @retval ::NRF_ERROR_INVALID_STATE SoftDevice is already enabled, and the clock source and fault handler cannot be updated.
* @retval ::NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION SoftDevice interrupt is already enabled, or an enabled interrupt has
an illegal priority level.
* @retval ::NRF_ERROR_SDM_LFCLK_SOURCE_UNKNOWN Unknown low frequency clock source selected.
* @retval ::NRF_ERROR_INVALID_PARAM Invalid clock source configuration supplied in p_clock_lf_cfg.
*/
SVCALL(SD_SOFTDEVICE_ENABLE, uint32_t, sd_softdevice_enable(nrf_clock_lf_cfg_t const *p_clock_lf_cfg, nrf_fault_handler_t fault_handler));
SVCALL(SD_SOFTDEVICE_ENABLE, uint32_t,
sd_softdevice_enable(nrf_clock_lf_cfg_t const *p_clock_lf_cfg, nrf_fault_handler_t fault_handler));
/**@brief Disables the SoftDevice and by extension the protocol stack.
*
+187 -190
View File
@@ -81,32 +81,33 @@ extern "C" {
#define SOC_ECB_CIPHERTEXT_LENGTH (SOC_ECB_CLEARTEXT_LENGTH) /**< ECB ciphertext length. */
#define SD_EVT_IRQn (SWI2_IRQn) /**< SoftDevice Event IRQ number. Used for both protocol events and SoC events. */
#define SD_EVT_IRQHandler \
(SWI2_IRQHandler) /**< SoftDevice Event IRQ handler. Used for both protocol events and SoC events. \
#define SD_EVT_IRQHandler \
(SWI2_IRQHandler) /**< SoftDevice Event IRQ handler. Used for both protocol events and SoC events. \
The default interrupt priority for this handler is set to 6 */
#define RADIO_NOTIFICATION_IRQn (SWI1_IRQn) /**< The radio notification IRQ number. */
#define RADIO_NOTIFICATION_IRQHandler \
(SWI1_IRQHandler) /**< The radio notification IRQ handler. \
#define RADIO_NOTIFICATION_IRQHandler \
(SWI1_IRQHandler) /**< The radio notification IRQ handler. \
The default interrupt priority for this handler is set to 6 */
#define NRF_RADIO_LENGTH_MIN_US (100) /**< The shortest allowed radio timeslot, in microseconds. */
#define NRF_RADIO_LENGTH_MAX_US (100000) /**< The longest allowed radio timeslot, in microseconds. */
#define NRF_RADIO_DISTANCE_MAX_US \
(128000000UL - 1UL) /**< The longest timeslot distance, in microseconds, allowed for the distance parameter (see \
@ref nrf_radio_request_normal_t) in the request. */
#define NRF_RADIO_DISTANCE_MAX_US \
(128000000UL - 1UL) /**< The longest timeslot distance, in microseconds, allowed for the distance parameter (see @ref \
nrf_radio_request_normal_t) in the request. */
#define NRF_RADIO_EARLIEST_TIMEOUT_MAX_US \
(128000000UL - 1UL) /**< The longest timeout, in microseconds, allowed when requesting the earliest possible timeslot. */
#define NRF_RADIO_EARLIEST_TIMEOUT_MAX_US \
(128000000UL - 1UL) /**< The longest timeout, in microseconds, allowed when requesting the earliest possible timeslot. */
#define NRF_RADIO_START_JITTER_US (2) /**< The maximum jitter in @ref NRF_RADIO_CALLBACK_SIGNAL_TYPE_START relative to the requested start time. */
#define NRF_RADIO_START_JITTER_US \
(2) /**< The maximum jitter in @ref NRF_RADIO_CALLBACK_SIGNAL_TYPE_START relative to the requested start time. */
/**@brief Mask of PPI channels reserved by the SoftDevice when the SoftDevice is disabled. */
#define NRF_SOC_SD_PPI_CHANNELS_SD_DISABLED_MSK ((uint32_t)(0))
/**@brief Mask of PPI channels reserved by the SoftDevice when the SoftDevice is enabled. */
#define NRF_SOC_SD_PPI_CHANNELS_SD_ENABLED_MSK \
((uint32_t)((1U << 17) | (1U << 18) | (1U << 19) | (1U << 20) | (1U << 21) | (1U << 22) | (1U << 23) | (1U << 24) | (1U << 25) | (1U << 26) | \
(1U << 27) | (1U << 28) | (1U << 29) | (1U << 30) | (1U << 31)))
#define NRF_SOC_SD_PPI_CHANNELS_SD_ENABLED_MSK \
((uint32_t)((1U << 17) | (1U << 18) | (1U << 19) | (1U << 20) | (1U << 21) | (1U << 22) | (1U << 23) | (1U << 24) | \
(1U << 25) | (1U << 26) | (1U << 27) | (1U << 28) | (1U << 29) | (1U << 30) | (1U << 31)))
/**@brief Mask of PPI groups reserved by the SoftDevice when the SoftDevice is disabled. */
#define NRF_SOC_SD_PPI_GROUPS_SD_DISABLED_MSK ((uint32_t)(0))
@@ -121,55 +122,55 @@ extern "C" {
/**@brief The SVC numbers used by the SVC functions in the SoC library. */
enum NRF_SOC_SVCS {
SD_PPI_CHANNEL_ENABLE_GET = SOC_SVC_BASE,
SD_PPI_CHANNEL_ENABLE_SET = SOC_SVC_BASE + 1,
SD_PPI_CHANNEL_ENABLE_CLR = SOC_SVC_BASE + 2,
SD_PPI_CHANNEL_ASSIGN = SOC_SVC_BASE + 3,
SD_PPI_GROUP_TASK_ENABLE = SOC_SVC_BASE + 4,
SD_PPI_GROUP_TASK_DISABLE = SOC_SVC_BASE + 5,
SD_PPI_GROUP_ASSIGN = SOC_SVC_BASE + 6,
SD_PPI_GROUP_GET = SOC_SVC_BASE + 7,
SD_FLASH_PAGE_ERASE = SOC_SVC_BASE + 8,
SD_FLASH_WRITE = SOC_SVC_BASE + 9,
SD_PROTECTED_REGISTER_WRITE = SOC_SVC_BASE + 11,
SD_MUTEX_NEW = SOC_SVC_BASE_NOT_AVAILABLE,
SD_MUTEX_ACQUIRE = SOC_SVC_BASE_NOT_AVAILABLE + 1,
SD_MUTEX_RELEASE = SOC_SVC_BASE_NOT_AVAILABLE + 2,
SD_RAND_APPLICATION_POOL_CAPACITY_GET = SOC_SVC_BASE_NOT_AVAILABLE + 3,
SD_RAND_APPLICATION_BYTES_AVAILABLE_GET = SOC_SVC_BASE_NOT_AVAILABLE + 4,
SD_RAND_APPLICATION_VECTOR_GET = SOC_SVC_BASE_NOT_AVAILABLE + 5,
SD_POWER_MODE_SET = SOC_SVC_BASE_NOT_AVAILABLE + 6,
SD_POWER_SYSTEM_OFF = SOC_SVC_BASE_NOT_AVAILABLE + 7,
SD_POWER_RESET_REASON_GET = SOC_SVC_BASE_NOT_AVAILABLE + 8,
SD_POWER_RESET_REASON_CLR = SOC_SVC_BASE_NOT_AVAILABLE + 9,
SD_POWER_POF_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 10,
SD_POWER_POF_THRESHOLD_SET = SOC_SVC_BASE_NOT_AVAILABLE + 11,
SD_POWER_POF_THRESHOLDVDDH_SET = SOC_SVC_BASE_NOT_AVAILABLE + 12,
SD_POWER_RAM_POWER_SET = SOC_SVC_BASE_NOT_AVAILABLE + 13,
SD_POWER_RAM_POWER_CLR = SOC_SVC_BASE_NOT_AVAILABLE + 14,
SD_POWER_RAM_POWER_GET = SOC_SVC_BASE_NOT_AVAILABLE + 15,
SD_POWER_GPREGRET_SET = SOC_SVC_BASE_NOT_AVAILABLE + 16,
SD_POWER_GPREGRET_CLR = SOC_SVC_BASE_NOT_AVAILABLE + 17,
SD_POWER_GPREGRET_GET = SOC_SVC_BASE_NOT_AVAILABLE + 18,
SD_POWER_DCDC_MODE_SET = SOC_SVC_BASE_NOT_AVAILABLE + 19,
SD_POWER_DCDC0_MODE_SET = SOC_SVC_BASE_NOT_AVAILABLE + 20,
SD_APP_EVT_WAIT = SOC_SVC_BASE_NOT_AVAILABLE + 21,
SD_CLOCK_HFCLK_REQUEST = SOC_SVC_BASE_NOT_AVAILABLE + 22,
SD_CLOCK_HFCLK_RELEASE = SOC_SVC_BASE_NOT_AVAILABLE + 23,
SD_CLOCK_HFCLK_IS_RUNNING = SOC_SVC_BASE_NOT_AVAILABLE + 24,
SD_RADIO_NOTIFICATION_CFG_SET = SOC_SVC_BASE_NOT_AVAILABLE + 25,
SD_ECB_BLOCK_ENCRYPT = SOC_SVC_BASE_NOT_AVAILABLE + 26,
SD_ECB_BLOCKS_ENCRYPT = SOC_SVC_BASE_NOT_AVAILABLE + 27,
SD_RADIO_SESSION_OPEN = SOC_SVC_BASE_NOT_AVAILABLE + 28,
SD_RADIO_SESSION_CLOSE = SOC_SVC_BASE_NOT_AVAILABLE + 29,
SD_RADIO_REQUEST = SOC_SVC_BASE_NOT_AVAILABLE + 30,
SD_EVT_GET = SOC_SVC_BASE_NOT_AVAILABLE + 31,
SD_TEMP_GET = SOC_SVC_BASE_NOT_AVAILABLE + 32,
SD_POWER_USBPWRRDY_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 33,
SD_POWER_USBDETECTED_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 34,
SD_POWER_USBREMOVED_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 35,
SD_POWER_USBREGSTATUS_GET = SOC_SVC_BASE_NOT_AVAILABLE + 36,
SVC_SOC_LAST = SOC_SVC_BASE_NOT_AVAILABLE + 37
SD_PPI_CHANNEL_ENABLE_GET = SOC_SVC_BASE,
SD_PPI_CHANNEL_ENABLE_SET = SOC_SVC_BASE + 1,
SD_PPI_CHANNEL_ENABLE_CLR = SOC_SVC_BASE + 2,
SD_PPI_CHANNEL_ASSIGN = SOC_SVC_BASE + 3,
SD_PPI_GROUP_TASK_ENABLE = SOC_SVC_BASE + 4,
SD_PPI_GROUP_TASK_DISABLE = SOC_SVC_BASE + 5,
SD_PPI_GROUP_ASSIGN = SOC_SVC_BASE + 6,
SD_PPI_GROUP_GET = SOC_SVC_BASE + 7,
SD_FLASH_PAGE_ERASE = SOC_SVC_BASE + 8,
SD_FLASH_WRITE = SOC_SVC_BASE + 9,
SD_PROTECTED_REGISTER_WRITE = SOC_SVC_BASE + 11,
SD_MUTEX_NEW = SOC_SVC_BASE_NOT_AVAILABLE,
SD_MUTEX_ACQUIRE = SOC_SVC_BASE_NOT_AVAILABLE + 1,
SD_MUTEX_RELEASE = SOC_SVC_BASE_NOT_AVAILABLE + 2,
SD_RAND_APPLICATION_POOL_CAPACITY_GET = SOC_SVC_BASE_NOT_AVAILABLE + 3,
SD_RAND_APPLICATION_BYTES_AVAILABLE_GET = SOC_SVC_BASE_NOT_AVAILABLE + 4,
SD_RAND_APPLICATION_VECTOR_GET = SOC_SVC_BASE_NOT_AVAILABLE + 5,
SD_POWER_MODE_SET = SOC_SVC_BASE_NOT_AVAILABLE + 6,
SD_POWER_SYSTEM_OFF = SOC_SVC_BASE_NOT_AVAILABLE + 7,
SD_POWER_RESET_REASON_GET = SOC_SVC_BASE_NOT_AVAILABLE + 8,
SD_POWER_RESET_REASON_CLR = SOC_SVC_BASE_NOT_AVAILABLE + 9,
SD_POWER_POF_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 10,
SD_POWER_POF_THRESHOLD_SET = SOC_SVC_BASE_NOT_AVAILABLE + 11,
SD_POWER_POF_THRESHOLDVDDH_SET = SOC_SVC_BASE_NOT_AVAILABLE + 12,
SD_POWER_RAM_POWER_SET = SOC_SVC_BASE_NOT_AVAILABLE + 13,
SD_POWER_RAM_POWER_CLR = SOC_SVC_BASE_NOT_AVAILABLE + 14,
SD_POWER_RAM_POWER_GET = SOC_SVC_BASE_NOT_AVAILABLE + 15,
SD_POWER_GPREGRET_SET = SOC_SVC_BASE_NOT_AVAILABLE + 16,
SD_POWER_GPREGRET_CLR = SOC_SVC_BASE_NOT_AVAILABLE + 17,
SD_POWER_GPREGRET_GET = SOC_SVC_BASE_NOT_AVAILABLE + 18,
SD_POWER_DCDC_MODE_SET = SOC_SVC_BASE_NOT_AVAILABLE + 19,
SD_POWER_DCDC0_MODE_SET = SOC_SVC_BASE_NOT_AVAILABLE + 20,
SD_APP_EVT_WAIT = SOC_SVC_BASE_NOT_AVAILABLE + 21,
SD_CLOCK_HFCLK_REQUEST = SOC_SVC_BASE_NOT_AVAILABLE + 22,
SD_CLOCK_HFCLK_RELEASE = SOC_SVC_BASE_NOT_AVAILABLE + 23,
SD_CLOCK_HFCLK_IS_RUNNING = SOC_SVC_BASE_NOT_AVAILABLE + 24,
SD_RADIO_NOTIFICATION_CFG_SET = SOC_SVC_BASE_NOT_AVAILABLE + 25,
SD_ECB_BLOCK_ENCRYPT = SOC_SVC_BASE_NOT_AVAILABLE + 26,
SD_ECB_BLOCKS_ENCRYPT = SOC_SVC_BASE_NOT_AVAILABLE + 27,
SD_RADIO_SESSION_OPEN = SOC_SVC_BASE_NOT_AVAILABLE + 28,
SD_RADIO_SESSION_CLOSE = SOC_SVC_BASE_NOT_AVAILABLE + 29,
SD_RADIO_REQUEST = SOC_SVC_BASE_NOT_AVAILABLE + 30,
SD_EVT_GET = SOC_SVC_BASE_NOT_AVAILABLE + 31,
SD_TEMP_GET = SOC_SVC_BASE_NOT_AVAILABLE + 32,
SD_POWER_USBPWRRDY_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 33,
SD_POWER_USBDETECTED_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 34,
SD_POWER_USBREMOVED_ENABLE = SOC_SVC_BASE_NOT_AVAILABLE + 35,
SD_POWER_USBREGSTATUS_GET = SOC_SVC_BASE_NOT_AVAILABLE + 36,
SVC_SOC_LAST = SOC_SVC_BASE_NOT_AVAILABLE + 37
};
/**@brief Possible values of a ::nrf_mutex_t. */
@@ -177,80 +178,79 @@ enum NRF_MUTEX_VALUES { NRF_MUTEX_FREE, NRF_MUTEX_TAKEN };
/**@brief Power modes. */
enum NRF_POWER_MODES {
NRF_POWER_MODE_CONSTLAT, /**< Constant latency mode. See power management in the reference manual. */
NRF_POWER_MODE_LOWPWR /**< Low power mode. See power management in the reference manual. */
NRF_POWER_MODE_CONSTLAT, /**< Constant latency mode. See power management in the reference manual. */
NRF_POWER_MODE_LOWPWR /**< Low power mode. See power management in the reference manual. */
};
/**@brief Power failure thresholds */
enum NRF_POWER_THRESHOLDS {
NRF_POWER_THRESHOLD_V17 = 4UL, /**< 1.7 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V18, /**< 1.8 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V19, /**< 1.9 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V20, /**< 2.0 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V21, /**< 2.1 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V22, /**< 2.2 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V23, /**< 2.3 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V24, /**< 2.4 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V25, /**< 2.5 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V26, /**< 2.6 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V27, /**< 2.7 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V28 /**< 2.8 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V17 = 4UL, /**< 1.7 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V18, /**< 1.8 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V19, /**< 1.9 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V20, /**< 2.0 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V21, /**< 2.1 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V22, /**< 2.2 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V23, /**< 2.3 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V24, /**< 2.4 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V25, /**< 2.5 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V26, /**< 2.6 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V27, /**< 2.7 Volts power failure threshold. */
NRF_POWER_THRESHOLD_V28 /**< 2.8 Volts power failure threshold. */
};
/**@brief Power failure thresholds for high voltage */
enum NRF_POWER_THRESHOLDVDDHS {
NRF_POWER_THRESHOLDVDDH_V27, /**< 2.7 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V28, /**< 2.8 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V29, /**< 2.9 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V30, /**< 3.0 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V31, /**< 3.1 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V32, /**< 3.2 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V33, /**< 3.3 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V34, /**< 3.4 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V35, /**< 3.5 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V36, /**< 3.6 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V37, /**< 3.7 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V38, /**< 3.8 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V39, /**< 3.9 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V40, /**< 4.0 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V41, /**< 4.1 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V42 /**< 4.2 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V27, /**< 2.7 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V28, /**< 2.8 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V29, /**< 2.9 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V30, /**< 3.0 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V31, /**< 3.1 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V32, /**< 3.2 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V33, /**< 3.3 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V34, /**< 3.4 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V35, /**< 3.5 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V36, /**< 3.6 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V37, /**< 3.7 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V38, /**< 3.8 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V39, /**< 3.9 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V40, /**< 4.0 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V41, /**< 4.1 Volts power failure threshold. */
NRF_POWER_THRESHOLDVDDH_V42 /**< 4.2 Volts power failure threshold. */
};
/**@brief DC/DC converter modes. */
enum NRF_POWER_DCDC_MODES {
NRF_POWER_DCDC_DISABLE, /**< The DCDC is disabled. */
NRF_POWER_DCDC_ENABLE /**< The DCDC is enabled. */
NRF_POWER_DCDC_DISABLE, /**< The DCDC is disabled. */
NRF_POWER_DCDC_ENABLE /**< The DCDC is enabled. */
};
/**@brief Radio notification distances. */
enum NRF_RADIO_NOTIFICATION_DISTANCES {
NRF_RADIO_NOTIFICATION_DISTANCE_NONE = 0, /**< The event does not have a notification. */
NRF_RADIO_NOTIFICATION_DISTANCE_800US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_1740US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_2680US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_3620US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_4560US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_5500US /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_NONE = 0, /**< The event does not have a notification. */
NRF_RADIO_NOTIFICATION_DISTANCE_800US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_1740US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_2680US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_3620US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_4560US, /**< The distance from the active notification to start of radio activity. */
NRF_RADIO_NOTIFICATION_DISTANCE_5500US /**< The distance from the active notification to start of radio activity. */
};
/**@brief Radio notification types. */
enum NRF_RADIO_NOTIFICATION_TYPES {
NRF_RADIO_NOTIFICATION_TYPE_NONE = 0, /**< The event does not have a radio notification signal. */
NRF_RADIO_NOTIFICATION_TYPE_INT_ON_ACTIVE, /**< Using interrupt for notification when the radio will be enabled. */
NRF_RADIO_NOTIFICATION_TYPE_INT_ON_INACTIVE, /**< Using interrupt for notification when the radio has been disabled.
*/
NRF_RADIO_NOTIFICATION_TYPE_INT_ON_BOTH, /**< Using interrupt for notification both when the radio will be enabled and
disabled. */
NRF_RADIO_NOTIFICATION_TYPE_NONE = 0, /**< The event does not have a radio notification signal. */
NRF_RADIO_NOTIFICATION_TYPE_INT_ON_ACTIVE, /**< Using interrupt for notification when the radio will be enabled. */
NRF_RADIO_NOTIFICATION_TYPE_INT_ON_INACTIVE, /**< Using interrupt for notification when the radio has been disabled. */
NRF_RADIO_NOTIFICATION_TYPE_INT_ON_BOTH, /**< Using interrupt for notification both when the radio will be enabled and
disabled. */
};
/**@brief The Radio signal callback types. */
enum NRF_RADIO_CALLBACK_SIGNAL_TYPE {
NRF_RADIO_CALLBACK_SIGNAL_TYPE_START, /**< This signal indicates the start of the radio timeslot. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0, /**< This signal indicates the NRF_TIMER0 interrupt. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO, /**< This signal indicates the NRF_RADIO interrupt. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED, /**< This signal indicates extend action failed. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED /**< This signal indicates extend action succeeded. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_START, /**< This signal indicates the start of the radio timeslot. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0, /**< This signal indicates the NRF_TIMER0 interrupt. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO, /**< This signal indicates the NRF_RADIO interrupt. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED, /**< This signal indicates extend action failed. */
NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED /**< This signal indicates extend action succeeded. */
};
/**@brief The actions requested by the signal callback.
@@ -259,66 +259,63 @@ enum NRF_RADIO_CALLBACK_SIGNAL_TYPE {
* returned.
*/
enum NRF_RADIO_SIGNAL_CALLBACK_ACTION {
NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE, /**< Return without action. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND, /**< Request an extension of the current
timeslot. Maximum execution time for this action:
@ref NRF_RADIO_MAX_EXTENSION_PROCESSING_TIME_US.
This action must be started at least
@ref NRF_RADIO_MIN_EXTENSION_MARGIN_US before
the end of the timeslot. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_END, /**< End the current radio timeslot. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END /**< Request a new radio timeslot and end the current timeslot. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE, /**< Return without action. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND, /**< Request an extension of the current
timeslot. Maximum execution time for this action:
@ref NRF_RADIO_MAX_EXTENSION_PROCESSING_TIME_US.
This action must be started at least
@ref NRF_RADIO_MIN_EXTENSION_MARGIN_US before
the end of the timeslot. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_END, /**< End the current radio timeslot. */
NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END /**< Request a new radio timeslot and end the current timeslot. */
};
/**@brief Radio timeslot high frequency clock source configuration. */
enum NRF_RADIO_HFCLK_CFG {
NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED, /**< The SoftDevice will guarantee that the high frequency clock source is the
external crystal for the whole duration of the timeslot. This should be the
preferred option for events that use the radio or require high timing
accuracy.
@note The SoftDevice will automatically turn on and off the external crystal,
at the beginning and end of the timeslot, respectively. The crystal may also
intentionally be left running after the timeslot, in cases where it is needed
by the SoftDevice shortly after the end of the timeslot. */
NRF_RADIO_HFCLK_CFG_NO_GUARANTEE /**< This configuration allows for earlier and tighter scheduling of timeslots.
The RC oscillator may be the clock source in part or for the whole duration of
the timeslot. The RC oscillator's accuracy must therefore be taken into
consideration.
@note If the application will use the radio peripheral in timeslots with this
configuration, it must make sure that the crystal is running and stable before
starting the radio. */
NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED, /**< The SoftDevice will guarantee that the high frequency clock source is the
external crystal for the whole duration of the timeslot. This should be the
preferred option for events that use the radio or require high timing accuracy.
@note The SoftDevice will automatically turn on and off the external crystal,
at the beginning and end of the timeslot, respectively. The crystal may also
intentionally be left running after the timeslot, in cases where it is needed
by the SoftDevice shortly after the end of the timeslot. */
NRF_RADIO_HFCLK_CFG_NO_GUARANTEE /**< This configuration allows for earlier and tighter scheduling of timeslots.
The RC oscillator may be the clock source in part or for the whole duration of the
timeslot. The RC oscillator's accuracy must therefore be taken into consideration.
@note If the application will use the radio peripheral in timeslots with this
configuration, it must make sure that the crystal is running and stable before
starting the radio. */
};
/**@brief Radio timeslot priorities. */
enum NRF_RADIO_PRIORITY {
NRF_RADIO_PRIORITY_HIGH, /**< High (equal priority as the normal connection priority of the SoftDevice stack(s)). */
NRF_RADIO_PRIORITY_NORMAL, /**< Normal (equal priority as the priority of secondary activities of the SoftDevice
stack(s)). */
NRF_RADIO_PRIORITY_HIGH, /**< High (equal priority as the normal connection priority of the SoftDevice stack(s)). */
NRF_RADIO_PRIORITY_NORMAL, /**< Normal (equal priority as the priority of secondary activities of the SoftDevice stack(s)). */
};
/**@brief Radio timeslot request type. */
enum NRF_RADIO_REQUEST_TYPE {
NRF_RADIO_REQ_TYPE_EARLIEST, /**< Request radio timeslot as early as possible. This should always be used for the
first request in a session. */
NRF_RADIO_REQ_TYPE_NORMAL /**< Normal radio timeslot request. */
NRF_RADIO_REQ_TYPE_EARLIEST, /**< Request radio timeslot as early as possible. This should always be used for the first
request in a session. */
NRF_RADIO_REQ_TYPE_NORMAL /**< Normal radio timeslot request. */
};
/**@brief SoC Events. */
enum NRF_SOC_EVTS {
NRF_EVT_HFCLKSTARTED, /**< Event indicating that the HFCLK has started. */
NRF_EVT_POWER_FAILURE_WARNING, /**< Event indicating that a power failure warning has occurred. */
NRF_EVT_FLASH_OPERATION_SUCCESS, /**< Event indicating that the ongoing flash operation has completed successfully. */
NRF_EVT_FLASH_OPERATION_ERROR, /**< Event indicating that the ongoing flash operation has timed out with an error. */
NRF_EVT_RADIO_BLOCKED, /**< Event indicating that a radio timeslot was blocked. */
NRF_EVT_RADIO_CANCELED, /**< Event indicating that a radio timeslot was canceled by SoftDevice. */
NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN, /**< Event indicating that a radio timeslot signal callback handler
return was invalid. */
NRF_EVT_RADIO_SESSION_IDLE, /**< Event indicating that a radio timeslot session is idle. */
NRF_EVT_RADIO_SESSION_CLOSED, /**< Event indicating that a radio timeslot session is closed. */
NRF_EVT_POWER_USB_POWER_READY, /**< Event indicating that a USB 3.3 V supply is ready. */
NRF_EVT_POWER_USB_DETECTED, /**< Event indicating that voltage supply is detected on VBUS. */
NRF_EVT_POWER_USB_REMOVED, /**< Event indicating that voltage supply is removed from VBUS. */
NRF_EVT_NUMBER_OF_EVTS
NRF_EVT_HFCLKSTARTED, /**< Event indicating that the HFCLK has started. */
NRF_EVT_POWER_FAILURE_WARNING, /**< Event indicating that a power failure warning has occurred. */
NRF_EVT_FLASH_OPERATION_SUCCESS, /**< Event indicating that the ongoing flash operation has completed successfully. */
NRF_EVT_FLASH_OPERATION_ERROR, /**< Event indicating that the ongoing flash operation has timed out with an error. */
NRF_EVT_RADIO_BLOCKED, /**< Event indicating that a radio timeslot was blocked. */
NRF_EVT_RADIO_CANCELED, /**< Event indicating that a radio timeslot was canceled by SoftDevice. */
NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN, /**< Event indicating that a radio timeslot signal callback handler return was
invalid. */
NRF_EVT_RADIO_SESSION_IDLE, /**< Event indicating that a radio timeslot session is idle. */
NRF_EVT_RADIO_SESSION_CLOSED, /**< Event indicating that a radio timeslot session is closed. */
NRF_EVT_POWER_USB_POWER_READY, /**< Event indicating that a USB 3.3 V supply is ready. */
NRF_EVT_POWER_USB_DETECTED, /**< Event indicating that voltage supply is detected on VBUS. */
NRF_EVT_POWER_USB_REMOVED, /**< Event indicating that voltage supply is removed from VBUS. */
NRF_EVT_NUMBER_OF_EVTS
};
/**@} */
@@ -333,44 +330,44 @@ typedef volatile uint8_t nrf_mutex_t;
/**@brief Parameters for a request for a timeslot as early as possible. */
typedef struct {
uint8_t hfclk; /**< High frequency clock source, see @ref NRF_RADIO_HFCLK_CFG. */
uint8_t priority; /**< The radio timeslot priority, see @ref NRF_RADIO_PRIORITY. */
uint32_t length_us; /**< The radio timeslot length (in the range 100 to 100,000] microseconds). */
uint32_t timeout_us; /**< Longest acceptable delay until the start of the requested timeslot (up to @ref
NRF_RADIO_EARLIEST_TIMEOUT_MAX_US microseconds). */
uint8_t hfclk; /**< High frequency clock source, see @ref NRF_RADIO_HFCLK_CFG. */
uint8_t priority; /**< The radio timeslot priority, see @ref NRF_RADIO_PRIORITY. */
uint32_t length_us; /**< The radio timeslot length (in the range 100 to 100,000] microseconds). */
uint32_t timeout_us; /**< Longest acceptable delay until the start of the requested timeslot (up to @ref
NRF_RADIO_EARLIEST_TIMEOUT_MAX_US microseconds). */
} nrf_radio_request_earliest_t;
/**@brief Parameters for a normal radio timeslot request. */
typedef struct {
uint8_t hfclk; /**< High frequency clock source, see @ref NRF_RADIO_HFCLK_CFG. */
uint8_t priority; /**< The radio timeslot priority, see @ref NRF_RADIO_PRIORITY. */
uint32_t distance_us; /**< Distance from the start of the previous radio timeslot (up to @ref
NRF_RADIO_DISTANCE_MAX_US microseconds). */
uint32_t length_us; /**< The radio timeslot length (in the range [100..100,000] microseconds). */
uint8_t hfclk; /**< High frequency clock source, see @ref NRF_RADIO_HFCLK_CFG. */
uint8_t priority; /**< The radio timeslot priority, see @ref NRF_RADIO_PRIORITY. */
uint32_t distance_us; /**< Distance from the start of the previous radio timeslot (up to @ref NRF_RADIO_DISTANCE_MAX_US
microseconds). */
uint32_t length_us; /**< The radio timeslot length (in the range [100..100,000] microseconds). */
} nrf_radio_request_normal_t;
/**@brief Radio timeslot request parameters. */
typedef struct {
uint8_t request_type; /**< Type of request, see @ref NRF_RADIO_REQUEST_TYPE. */
union {
nrf_radio_request_earliest_t earliest; /**< Parameters for requesting a radio timeslot as early as possible. */
nrf_radio_request_normal_t normal; /**< Parameters for requesting a normal radio timeslot. */
} params; /**< Parameter union. */
uint8_t request_type; /**< Type of request, see @ref NRF_RADIO_REQUEST_TYPE. */
union {
nrf_radio_request_earliest_t earliest; /**< Parameters for requesting a radio timeslot as early as possible. */
nrf_radio_request_normal_t normal; /**< Parameters for requesting a normal radio timeslot. */
} params; /**< Parameter union. */
} nrf_radio_request_t;
/**@brief Return parameters of the radio timeslot signal callback. */
typedef struct {
uint8_t callback_action; /**< The action requested by the application when returning from the signal callback, see
@ref NRF_RADIO_SIGNAL_CALLBACK_ACTION. */
union {
struct {
nrf_radio_request_t *p_next; /**< The request parameters for the next radio timeslot. */
} request; /**< Additional parameters for return_code @ref NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END. */
struct {
uint32_t length_us; /**< Requested extension of the radio timeslot duration (microseconds) (for minimum time see
@ref NRF_RADIO_MINIMUM_TIMESLOT_LENGTH_EXTENSION_TIME_US). */
} extend; /**< Additional parameters for return_code @ref NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND. */
} params; /**< Parameter union. */
uint8_t callback_action; /**< The action requested by the application when returning from the signal callback, see @ref
NRF_RADIO_SIGNAL_CALLBACK_ACTION. */
union {
struct {
nrf_radio_request_t *p_next; /**< The request parameters for the next radio timeslot. */
} request; /**< Additional parameters for return_code @ref NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END. */
struct {
uint32_t length_us; /**< Requested extension of the radio timeslot duration (microseconds) (for minimum time see @ref
NRF_RADIO_MINIMUM_TIMESLOT_LENGTH_EXTENSION_TIME_US). */
} extend; /**< Additional parameters for return_code @ref NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND. */
} params; /**< Parameter union. */
} nrf_radio_signal_callback_return_param_t;
/**@brief The radio timeslot signal callback type.
@@ -394,17 +391,17 @@ typedef uint8_t soc_ecb_ciphertext_t[SOC_ECB_CIPHERTEXT_LENGTH]; /**< Ciphertext
/**@brief AES ECB data structure */
typedef struct {
soc_ecb_key_t key; /**< Encryption key. */
soc_ecb_cleartext_t cleartext; /**< Cleartext data. */
soc_ecb_ciphertext_t ciphertext; /**< Ciphertext data. */
soc_ecb_key_t key; /**< Encryption key. */
soc_ecb_cleartext_t cleartext; /**< Cleartext data. */
soc_ecb_ciphertext_t ciphertext; /**< Ciphertext data. */
} nrf_ecb_hal_data_t;
/**@brief AES ECB block. Used to provide multiple blocks in a single call
to @ref sd_ecb_blocks_encrypt.*/
typedef struct {
soc_ecb_key_t const *p_key; /**< Pointer to the Encryption key. */
soc_ecb_cleartext_t const *p_cleartext; /**< Pointer to the Cleartext data. */
soc_ecb_ciphertext_t *p_ciphertext; /**< Pointer to the Ciphertext data. */
soc_ecb_key_t const *p_key; /**< Pointer to the Encryption key. */
soc_ecb_cleartext_t const *p_cleartext; /**< Pointer to the Cleartext data. */
soc_ecb_ciphertext_t *p_ciphertext; /**< Pointer to the Ciphertext data. */
} nrf_ecb_hal_data_block_t;
/**@} */
@@ -459,8 +456,8 @@ SVCALL(SD_RAND_APPLICATION_BYTES_AVAILABLE_GET, uint32_t, sd_rand_application_by
* @param[in] length Number of bytes to take from pool and place in p_buff.
*
* @retval ::NRF_SUCCESS The requested bytes were written to p_buff.
* @retval ::NRF_ERROR_SOC_RAND_NOT_ENOUGH_VALUES No bytes were written to the buffer, because there were not enough
* bytes available.
* @retval ::NRF_ERROR_SOC_RAND_NOT_ENOUGH_VALUES No bytes were written to the buffer, because there were not enough bytes
* available.
*/
SVCALL(SD_RAND_APPLICATION_VECTOR_GET, uint32_t, sd_rand_application_vector_get(uint8_t *p_buff, uint8_t length));
+14 -13
View File
@@ -68,22 +68,23 @@ extern "C" {
#else
#define GCC_CAST_CPP
#endif
#define SVCALL(number, return_type, signature) \
_Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wreturn-type\"") __attribute__((naked)) \
__attribute__((unused)) static return_type signature { \
__asm("svc %0\n" \
"bx r14" \
: \
: "I"(GCC_CAST_CPP number) \
: "r0"); \
} \
_Pragma("GCC diagnostic pop")
#define SVCALL(number, return_type, signature) \
_Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wreturn-type\"") __attribute__((naked)) \
__attribute__((unused)) static return_type signature \
{ \
__asm("svc %0\n" \
"bx r14" \
: \
: "I"(GCC_CAST_CPP number) \
: "r0"); \
} \
_Pragma("GCC diagnostic pop")
#elif defined(__ICCARM__)
#define PRAGMA(x) _Pragma(#x)
#define SVCALL(number, return_type, signature) \
PRAGMA(swi_number = (number)) \
__swi return_type signature;
#define SVCALL(number, return_type, signature) \
PRAGMA(swi_number = (number)) \
__swi return_type signature;
#else
#define SVCALL(number, return_type, signature) return_type signature
#endif
File diff suppressed because it is too large Load Diff
+449 -435
View File
@@ -12,26 +12,38 @@
// Product strings for auto-configuration
// {"PRODUCT_STRING", "CONFIG.YAML"}
// YAML paths are relative to `meshtastic/available.d`
inline const std::unordered_map<std::string, std::string> configProducts = {{"MESHTOAD", "lora-usb-meshtoad-e22.yaml"},
{"MESHSTICK", "lora-meshstick-1262.yaml"},
{"MESHADV-PI", "lora-MeshAdv-900M30S.yaml"},
{"MeshAdv Mini", "lora-MeshAdv-Mini-900M22S.yaml"},
{"POWERPI", "lora-MeshAdv-900M30S.yaml"},
{"RAK6421-13300-S1", "lora-RAK6421-13300-slot1.yaml"},
{"RAK6421-13300-S2", "lora-RAK6421-13300-slot2.yaml"}};
inline const std::unordered_map<std::string, std::string> configProducts = {
{"MESHTOAD", "lora-usb-meshtoad-e22.yaml"},
{"MESHSTICK", "lora-meshstick-1262.yaml"},
{"MESHADV-PI", "lora-MeshAdv-900M30S.yaml"},
{"MeshAdv Mini", "lora-MeshAdv-Mini-900M22S.yaml"},
{"POWERPI", "lora-MeshAdv-900M30S.yaml"},
{"RAK6421-13300-S1", "lora-RAK6421-13300-slot1.yaml"},
{"RAK6421-13300-S2", "lora-RAK6421-13300-slot2.yaml"}};
enum screen_modules { no_screen, x11, fb, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d };
enum touchscreen_modules { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };
enum portduino_log_level { level_error, level_warn, level_info, level_debug, level_trace };
enum lora_module_enum { use_simradio, use_autoconf, use_rf95, use_sx1262, use_sx1268, use_sx1280, use_lr1110, use_lr1120, use_lr1121, use_llcc68 };
enum lora_module_enum {
use_simradio,
use_autoconf,
use_rf95,
use_sx1262,
use_sx1268,
use_sx1280,
use_lr1110,
use_lr1120,
use_lr1121,
use_llcc68
};
struct pinMapping {
std::string config_section;
std::string config_name;
int pin = RADIOLIB_NC;
int gpiochip;
int line;
bool enabled = false;
std::string config_section;
std::string config_name;
int pin = RADIOLIB_NC;
int gpiochip;
int line;
bool enabled = false;
};
extern std::ofstream traceFile;
@@ -47,446 +59,448 @@ void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault
std::string exec(const char *cmd);
extern struct portduino_config_struct {
// Lora
std::map<lora_module_enum, std::string> loraModules = {
{use_simradio, "sim"}, {use_autoconf, "auto"}, {use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"},
{use_sx1280, "sx1280"}, {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
std::map<screen_modules, std::string> screen_names = {{x11, "X11"}, {fb, "FB"}, {st7789, "ST7789"}, {st7735, "ST7735"},
{st7735s, "ST7735S"}, {st7796, "ST7796"}, {ili9341, "ILI9341"}, {ili9342, "ILI9342"},
{ili9486, "ILI9486"}, {ili9488, "ILI9488"}, {hx8357d, "HX8357D"}};
lora_module_enum lora_module;
bool has_rfswitch_table = false;
uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
Module::RfSwitchMode_t rfswitch_table[8];
bool force_simradio = false;
bool has_device_id = false;
uint8_t device_id[16] = {0};
std::string lora_spi_dev = "";
std::string lora_usb_serial_num = "";
int lora_spi_dev_int = 0;
int lora_default_gpiochip = 0;
int sx126x_max_power = 22;
int sx128x_max_power = 13;
int lr1110_max_power = 22;
int lr1120_max_power = 13;
int rf95_max_power = 20;
bool dio2_as_rf_switch = false;
int dio3_tcxo_voltage = 0;
int lora_usb_pid = 0x5512;
int lora_usb_vid = 0x1A86;
int spiSpeed = 2000000;
pinMapping lora_cs_pin = {"Lora", "CS"};
pinMapping lora_irq_pin = {"Lora", "IRQ"};
pinMapping lora_busy_pin = {"Lora", "Busy"};
pinMapping lora_reset_pin = {"Lora", "Reset"};
pinMapping lora_txen_pin = {"Lora", "TXen"};
pinMapping lora_rxen_pin = {"Lora", "RXen"};
pinMapping lora_sx126x_ant_sw_pin = {"Lora", "SX126X_ANT_SW"};
// GPS
bool has_gps = false;
// I2C
std::string i2cdev = "";
// Display
std::string display_spi_dev = "";
int display_spi_dev_int = 0;
int displayBusFrequency = 40000000;
screen_modules displayPanel = no_screen;
int displayWidth = 0;
int displayHeight = 0;
bool displayRGBOrder = false;
bool displayBacklightInvert = false;
bool displayRotate = false;
int displayOffsetRotate = 1;
bool displayInvert = false;
int displayOffsetX = 0;
int displayOffsetY = 0;
pinMapping displayDC = {"Display", "DC"};
pinMapping displayCS = {"Display", "CS"};
pinMapping displayBacklight = {"Display", "Backlight"};
pinMapping displayBacklightPWMChannel = {"Display", "BacklightPWMChannel"};
pinMapping displayReset = {"Display", "Reset"};
// Touchscreen
std::string touchscreen_spi_dev = "";
int touchscreen_spi_dev_int = 0;
touchscreen_modules touchscreenModule = no_touchscreen;
int touchscreenI2CAddr = -1;
int touchscreenBusFrequency = 1000000;
int touchscreenRotate = -1;
pinMapping touchscreenCS = {"Touchscreen", "CS"};
pinMapping touchscreenIRQ = {"Touchscreen", "IRQ"};
// Input
std::string keyboardDevice = "";
std::string pointerDevice = "";
int tbDirection;
pinMapping userButtonPin = {"Input", "User"};
pinMapping tbUpPin = {"Input", "TrackballUp"};
pinMapping tbDownPin = {"Input", "TrackballDown"};
pinMapping tbLeftPin = {"Input", "TrackballLwft"};
pinMapping tbRightPin = {"Input", "TrackballRight"};
pinMapping tbPressPin = {"Input", "TrackballPress"};
// Logging
portduino_log_level logoutputlevel = level_debug;
std::string traceFilename;
bool ascii_logs = !isatty(1);
bool ascii_logs_explicit = false;
std::string JSONFilename;
meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;
// Webserver
std::string webserver_root_path = "";
std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
std::string webserver_ssl_cert_path = "/etc/meshtasticd/ssl/certificate.pem";
int webserverport = -1;
// HostMetrics
std::string hostMetrics_user_command = "";
int hostMetrics_interval = 0;
int hostMetrics_channel = 0;
// config
int configDisplayMode = 0;
bool has_configDisplayMode = false;
// General
std::string mac_address = "";
bool mac_address_explicit = false;
std::string mac_address_source = "";
std::string config_directory = "";
std::string available_directory = "/etc/meshtasticd/available.d/";
int maxtophone = 100;
int MaxNodes = 200;
pinMapping *all_pins[20] = {&lora_cs_pin,
&lora_irq_pin,
&lora_busy_pin,
&lora_reset_pin,
&lora_txen_pin,
&lora_rxen_pin,
&lora_sx126x_ant_sw_pin,
&displayDC,
&displayCS,
&displayBacklight,
&displayBacklightPWMChannel,
&displayReset,
&touchscreenCS,
&touchscreenIRQ,
&userButtonPin,
&tbUpPin,
&tbDownPin,
&tbLeftPin,
&tbRightPin,
&tbPressPin};
std::string emit_yaml() {
YAML::Emitter out;
out << YAML::BeginMap;
// Lora
out << YAML::Key << "Lora" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Module" << YAML::Value << loraModules[lora_module];
std::map<lora_module_enum, std::string> loraModules = {
{use_simradio, "sim"}, {use_autoconf, "auto"}, {use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"},
{use_sx1280, "sx1280"}, {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
for (auto lora_pin : all_pins) {
if (lora_pin->config_section == "Lora" && lora_pin->enabled) {
out << YAML::Key << lora_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << lora_pin->pin;
out << YAML::Key << "line" << YAML::Value << lora_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << lora_pin->gpiochip;
out << YAML::EndMap; // User
}
}
std::map<screen_modules, std::string> screen_names = {{x11, "X11"}, {fb, "FB"}, {st7789, "ST7789"},
{st7735, "ST7735"}, {st7735s, "ST7735S"}, {st7796, "ST7796"},
{ili9341, "ILI9341"}, {ili9342, "ILI9342"}, {ili9486, "ILI9486"},
{ili9488, "ILI9488"}, {hx8357d, "HX8357D"}};
if (sx126x_max_power != 22)
out << YAML::Key << "SX126X_MAX_POWER" << YAML::Value << sx126x_max_power;
if (sx128x_max_power != 13)
out << YAML::Key << "SX128X_MAX_POWER" << YAML::Value << sx128x_max_power;
if (lr1110_max_power != 22)
out << YAML::Key << "LR1110_MAX_POWER" << YAML::Value << lr1110_max_power;
if (lr1120_max_power != 13)
out << YAML::Key << "LR1120_MAX_POWER" << YAML::Value << lr1120_max_power;
if (rf95_max_power != 20)
out << YAML::Key << "RF95_MAX_POWER" << YAML::Value << rf95_max_power;
out << YAML::Key << "DIO2_AS_RF_SWITCH" << YAML::Value << dio2_as_rf_switch;
if (dio3_tcxo_voltage != 0)
out << YAML::Key << "DIO3_TCXO_VOLTAGE" << YAML::Value << YAML::Precision(3) << (float)dio3_tcxo_voltage / 1000;
if (lora_usb_pid != 0x5512)
out << YAML::Key << "USB_PID" << YAML::Value << YAML::Hex << lora_usb_pid;
if (lora_usb_vid != 0x1A86)
out << YAML::Key << "USB_VID" << YAML::Value << YAML::Hex << lora_usb_vid;
if (lora_spi_dev != "")
out << YAML::Key << "spidev" << YAML::Value << lora_spi_dev;
if (lora_usb_serial_num != "")
out << YAML::Key << "USB_Serialnum" << YAML::Value << lora_usb_serial_num;
out << YAML::Key << "spiSpeed" << YAML::Value << spiSpeed;
if (rfswitch_dio_pins[0] != RADIOLIB_NC) {
out << YAML::Key << "rfswitch_table" << YAML::Value << YAML::BeginMap;
lora_module_enum lora_module;
bool has_rfswitch_table = false;
uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
Module::RfSwitchMode_t rfswitch_table[8];
bool force_simradio = false;
bool has_device_id = false;
uint8_t device_id[16] = {0};
std::string lora_spi_dev = "";
std::string lora_usb_serial_num = "";
int lora_spi_dev_int = 0;
int lora_default_gpiochip = 0;
int sx126x_max_power = 22;
int sx128x_max_power = 13;
int lr1110_max_power = 22;
int lr1120_max_power = 13;
int rf95_max_power = 20;
bool dio2_as_rf_switch = false;
int dio3_tcxo_voltage = 0;
int lora_usb_pid = 0x5512;
int lora_usb_vid = 0x1A86;
int spiSpeed = 2000000;
pinMapping lora_cs_pin = {"Lora", "CS"};
pinMapping lora_irq_pin = {"Lora", "IRQ"};
pinMapping lora_busy_pin = {"Lora", "Busy"};
pinMapping lora_reset_pin = {"Lora", "Reset"};
pinMapping lora_txen_pin = {"Lora", "TXen"};
pinMapping lora_rxen_pin = {"Lora", "RXen"};
pinMapping lora_sx126x_ant_sw_pin = {"Lora", "SX126X_ANT_SW"};
out << YAML::Key << "pins";
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
// GPS
bool has_gps = false;
for (int i = 0; i < 5; i++) {
// set up the pin array first
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO5)
out << "DIO5";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO6)
out << "DIO6";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO7)
out << "DIO7";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO8)
out << "DIO8";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO10)
out << "DIO10";
}
out << YAML::EndSeq;
for (int i = 0; i < 7; i++) {
switch (i) {
case 0:
out << YAML::Key << "MODE_STBY";
break;
case 1:
out << YAML::Key << "MODE_RX";
break;
case 2:
out << YAML::Key << "MODE_TX";
break;
case 3:
out << YAML::Key << "MODE_TX_HP";
break;
case 4:
out << YAML::Key << "MODE_TX_HF";
break;
case 5:
out << YAML::Key << "MODE_GNSS";
break;
case 6:
out << YAML::Key << "MODE_WIFI";
break;
}
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
for (int j = 0; j < 5; j++) {
if (rfswitch_table[i].values[j] == HIGH) {
out << "HIGH";
} else {
out << "LOW";
}
}
out << YAML::EndSeq;
}
out << YAML::EndMap; // rfswitch_table
}
out << YAML::EndMap; // Lora
if (i2cdev != "") {
out << YAML::Key << "I2C" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "I2CDevice" << YAML::Value << i2cdev;
out << YAML::EndMap; // I2C
}
// I2C
std::string i2cdev = "";
// Display
if (displayPanel != no_screen) {
out << YAML::Key << "Display" << YAML::Value << YAML::BeginMap;
for (auto &screen_name : screen_names) {
if (displayPanel == screen_name.first)
out << YAML::Key << "Module" << YAML::Value << screen_name.second;
}
for (auto display_pin : all_pins) {
if (display_pin->config_section == "Display" && display_pin->enabled) {
out << YAML::Key << display_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << display_pin->pin;
out << YAML::Key << "line" << YAML::Value << display_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << display_pin->gpiochip;
out << YAML::EndMap;
}
}
out << YAML::Key << "spidev" << YAML::Value << display_spi_dev;
out << YAML::Key << "BusFrequency" << YAML::Value << displayBusFrequency;
if (displayWidth)
out << YAML::Key << "Width" << YAML::Value << displayWidth;
if (displayHeight)
out << YAML::Key << "Height" << YAML::Value << displayHeight;
if (displayRGBOrder)
out << YAML::Key << "RGBOrder" << YAML::Value << true;
if (displayBacklightInvert)
out << YAML::Key << "BacklightInvert" << YAML::Value << true;
if (displayRotate)
out << YAML::Key << "Rotate" << YAML::Value << true;
if (displayInvert)
out << YAML::Key << "Invert" << YAML::Value << true;
if (displayOffsetX)
out << YAML::Key << "OffsetX" << YAML::Value << displayOffsetX;
if (displayOffsetY)
out << YAML::Key << "OffsetY" << YAML::Value << displayOffsetY;
out << YAML::Key << "OffsetRotate" << YAML::Value << displayOffsetRotate;
out << YAML::EndMap; // Display
}
std::string display_spi_dev = "";
int display_spi_dev_int = 0;
int displayBusFrequency = 40000000;
screen_modules displayPanel = no_screen;
int displayWidth = 0;
int displayHeight = 0;
bool displayRGBOrder = false;
bool displayBacklightInvert = false;
bool displayRotate = false;
int displayOffsetRotate = 1;
bool displayInvert = false;
int displayOffsetX = 0;
int displayOffsetY = 0;
pinMapping displayDC = {"Display", "DC"};
pinMapping displayCS = {"Display", "CS"};
pinMapping displayBacklight = {"Display", "Backlight"};
pinMapping displayBacklightPWMChannel = {"Display", "BacklightPWMChannel"};
pinMapping displayReset = {"Display", "Reset"};
// Touchscreen
if (touchscreen_spi_dev != "") {
out << YAML::Key << "Touchscreen" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "spidev" << YAML::Value << touchscreen_spi_dev;
out << YAML::Key << "BusFrequency" << YAML::Value << touchscreenBusFrequency;
switch (touchscreenModule) {
case xpt2046:
out << YAML::Key << "Module" << YAML::Value << "XPT2046";
case stmpe610:
out << YAML::Key << "Module" << YAML::Value << "STMPE610";
case gt911:
out << YAML::Key << "Module" << YAML::Value << "GT911";
case ft5x06:
out << YAML::Key << "Module" << YAML::Value << "FT5x06";
}
for (auto touchscreen_pin : all_pins) {
if (touchscreen_pin->config_section == "Touchscreen" && touchscreen_pin->enabled) {
out << YAML::Key << touchscreen_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << touchscreen_pin->pin;
out << YAML::Key << "line" << YAML::Value << touchscreen_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << touchscreen_pin->gpiochip;
out << YAML::EndMap;
}
}
if (touchscreenRotate != -1)
out << YAML::Key << "Rotate" << YAML::Value << touchscreenRotate;
if (touchscreenI2CAddr != -1)
out << YAML::Key << "I2CAddr" << YAML::Value << touchscreenI2CAddr;
out << YAML::EndMap; // Touchscreen
}
std::string touchscreen_spi_dev = "";
int touchscreen_spi_dev_int = 0;
touchscreen_modules touchscreenModule = no_touchscreen;
int touchscreenI2CAddr = -1;
int touchscreenBusFrequency = 1000000;
int touchscreenRotate = -1;
pinMapping touchscreenCS = {"Touchscreen", "CS"};
pinMapping touchscreenIRQ = {"Touchscreen", "IRQ"};
// Input
out << YAML::Key << "Input" << YAML::Value << YAML::BeginMap;
if (keyboardDevice != "")
out << YAML::Key << "KeyboardDevice" << YAML::Value << keyboardDevice;
if (pointerDevice != "")
out << YAML::Key << "PointerDevice" << YAML::Value << pointerDevice;
std::string keyboardDevice = "";
std::string pointerDevice = "";
int tbDirection;
pinMapping userButtonPin = {"Input", "User"};
pinMapping tbUpPin = {"Input", "TrackballUp"};
pinMapping tbDownPin = {"Input", "TrackballDown"};
pinMapping tbLeftPin = {"Input", "TrackballLwft"};
pinMapping tbRightPin = {"Input", "TrackballRight"};
pinMapping tbPressPin = {"Input", "TrackballPress"};
for (auto input_pin : all_pins) {
if (input_pin->config_section == "Input" && input_pin->enabled) {
out << YAML::Key << input_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << input_pin->pin;
out << YAML::Key << "line" << YAML::Value << input_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << input_pin->gpiochip;
out << YAML::EndMap;
}
}
if (tbDirection == 3)
out << YAML::Key << "TrackballDirection" << YAML::Value << "FALLING";
// Logging
portduino_log_level logoutputlevel = level_debug;
std::string traceFilename;
bool ascii_logs = !isatty(1);
bool ascii_logs_explicit = false;
out << YAML::EndMap; // Input
out << YAML::Key << "Logging" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "LogLevel" << YAML::Value;
switch (logoutputlevel) {
case level_error:
out << "error";
break;
case level_warn:
out << "warn";
break;
case level_info:
out << "info";
break;
case level_debug:
out << "debug";
break;
case level_trace:
out << "trace";
break;
}
if (traceFilename != "")
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
if (JSONFilename != "") {
out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "telemetry";
else if (JSONFilter == meshtastic_PortNum_NODEINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "nodeinfo";
else if (JSONFilter == meshtastic_PortNum_POSITION_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "position";
else if (JSONFilter == meshtastic_PortNum_WAYPOINT_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "waypoint";
else if (JSONFilter == meshtastic_PortNum_NEIGHBORINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "neighborinfo";
else if (JSONFilter == meshtastic_PortNum_TRACEROUTE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "traceroute";
else if (JSONFilter == meshtastic_PortNum_DETECTION_SENSOR_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "detection";
else if (JSONFilter == meshtastic_PortNum_PAXCOUNTER_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "paxcounter";
else if (JSONFilter == meshtastic_PortNum_REMOTE_HARDWARE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "remotehardware";
}
if (ascii_logs_explicit) {
out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
}
out << YAML::EndMap; // Logging
std::string JSONFilename;
meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;
// Webserver
if (webserver_root_path != "") {
out << YAML::Key << "Webserver" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "RootPath" << YAML::Value << webserver_root_path;
out << YAML::Key << "SSLKey" << YAML::Value << webserver_ssl_key_path;
out << YAML::Key << "SSLCert" << YAML::Value << webserver_ssl_cert_path;
out << YAML::Key << "Port" << YAML::Value << webserverport;
out << YAML::EndMap; // Webserver
}
std::string webserver_root_path = "";
std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
std::string webserver_ssl_cert_path = "/etc/meshtasticd/ssl/certificate.pem";
int webserverport = -1;
// HostMetrics
if (hostMetrics_user_command != "") {
out << YAML::Key << "HostMetrics" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "UserStringCommand" << YAML::Value << hostMetrics_user_command;
out << YAML::Key << "ReportInterval" << YAML::Value << hostMetrics_interval;
out << YAML::Key << "Channel" << YAML::Value << hostMetrics_channel;
out << YAML::EndMap; // HostMetrics
}
std::string hostMetrics_user_command = "";
int hostMetrics_interval = 0;
int hostMetrics_channel = 0;
// config
if (has_configDisplayMode) {
out << YAML::Key << "Config" << YAML::Value << YAML::BeginMap;
switch (configDisplayMode) {
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
break;
}
out << YAML::EndMap; // Config
}
int configDisplayMode = 0;
bool has_configDisplayMode = false;
// General
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
if (config_directory != "")
out << YAML::Key << "ConfigDirectory" << YAML::Value << config_directory;
if (mac_address_explicit)
out << YAML::Key << "MACAddress" << YAML::Value << mac_address;
if (mac_address_source != "")
out << YAML::Key << "MACAddressSource" << YAML::Value << mac_address_source;
if (available_directory != "")
out << YAML::Key << "AvailableDirectory" << YAML::Value << available_directory;
out << YAML::Key << "MaxMessageQueue" << YAML::Value << maxtophone;
out << YAML::Key << "MaxNodes" << YAML::Value << MaxNodes;
out << YAML::EndMap; // General
return out.c_str();
}
std::string mac_address = "";
bool mac_address_explicit = false;
std::string mac_address_source = "";
std::string config_directory = "";
std::string available_directory = "/etc/meshtasticd/available.d/";
int maxtophone = 100;
int MaxNodes = 200;
pinMapping *all_pins[20] = {&lora_cs_pin,
&lora_irq_pin,
&lora_busy_pin,
&lora_reset_pin,
&lora_txen_pin,
&lora_rxen_pin,
&lora_sx126x_ant_sw_pin,
&displayDC,
&displayCS,
&displayBacklight,
&displayBacklightPWMChannel,
&displayReset,
&touchscreenCS,
&touchscreenIRQ,
&userButtonPin,
&tbUpPin,
&tbDownPin,
&tbLeftPin,
&tbRightPin,
&tbPressPin};
std::string emit_yaml()
{
YAML::Emitter out;
out << YAML::BeginMap;
// Lora
out << YAML::Key << "Lora" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Module" << YAML::Value << loraModules[lora_module];
for (auto lora_pin : all_pins) {
if (lora_pin->config_section == "Lora" && lora_pin->enabled) {
out << YAML::Key << lora_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << lora_pin->pin;
out << YAML::Key << "line" << YAML::Value << lora_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << lora_pin->gpiochip;
out << YAML::EndMap; // User
}
}
if (sx126x_max_power != 22)
out << YAML::Key << "SX126X_MAX_POWER" << YAML::Value << sx126x_max_power;
if (sx128x_max_power != 13)
out << YAML::Key << "SX128X_MAX_POWER" << YAML::Value << sx128x_max_power;
if (lr1110_max_power != 22)
out << YAML::Key << "LR1110_MAX_POWER" << YAML::Value << lr1110_max_power;
if (lr1120_max_power != 13)
out << YAML::Key << "LR1120_MAX_POWER" << YAML::Value << lr1120_max_power;
if (rf95_max_power != 20)
out << YAML::Key << "RF95_MAX_POWER" << YAML::Value << rf95_max_power;
out << YAML::Key << "DIO2_AS_RF_SWITCH" << YAML::Value << dio2_as_rf_switch;
if (dio3_tcxo_voltage != 0)
out << YAML::Key << "DIO3_TCXO_VOLTAGE" << YAML::Value << YAML::Precision(3) << (float)dio3_tcxo_voltage / 1000;
if (lora_usb_pid != 0x5512)
out << YAML::Key << "USB_PID" << YAML::Value << YAML::Hex << lora_usb_pid;
if (lora_usb_vid != 0x1A86)
out << YAML::Key << "USB_VID" << YAML::Value << YAML::Hex << lora_usb_vid;
if (lora_spi_dev != "")
out << YAML::Key << "spidev" << YAML::Value << lora_spi_dev;
if (lora_usb_serial_num != "")
out << YAML::Key << "USB_Serialnum" << YAML::Value << lora_usb_serial_num;
out << YAML::Key << "spiSpeed" << YAML::Value << spiSpeed;
if (rfswitch_dio_pins[0] != RADIOLIB_NC) {
out << YAML::Key << "rfswitch_table" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pins";
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
for (int i = 0; i < 5; i++) {
// set up the pin array first
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO5)
out << "DIO5";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO6)
out << "DIO6";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO7)
out << "DIO7";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO8)
out << "DIO8";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO10)
out << "DIO10";
}
out << YAML::EndSeq;
for (int i = 0; i < 7; i++) {
switch (i) {
case 0:
out << YAML::Key << "MODE_STBY";
break;
case 1:
out << YAML::Key << "MODE_RX";
break;
case 2:
out << YAML::Key << "MODE_TX";
break;
case 3:
out << YAML::Key << "MODE_TX_HP";
break;
case 4:
out << YAML::Key << "MODE_TX_HF";
break;
case 5:
out << YAML::Key << "MODE_GNSS";
break;
case 6:
out << YAML::Key << "MODE_WIFI";
break;
}
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
for (int j = 0; j < 5; j++) {
if (rfswitch_table[i].values[j] == HIGH) {
out << "HIGH";
} else {
out << "LOW";
}
}
out << YAML::EndSeq;
}
out << YAML::EndMap; // rfswitch_table
}
out << YAML::EndMap; // Lora
if (i2cdev != "") {
out << YAML::Key << "I2C" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "I2CDevice" << YAML::Value << i2cdev;
out << YAML::EndMap; // I2C
}
// Display
if (displayPanel != no_screen) {
out << YAML::Key << "Display" << YAML::Value << YAML::BeginMap;
for (auto &screen_name : screen_names) {
if (displayPanel == screen_name.first)
out << YAML::Key << "Module" << YAML::Value << screen_name.second;
}
for (auto display_pin : all_pins) {
if (display_pin->config_section == "Display" && display_pin->enabled) {
out << YAML::Key << display_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << display_pin->pin;
out << YAML::Key << "line" << YAML::Value << display_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << display_pin->gpiochip;
out << YAML::EndMap;
}
}
out << YAML::Key << "spidev" << YAML::Value << display_spi_dev;
out << YAML::Key << "BusFrequency" << YAML::Value << displayBusFrequency;
if (displayWidth)
out << YAML::Key << "Width" << YAML::Value << displayWidth;
if (displayHeight)
out << YAML::Key << "Height" << YAML::Value << displayHeight;
if (displayRGBOrder)
out << YAML::Key << "RGBOrder" << YAML::Value << true;
if (displayBacklightInvert)
out << YAML::Key << "BacklightInvert" << YAML::Value << true;
if (displayRotate)
out << YAML::Key << "Rotate" << YAML::Value << true;
if (displayInvert)
out << YAML::Key << "Invert" << YAML::Value << true;
if (displayOffsetX)
out << YAML::Key << "OffsetX" << YAML::Value << displayOffsetX;
if (displayOffsetY)
out << YAML::Key << "OffsetY" << YAML::Value << displayOffsetY;
out << YAML::Key << "OffsetRotate" << YAML::Value << displayOffsetRotate;
out << YAML::EndMap; // Display
}
// Touchscreen
if (touchscreen_spi_dev != "") {
out << YAML::Key << "Touchscreen" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "spidev" << YAML::Value << touchscreen_spi_dev;
out << YAML::Key << "BusFrequency" << YAML::Value << touchscreenBusFrequency;
switch (touchscreenModule) {
case xpt2046:
out << YAML::Key << "Module" << YAML::Value << "XPT2046";
case stmpe610:
out << YAML::Key << "Module" << YAML::Value << "STMPE610";
case gt911:
out << YAML::Key << "Module" << YAML::Value << "GT911";
case ft5x06:
out << YAML::Key << "Module" << YAML::Value << "FT5x06";
}
for (auto touchscreen_pin : all_pins) {
if (touchscreen_pin->config_section == "Touchscreen" && touchscreen_pin->enabled) {
out << YAML::Key << touchscreen_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << touchscreen_pin->pin;
out << YAML::Key << "line" << YAML::Value << touchscreen_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << touchscreen_pin->gpiochip;
out << YAML::EndMap;
}
}
if (touchscreenRotate != -1)
out << YAML::Key << "Rotate" << YAML::Value << touchscreenRotate;
if (touchscreenI2CAddr != -1)
out << YAML::Key << "I2CAddr" << YAML::Value << touchscreenI2CAddr;
out << YAML::EndMap; // Touchscreen
}
// Input
out << YAML::Key << "Input" << YAML::Value << YAML::BeginMap;
if (keyboardDevice != "")
out << YAML::Key << "KeyboardDevice" << YAML::Value << keyboardDevice;
if (pointerDevice != "")
out << YAML::Key << "PointerDevice" << YAML::Value << pointerDevice;
for (auto input_pin : all_pins) {
if (input_pin->config_section == "Input" && input_pin->enabled) {
out << YAML::Key << input_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << input_pin->pin;
out << YAML::Key << "line" << YAML::Value << input_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << input_pin->gpiochip;
out << YAML::EndMap;
}
}
if (tbDirection == 3)
out << YAML::Key << "TrackballDirection" << YAML::Value << "FALLING";
out << YAML::EndMap; // Input
out << YAML::Key << "Logging" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "LogLevel" << YAML::Value;
switch (logoutputlevel) {
case level_error:
out << "error";
break;
case level_warn:
out << "warn";
break;
case level_info:
out << "info";
break;
case level_debug:
out << "debug";
break;
case level_trace:
out << "trace";
break;
}
if (traceFilename != "")
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
if (JSONFilename != "") {
out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "telemetry";
else if (JSONFilter == meshtastic_PortNum_NODEINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "nodeinfo";
else if (JSONFilter == meshtastic_PortNum_POSITION_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "position";
else if (JSONFilter == meshtastic_PortNum_WAYPOINT_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "waypoint";
else if (JSONFilter == meshtastic_PortNum_NEIGHBORINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "neighborinfo";
else if (JSONFilter == meshtastic_PortNum_TRACEROUTE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "traceroute";
else if (JSONFilter == meshtastic_PortNum_DETECTION_SENSOR_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "detection";
else if (JSONFilter == meshtastic_PortNum_PAXCOUNTER_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "paxcounter";
else if (JSONFilter == meshtastic_PortNum_REMOTE_HARDWARE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "remotehardware";
}
if (ascii_logs_explicit) {
out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
}
out << YAML::EndMap; // Logging
// Webserver
if (webserver_root_path != "") {
out << YAML::Key << "Webserver" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "RootPath" << YAML::Value << webserver_root_path;
out << YAML::Key << "SSLKey" << YAML::Value << webserver_ssl_key_path;
out << YAML::Key << "SSLCert" << YAML::Value << webserver_ssl_cert_path;
out << YAML::Key << "Port" << YAML::Value << webserverport;
out << YAML::EndMap; // Webserver
}
// HostMetrics
if (hostMetrics_user_command != "") {
out << YAML::Key << "HostMetrics" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "UserStringCommand" << YAML::Value << hostMetrics_user_command;
out << YAML::Key << "ReportInterval" << YAML::Value << hostMetrics_interval;
out << YAML::Key << "Channel" << YAML::Value << hostMetrics_channel;
out << YAML::EndMap; // HostMetrics
}
// config
if (has_configDisplayMode) {
out << YAML::Key << "Config" << YAML::Value << YAML::BeginMap;
switch (configDisplayMode) {
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
break;
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
break;
}
out << YAML::EndMap; // Config
}
// General
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
if (config_directory != "")
out << YAML::Key << "ConfigDirectory" << YAML::Value << config_directory;
if (mac_address_explicit)
out << YAML::Key << "MACAddress" << YAML::Value << mac_address;
if (mac_address_source != "")
out << YAML::Key << "MACAddressSource" << YAML::Value << mac_address_source;
if (available_directory != "")
out << YAML::Key << "AvailableDirectory" << YAML::Value << available_directory;
out << YAML::Key << "MaxMessageQueue" << YAML::Value << maxtophone;
out << YAML::Key << "MaxNodes" << YAML::Value << MaxNodes;
out << YAML::EndMap; // General
return out.c_str();
}
} portduino_config;
+280 -248
View File
@@ -2,311 +2,341 @@
#include "MeshService.h"
#include "Router.h"
SimRadio::SimRadio() : NotifiedWorkerThread("SimRadio") { instance = this; }
SimRadio::SimRadio() : NotifiedWorkerThread("SimRadio")
{
instance = this;
}
SimRadio *SimRadio::instance;
ErrorCode SimRadio::send(meshtastic_MeshPacket *p) {
printPacket("enqueuing for send", p);
ErrorCode SimRadio::send(meshtastic_MeshPacket *p)
{
printPacket("enqueuing for send", p);
bool dropped = false;
ErrorCode res = txQueue.enqueue(p, &dropped) ? ERRNO_OK : ERRNO_UNKNOWN;
bool dropped = false;
ErrorCode res = txQueue.enqueue(p, &dropped) ? ERRNO_OK : ERRNO_UNKNOWN;
if (dropped) {
txDrop++;
}
if (dropped) {
txDrop++;
}
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
packetPool.release(p);
if (res != ERRNO_OK) { // we weren't able to queue it, so we must drop it to prevent leaks
packetPool.release(p);
return res;
}
// set (random) transmit delay to let others reconfigure their radio,
// to avoid collisions and implement timing-based flooding
LOG_DEBUG("Set random delay before tx");
setTransmitDelay();
return res;
}
// set (random) transmit delay to let others reconfigure their radio,
// to avoid collisions and implement timing-based flooding
LOG_DEBUG("Set random delay before tx");
setTransmitDelay();
return res;
}
void SimRadio::setTransmitDelay() {
meshtastic_MeshPacket *p = txQueue.getFront();
// We want all sending/receiving to be done by our daemon thread.
// We use a delay here because this packet might have been sent in response to a packet we just received.
// So we want to make sure the other side has had a chance to reconfigure its radio.
void SimRadio::setTransmitDelay()
{
meshtastic_MeshPacket *p = txQueue.getFront();
// We want all sending/receiving to be done by our daemon thread.
// We use a delay here because this packet might have been sent in response to a packet we just received.
// So we want to make sure the other side has had a chance to reconfigure its radio.
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
* floor.
*/
if (p->rx_snr == 0 && p->rx_rssi == 0) {
startTransmitTimer(true);
} else {
// If there is a SNR, start a timer scaled based on that SNR.
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
startTransmitTimerRebroadcast(p);
}
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
* floor.
*/
if (p->rx_snr == 0 && p->rx_rssi == 0) {
startTransmitTimer(true);
} else {
// If there is a SNR, start a timer scaled based on that SNR.
LOG_DEBUG("rx_snr found. hop_limit:%d rx_snr:%f", p->hop_limit, p->rx_snr);
startTransmitTimerRebroadcast(p);
}
}
void SimRadio::startTransmitTimer(bool withDelay) {
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.empty()) {
uint32_t delayMsec = !withDelay ? 1 : getTxDelayMsec();
// LOG_DEBUG("xmit timer %d", delay);
notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false);
}
void SimRadio::startTransmitTimer(bool withDelay)
{
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.empty()) {
uint32_t delayMsec = !withDelay ? 1 : getTxDelayMsec();
// LOG_DEBUG("xmit timer %d", delay);
notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false);
}
}
void SimRadio::startTransmitTimerRebroadcast(meshtastic_MeshPacket *p) {
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.empty()) {
uint32_t delayMsec = getTxDelayMsecWeighted(p);
// LOG_DEBUG("xmit timer %d", delay);
notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false);
}
void SimRadio::startTransmitTimerRebroadcast(meshtastic_MeshPacket *p)
{
// If we have work to do and the timer wasn't already scheduled, schedule it now
if (!txQueue.empty()) {
uint32_t delayMsec = getTxDelayMsecWeighted(p);
// LOG_DEBUG("xmit timer %d", delay);
notifyLater(delayMsec, TRANSMIT_DELAY_COMPLETED, false);
}
}
void SimRadio::handleTransmitInterrupt() {
// This can be null if we forced the device to enter standby mode. In that case
// ignore the transmit interrupt
if (sendingPacket)
completeSending();
void SimRadio::handleTransmitInterrupt()
{
// This can be null if we forced the device to enter standby mode. In that case
// ignore the transmit interrupt
if (sendingPacket)
completeSending();
isReceiving = true;
if (receivingPacket) // This happens when we don't consider something a collision if we weren't sending long enough
handleReceiveInterrupt();
isReceiving = true;
if (receivingPacket) // This happens when we don't consider something a collision if we weren't sending long enough
handleReceiveInterrupt();
}
void SimRadio::completeSending() {
// We are careful to clear sending packet before calling printPacket because
// that can take a long time
auto p = sendingPacket;
sendingPacket = NULL;
void SimRadio::completeSending()
{
// We are careful to clear sending packet before calling printPacket because
// that can take a long time
auto p = sendingPacket;
sendingPacket = NULL;
if (p) {
txGood++;
if (!isFromUs(p))
txRelay++;
printPacket("Completed sending", p);
if (p) {
txGood++;
if (!isFromUs(p))
txRelay++;
printPacket("Completed sending", p);
// We are done sending that packet, release it
packetPool.release(p);
// LOG_DEBUG("Done with send");
}
// We are done sending that packet, release it
packetPool.release(p);
// LOG_DEBUG("Done with send");
}
}
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
bool SimRadio::canSendImmediately() {
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
// To do otherwise would be doubly bad because not only would we drop the packet that was on the way in,
// we almost certainly guarantee no one outside will like the packet we are sending.
bool busyTx = sendingPacket != NULL;
bool busyRx = isReceiving && isActivelyReceiving();
bool SimRadio::canSendImmediately()
{
// We wait _if_ we are partially though receiving a packet (rather than just merely waiting for one).
// To do otherwise would be doubly bad because not only would we drop the packet that was on the way in,
// we almost certainly guarantee no one outside will like the packet we are sending.
bool busyTx = sendingPacket != NULL;
bool busyRx = isReceiving && isActivelyReceiving();
if (busyTx || busyRx) {
if (busyTx)
LOG_WARN("Can not send yet, busyTx");
if (busyRx)
LOG_WARN("Can not send yet, busyRx");
return false;
} else
return true;
if (busyTx || busyRx) {
if (busyTx)
LOG_WARN("Can not send yet, busyTx");
if (busyRx)
LOG_WARN("Can not send yet, busyRx");
return false;
} else
return true;
}
bool SimRadio::isActivelyReceiving() { return receivingPacket != nullptr; }
bool SimRadio::isActivelyReceiving()
{
return receivingPacket != nullptr;
}
bool SimRadio::isChannelActive() { return receivingPacket != nullptr; }
bool SimRadio::isChannelActive()
{
return receivingPacket != nullptr;
}
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool SimRadio::cancelSending(NodeNum from, PacketId id) {
auto p = txQueue.remove(from, id);
if (p)
packetPool.release(p); // free the packet we just removed
bool SimRadio::cancelSending(NodeNum from, PacketId id)
{
auto p = txQueue.remove(from, id);
if (p)
packetPool.release(p); // free the packet we just removed
bool result = (p != NULL);
LOG_DEBUG("cancelSending id=0x%x, removed=%d", id, result);
return result;
bool result = (p != NULL);
LOG_DEBUG("cancelSending id=0x%x, removed=%d", id, result);
return result;
}
/** Attempt to find a packet in the TxQueue. Returns true if the packet was found. */
bool SimRadio::findInTxQueue(NodeNum from, PacketId id) { return txQueue.find(from, id); }
bool SimRadio::findInTxQueue(NodeNum from, PacketId id)
{
return txQueue.find(from, id);
}
void SimRadio::onNotify(uint32_t notification) {
switch (notification) {
case ISR_TX:
handleTransmitInterrupt();
// LOG_DEBUG("tx complete - starting timer");
startTransmitTimer();
break;
case ISR_RX:
handleReceiveInterrupt();
// LOG_DEBUG("rx complete - starting timer");
startTransmitTimer();
break;
case TRANSMIT_DELAY_COMPLETED:
if (receivingPacket) { // This happens when we had a timer pending and we started receiving
handleReceiveInterrupt();
startTransmitTimer();
break;
}
LOG_DEBUG("delay done");
// If we are not currently in receive mode, then restart the random delay (this can happen if the main thread
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
if (!txQueue.empty()) {
if (!canSendImmediately()) {
// LOG_DEBUG("Currently Rx/Tx-ing: set random delay");
setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
} else {
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
// LOG_DEBUG("Channel is active: set random delay");
setTransmitDelay(); // reset random delay
} else {
// Send any outgoing packets we have ready
meshtastic_MeshPacket *txp = txQueue.dequeue();
assert(txp);
startSend(txp);
// Packet has been sent, count it toward our TX airtime utilization.
uint32_t xmitMsec = RadioInterface::getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
notifyLater(xmitMsec, ISR_TX, false); // Model the time it is busy sending
void SimRadio::onNotify(uint32_t notification)
{
switch (notification) {
case ISR_TX:
handleTransmitInterrupt();
// LOG_DEBUG("tx complete - starting timer");
startTransmitTimer();
break;
case ISR_RX:
handleReceiveInterrupt();
// LOG_DEBUG("rx complete - starting timer");
startTransmitTimer();
break;
case TRANSMIT_DELAY_COMPLETED:
if (receivingPacket) { // This happens when we had a timer pending and we started receiving
handleReceiveInterrupt();
startTransmitTimer();
break;
}
}
} else {
// LOG_DEBUG("done with txqueue");
LOG_DEBUG("delay done");
// If we are not currently in receive mode, then restart the random delay (this can happen if the main thread
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
if (!txQueue.empty()) {
if (!canSendImmediately()) {
// LOG_DEBUG("Currently Rx/Tx-ing: set random delay");
setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
} else {
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
// LOG_DEBUG("Channel is active: set random delay");
setTransmitDelay(); // reset random delay
} else {
// Send any outgoing packets we have ready
meshtastic_MeshPacket *txp = txQueue.dequeue();
assert(txp);
startSend(txp);
// Packet has been sent, count it toward our TX airtime utilization.
uint32_t xmitMsec = RadioInterface::getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
notifyLater(xmitMsec, ISR_TX, false); // Model the time it is busy sending
}
}
} else {
// LOG_DEBUG("done with txqueue");
}
break;
default:
assert(0); // We expected to receive a valid notification from the ISR
}
break;
default:
assert(0); // We expected to receive a valid notification from the ISR
}
}
/** start an immediate transmit */
void SimRadio::startSend(meshtastic_MeshPacket *txp) {
printPacket("Start low level send", txp);
isReceiving = false;
size_t numbytes = beginSending(txp);
meshtastic_MeshPacket *p = packetPool.allocCopy(*txp);
perhapsDecode(p);
meshtastic_Compressed c = meshtastic_Compressed_init_default;
c.portnum = p->decoded.portnum;
// LOG_DEBUG("Send back to simulator with portNum %d", p->decoded.portnum);
if (p->decoded.payload.size <= sizeof(c.data.bytes)) {
memcpy(&c.data.bytes, p->decoded.payload.bytes, p->decoded.payload.size);
c.data.size = p->decoded.payload.size;
} else {
LOG_WARN("Payload size larger than compressed message allows! Send empty payload");
}
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Compressed_msg, &c);
p->decoded.portnum = meshtastic_PortNum_SIMULATOR_APP;
void SimRadio::startSend(meshtastic_MeshPacket *txp)
{
printPacket("Start low level send", txp);
isReceiving = false;
size_t numbytes = beginSending(txp);
meshtastic_MeshPacket *p = packetPool.allocCopy(*txp);
perhapsDecode(p);
meshtastic_Compressed c = meshtastic_Compressed_init_default;
c.portnum = p->decoded.portnum;
// LOG_DEBUG("Send back to simulator with portNum %d", p->decoded.portnum);
if (p->decoded.payload.size <= sizeof(c.data.bytes)) {
memcpy(&c.data.bytes, p->decoded.payload.bytes, p->decoded.payload.size);
c.data.size = p->decoded.payload.size;
} else {
LOG_WARN("Payload size larger than compressed message allows! Send empty payload");
}
p->decoded.payload.size =
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Compressed_msg, &c);
p->decoded.portnum = meshtastic_PortNum_SIMULATOR_APP;
service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id);
service->sendToPhone(p); // Sending back to simulator
service->loop(); // Process the send immediately
service->sendQueueStatusToPhone(router->getQueueStatus(), 0, p->id);
service->sendToPhone(p); // Sending back to simulator
service->loop(); // Process the send immediately
}
// Simulates device received a packet via the LoRa chip
void SimRadio::unpackAndReceive(meshtastic_MeshPacket &p) {
// Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first
meshtastic_Compressed scratch;
meshtastic_Compressed *decoded = NULL;
if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch));
p.decoded.payload.size = pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch);
if (p.decoded.payload.size) {
decoded = &scratch;
// Extract the original payload and replace
memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data));
// Switch the port from PortNum_SIMULATOR_APP back to the original PortNum
p.decoded.portnum = decoded->portnum;
} else
LOG_ERROR("Error decoding proto for simulator message!");
}
// Let SimRadio receive as if it did via its LoRa chip
startReceive(&p);
void SimRadio::unpackAndReceive(meshtastic_MeshPacket &p)
{
// Simulator packet (=Compressed packet) is encapsulated in a MeshPacket, so need to unwrap first
meshtastic_Compressed scratch;
meshtastic_Compressed *decoded = NULL;
if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch));
p.decoded.payload.size =
pb_decode_from_bytes(p.decoded.payload.bytes, p.decoded.payload.size, &meshtastic_Compressed_msg, &scratch);
if (p.decoded.payload.size) {
decoded = &scratch;
// Extract the original payload and replace
memcpy(&p.decoded.payload, &decoded->data, sizeof(decoded->data));
// Switch the port from PortNum_SIMULATOR_APP back to the original PortNum
p.decoded.portnum = decoded->portnum;
} else
LOG_ERROR("Error decoding proto for simulator message!");
}
// Let SimRadio receive as if it did via its LoRa chip
startReceive(&p);
}
void SimRadio::startReceive(meshtastic_MeshPacket *p) {
void SimRadio::startReceive(meshtastic_MeshPacket *p)
{
#ifdef USERPREFS_SIMRADIO_EMULATE_COLLISIONS
if (isActivelyReceiving()) {
LOG_WARN("Collision detected, dropping current and previous packet!");
rxBad++;
airTime->logAirtime(RX_ALL_LOG, getPacketTime(receivingPacket, true));
packetPool.release(receivingPacket);
receivingPacket = nullptr;
return;
} else if (sendingPacket) {
uint32_t airtimeLeft = tillRun(millis());
if (airtimeLeft <= 0) {
LOG_WARN("Transmitting packet was already done");
handleTransmitInterrupt(); // Finish sending first
} else if ((interval - airtimeLeft) > preambleTimeMsec) {
// Only if transmitting for longer than preamble there is a collision
// (channel should actually be detected as active otherwise)
LOG_WARN("Collision detected during transmission!");
return;
if (isActivelyReceiving()) {
LOG_WARN("Collision detected, dropping current and previous packet!");
rxBad++;
airTime->logAirtime(RX_ALL_LOG, getPacketTime(receivingPacket, true));
packetPool.release(receivingPacket);
receivingPacket = nullptr;
return;
} else if (sendingPacket) {
uint32_t airtimeLeft = tillRun(millis());
if (airtimeLeft <= 0) {
LOG_WARN("Transmitting packet was already done");
handleTransmitInterrupt(); // Finish sending first
} else if ((interval - airtimeLeft) > preambleTimeMsec) {
// Only if transmitting for longer than preamble there is a collision
// (channel should actually be detected as active otherwise)
LOG_WARN("Collision detected during transmission!");
return;
}
}
}
isReceiving = true;
receivingPacket = packetPool.allocCopy(*p);
uint32_t airtimeMsec = getPacketTime(p, true);
notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving
isReceiving = true;
receivingPacket = packetPool.allocCopy(*p);
uint32_t airtimeMsec = getPacketTime(p, true);
notifyLater(airtimeMsec, ISR_RX, false); // Model the time it is busy receiving
#else
isReceiving = true;
receivingPacket = packetPool.allocCopy(*p);
handleReceiveInterrupt(); // Simulate receiving the packet immediately
startTransmitTimer();
isReceiving = true;
receivingPacket = packetPool.allocCopy(*p);
handleReceiveInterrupt(); // Simulate receiving the packet immediately
startTransmitTimer();
#endif
}
meshtastic_QueueStatus SimRadio::getQueueStatus() {
meshtastic_QueueStatus qs;
meshtastic_QueueStatus SimRadio::getQueueStatus()
{
meshtastic_QueueStatus qs;
qs.res = qs.mesh_packet_id = 0;
qs.free = txQueue.getFree();
qs.maxlen = txQueue.getMaxLen();
qs.res = qs.mesh_packet_id = 0;
qs.free = txQueue.getFree();
qs.maxlen = txQueue.getMaxLen();
return qs;
return qs;
}
void SimRadio::handleReceiveInterrupt() {
if (receivingPacket == nullptr) {
return;
}
void SimRadio::handleReceiveInterrupt()
{
if (receivingPacket == nullptr) {
return;
}
if (!isReceiving) {
LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode");
return;
}
if (!isReceiving) {
LOG_DEBUG("*** WAS_ASSERT *** handleReceiveInterrupt called when not in receive mode");
return;
}
LOG_DEBUG("HANDLE RECEIVE INTERRUPT");
rxGood++;
LOG_DEBUG("HANDLE RECEIVE INTERRUPT");
rxGood++;
meshtastic_MeshPacket *mp = packetPool.allocCopy(*receivingPacket); // keep a copy in packetPool
packetPool.release(receivingPacket); // release the original
receivingPacket = nullptr;
meshtastic_MeshPacket *mp = packetPool.allocCopy(*receivingPacket); // keep a copy in packetPool
packetPool.release(receivingPacket); // release the original
receivingPacket = nullptr;
printPacket("Lora RX", mp);
printPacket("Lora RX", mp);
airTime->logAirtime(RX_LOG, RadioInterface::getPacketTime(mp, true));
airTime->logAirtime(RX_LOG, RadioInterface::getPacketTime(mp, true));
deliverToReceiver(mp);
deliverToReceiver(mp);
}
size_t SimRadio::getPacketLength(meshtastic_MeshPacket *mp) {
auto &p = mp->decoded;
return (size_t)p.payload.size + sizeof(PacketHeader);
size_t SimRadio::getPacketLength(meshtastic_MeshPacket *mp)
{
auto &p = mp->decoded;
return (size_t)p.payload.size + sizeof(PacketHeader);
}
int16_t SimRadio::readData(uint8_t *data, size_t len) {
int16_t state = RADIOLIB_ERR_NONE;
int16_t SimRadio::readData(uint8_t *data, size_t len)
{
int16_t state = RADIOLIB_ERR_NONE;
if (state == RADIOLIB_ERR_NONE) {
// add null terminator
data[len] = 0;
}
if (state == RADIOLIB_ERR_NONE) {
// add null terminator
data[len] = 0;
}
return state;
return state;
}
/**
@@ -316,18 +346,20 @@ int16_t SimRadio::readData(uint8_t *data, size_t len) {
*
* @return num msecs for the packet
*/
uint32_t SimRadio::getPacketTime(uint32_t pl, bool received) {
float bandwidthHz = bw * 1000.0f;
bool headDisable = false; // we currently always use the header
float tSym = (1 << sf) / bandwidthHz;
uint32_t SimRadio::getPacketTime(uint32_t pl, bool received)
{
float bandwidthHz = bw * 1000.0f;
bool headDisable = false; // we currently always use the header
float tSym = (1 << sf) / bandwidthHz;
bool lowDataOptEn = tSym > 16e-3 ? true : false; // Needed if symbol time is >16ms
bool lowDataOptEn = tSym > 16e-3 ? true : false; // Needed if symbol time is >16ms
float tPreamble = (preambleLength + 4.25f) * tSym;
float numPayloadSym = 8 + max(ceilf(((8.0f * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * cr), 0.0f);
float tPayload = numPayloadSym * tSym;
float tPacket = tPreamble + tPayload;
float tPreamble = (preambleLength + 4.25f) * tSym;
float numPayloadSym =
8 + max(ceilf(((8.0f * pl - 4 * sf + 28 + 16 - 20 * headDisable) / (4 * (sf - 2 * lowDataOptEn))) * cr), 0.0f);
float tPayload = numPayloadSym * tSym;
float tPacket = tPreamble + tPayload;
uint32_t msecs = tPacket * 1000;
return msecs;
uint32_t msecs = tPacket * 1000;
return msecs;
}
+59 -58
View File
@@ -7,89 +7,90 @@
#include <RadioLib.h>
class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThread {
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
class SimRadio : public RadioInterface, protected concurrency::NotifiedWorkerThread
{
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
public:
SimRadio();
public:
SimRadio();
/** MeshService needs this to find our active instance
*/
static SimRadio *instance;
/** MeshService needs this to find our active instance
*/
static SimRadio *instance;
virtual ErrorCode send(meshtastic_MeshPacket *p) override;
virtual ErrorCode send(meshtastic_MeshPacket *p) override;
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive();
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive();
/** are we actively receiving a packet (only called during receiving state)
* This method is only public to facilitate debugging. Do not call.
*/
virtual bool isActivelyReceiving();
/** are we actively receiving a packet (only called during receiving state)
* This method is only public to facilitate debugging. Do not call.
*/
virtual bool isActivelyReceiving();
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
virtual bool cancelSending(NodeNum from, PacketId id) override;
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
virtual bool cancelSending(NodeNum from, PacketId id) override;
/** Attempt to find a packet in the TxQueue. Returns true if the packet was found. */
virtual bool findInTxQueue(NodeNum from, PacketId id) override;
/** Attempt to find a packet in the TxQueue. Returns true if the packet was found. */
virtual bool findInTxQueue(NodeNum from, PacketId id) override;
/**
* Start waiting to receive a message
*
* External functions can call this method to wake the device from sleep.
*/
virtual void startReceive(meshtastic_MeshPacket *p);
/**
* Start waiting to receive a message
*
* External functions can call this method to wake the device from sleep.
*/
virtual void startReceive(meshtastic_MeshPacket *p);
meshtastic_QueueStatus getQueueStatus() override;
meshtastic_QueueStatus getQueueStatus() override;
// Convert Compressed_msg to normal msg and receive it
void unpackAndReceive(meshtastic_MeshPacket &p);
// Convert Compressed_msg to normal msg and receive it
void unpackAndReceive(meshtastic_MeshPacket &p);
/**
* Debugging counts
*/
uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0;
uint16_t txDrop = 0;
/**
* Debugging counts
*/
uint32_t rxBad = 0, rxGood = 0, txGood = 0, txRelay = 0;
uint16_t txDrop = 0;
protected:
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
bool isReceiving = true;
protected:
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
bool isReceiving = true;
private:
void setTransmitDelay();
private:
void setTransmitDelay();
/** random timer with certain min. and max. settings */
void startTransmitTimer(bool withDelay = true);
/** random timer with certain min. and max. settings */
void startTransmitTimer(bool withDelay = true);
/** timer scaled to SNR of to be flooded packet */
void startTransmitTimerRebroadcast(meshtastic_MeshPacket *p);
/** timer scaled to SNR of to be flooded packet */
void startTransmitTimerRebroadcast(meshtastic_MeshPacket *p);
void handleTransmitInterrupt();
void handleReceiveInterrupt();
void handleTransmitInterrupt();
void handleReceiveInterrupt();
void onNotify(uint32_t notification);
void onNotify(uint32_t notification);
// start an immediate transmit
virtual void startSend(meshtastic_MeshPacket *txp);
// start an immediate transmit
virtual void startSend(meshtastic_MeshPacket *txp);
// derive packet length
size_t getPacketLength(meshtastic_MeshPacket *p);
// derive packet length
size_t getPacketLength(meshtastic_MeshPacket *p);
int16_t readData(uint8_t *str, size_t len);
int16_t readData(uint8_t *str, size_t len);
meshtastic_MeshPacket *receivingPacket = nullptr; // The packet we are currently receiving
meshtastic_MeshPacket *receivingPacket = nullptr; // The packet we are currently receiving
protected:
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately();
protected:
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately();
/**
* If a send was in progress finish it and return the buffer to the pool */
void completeSending();
/**
* If a send was in progress finish it and return the buffer to the pool */
void completeSending();
virtual uint32_t getPacketTime(uint32_t pl, bool received = false) override;
virtual uint32_t getPacketTime(uint32_t pl, bool received = false) override;
};
extern SimRadio *simRadio;
+121 -107
View File
@@ -23,132 +23,146 @@
// the HAL must inherit from the base RadioLibHal class
// and implement all of its virtual methods
class Ch341Hal : public RadioLibHal {
public:
// default constructor - initializes the base HAL and any needed private members
explicit Ch341Hal(uint8_t spiChannel, std::string serial = "", uint32_t vid = 0x1A86, uint32_t pid = 0x5512, uint32_t spiSpeed = 2000000,
uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING) {
if (serial != "") {
strncpy(pinedio.serial_number, serial.c_str(), 8);
pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1);
}
// LOG_INFO("USB Serial: %s", pinedio.serial_number);
class Ch341Hal : public RadioLibHal
{
public:
// default constructor - initializes the base HAL and any needed private members
explicit Ch341Hal(uint8_t spiChannel, std::string serial = "", uint32_t vid = 0x1A86, uint32_t pid = 0x5512,
uint32_t spiSpeed = 2000000, uint8_t spiDevice = 0, uint8_t gpioDevice = 0)
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING)
{
if (serial != "") {
strncpy(pinedio.serial_number, serial.c_str(), 8);
pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1);
}
// LOG_INFO("USB Serial: %s", pinedio.serial_number);
// There is no vendor with 0x0 -> so check
if (vid != 0x0) {
pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid);
pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid);
}
int32_t ret = pinedio_init(&pinedio, NULL);
if (ret != 0) {
std::string s = "Could not open SPI: ";
throw(s + std::to_string(ret));
// There is no vendor with 0x0 -> so check
if (vid != 0x0) {
pinedio_set_option(&pinedio, PINEDIO_OPTION_VID, vid);
pinedio_set_option(&pinedio, PINEDIO_OPTION_PID, pid);
}
int32_t ret = pinedio_init(&pinedio, NULL);
if (ret != 0) {
std::string s = "Could not open SPI: ";
throw(s + std::to_string(ret));
}
pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0);
pinedio_set_pin_mode(&pinedio, 3, true);
pinedio_set_pin_mode(&pinedio, 5, true);
}
pinedio_set_option(&pinedio, PINEDIO_OPTION_AUTO_CS, 0);
pinedio_set_pin_mode(&pinedio, 3, true);
pinedio_set_pin_mode(&pinedio, 5, true);
}
~Ch341Hal() { pinedio_deinit(&pinedio); }
~Ch341Hal() { pinedio_deinit(&pinedio); }
void getSerialString(char *_serial, size_t len) {
len = len > 8 ? 8 : len;
strncpy(_serial, pinedio.serial_number, len);
}
void getProductString(char *_product_string, size_t len) {
len = len > 95 ? 95 : len;
strncpy(_product_string, pinedio.product_string, len);
}
void init() override {}
void term() override {}
// GPIO-related methods (pinMode, digitalWrite etc.) should check
// RADIOLIB_NC as an alias for non-connected pins
void pinMode(uint32_t pin, uint32_t mode) override {
if (pin == RADIOLIB_NC) {
return;
void getSerialString(char *_serial, size_t len)
{
len = len > 8 ? 8 : len;
strncpy(_serial, pinedio.serial_number, len);
}
pinedio_set_pin_mode(&pinedio, pin, mode);
}
void digitalWrite(uint32_t pin, uint32_t value) override {
if (pin == RADIOLIB_NC) {
return;
void getProductString(char *_product_string, size_t len)
{
len = len > 95 ? 95 : len;
strncpy(_product_string, pinedio.product_string, len);
}
pinedio_digital_write(&pinedio, pin, value);
}
uint32_t digitalRead(uint32_t pin) override {
if (pin == RADIOLIB_NC) {
return 0;
void init() override {}
void term() override {}
// GPIO-related methods (pinMode, digitalWrite etc.) should check
// RADIOLIB_NC as an alias for non-connected pins
void pinMode(uint32_t pin, uint32_t mode) override
{
if (pin == RADIOLIB_NC) {
return;
}
pinedio_set_pin_mode(&pinedio, pin, mode);
}
return pinedio_digital_read(&pinedio, pin);
}
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
if (interruptNum == RADIOLIB_NC) {
return;
void digitalWrite(uint32_t pin, uint32_t value) override
{
if (pin == RADIOLIB_NC) {
return;
}
pinedio_digital_write(&pinedio, pin, value);
}
// LOG_DEBUG("Attach interrupt to pin %d", interruptNum);
pinedio_attach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum, (pinedio_int_mode)mode, interruptCb);
}
void detachInterrupt(uint32_t interruptNum) override {
if (interruptNum == RADIOLIB_NC) {
return;
uint32_t digitalRead(uint32_t pin) override
{
if (pin == RADIOLIB_NC) {
return 0;
}
return pinedio_digital_read(&pinedio, pin);
}
// LOG_DEBUG("Detach interrupt from pin %d", interruptNum);
pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum);
}
void delay(unsigned long ms) override { delayMicroseconds(ms * 1000); }
void delayMicroseconds(unsigned long us) override {
if (us == 0) {
sched_yield();
return;
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override
{
if (interruptNum == RADIOLIB_NC) {
return;
}
// LOG_DEBUG("Attach interrupt to pin %d", interruptNum);
pinedio_attach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum, (pinedio_int_mode)mode, interruptCb);
}
usleep(us);
}
void yield() override { sched_yield(); }
unsigned long millis() override {
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL);
}
unsigned long micros() override {
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
std::cerr << "pulseIn for pin " << pin << "is not supported!" << std::endl;
return 0;
}
void spiBegin() {}
void spiBeginTransaction() {}
void spiTransfer(uint8_t *out, size_t len, uint8_t *in) {
int32_t ret = pinedio_transceive(&this->pinedio, out, in, len);
if (ret < 0) {
std::cerr << "Could not perform SPI transfer: " << ret << std::endl;
void detachInterrupt(uint32_t interruptNum) override
{
if (interruptNum == RADIOLIB_NC) {
return;
}
// LOG_DEBUG("Detach interrupt from pin %d", interruptNum);
pinedio_deattach_interrupt(&this->pinedio, (pinedio_int_pin)interruptNum);
}
}
void spiEndTransaction() {}
void spiEnd() {}
void delay(unsigned long ms) override { delayMicroseconds(ms * 1000); }
private:
pinedio_inst pinedio = {0};
void delayMicroseconds(unsigned long us) override
{
if (us == 0) {
sched_yield();
return;
}
usleep(us);
}
void yield() override { sched_yield(); }
unsigned long millis() override
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000ULL) + (tv.tv_usec / 1000ULL);
}
unsigned long micros() override
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override
{
std::cerr << "pulseIn for pin " << pin << "is not supported!" << std::endl;
return 0;
}
void spiBegin() {}
void spiBeginTransaction() {}
void spiTransfer(uint8_t *out, size_t len, uint8_t *in)
{
int32_t ret = pinedio_transceive(&this->pinedio, out, in, len);
if (ret < 0) {
std::cerr << "Could not perform SPI transfer: " << ret << std::endl;
}
}
void spiEndTransaction() {}
void spiEnd() {}
private:
pinedio_inst pinedio = {0};
};
#endif
@@ -19,11 +19,11 @@ extern "C" {
*
* Ring Oscillator (ROSC) API
*
* A Ring Oscillator is an on-chip oscillator that requires no external crystal. Instead, the output is generated from a
* series of inverters that are chained together to create a feedback loop. RP2040 boots from the ring oscillator
* initially, meaning the first stages of the bootrom, including booting from SPI flash, will be clocked by the ring
* oscillator. If your design has a crystal oscillator, youll likely want to switch to this as your reference clock as
* soon as possible, because the frequency is more accurate than the ring oscillator.
* A Ring Oscillator is an on-chip oscillator that requires no external crystal. Instead, the output is generated from a series of
* inverters that are chained together to create a feedback loop. RP2040 boots from the ring oscillator initially, meaning the
* first stages of the bootrom, including booting from SPI flash, will be clocked by the ring oscillator. If your design has a
* crystal oscillator, youll likely want to switch to this as your reference clock as soon as possible, because the frequency is
* more accurate than the ring oscillator.
*/
/*! \brief Set frequency of the Ring Oscillator
@@ -68,15 +68,22 @@ uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz);
void rosc_set_div(uint32_t div);
inline static void rosc_clear_bad_write(void) { hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS); }
inline static void rosc_clear_bad_write(void)
{
hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS);
}
inline static bool rosc_write_okay(void) { return !(rosc_hw->status & ROSC_STATUS_BADWRITE_BITS); }
inline static bool rosc_write_okay(void)
{
return !(rosc_hw->status & ROSC_STATUS_BADWRITE_BITS);
}
inline static void rosc_write(io_rw_32 *addr, uint32_t value) {
rosc_clear_bad_write();
assert(rosc_write_okay());
*addr = value;
assert(rosc_write_okay());
inline static void rosc_write(io_rw_32 *addr, uint32_t value)
{
rosc_clear_bad_write();
assert(rosc_write_okay());
*addr = value;
assert(rosc_write_okay());
};
#ifdef __cplusplus
+43 -34
View File
@@ -12,50 +12,59 @@
// Given a ROSC delay stage code, return the next-numerically-higher code.
// Top result bit is set when called on maximum ROSC code.
uint32_t next_rosc_code(uint32_t code) { return ((code | 0x08888888u) + 1u) & 0xf7777777u; }
uint32_t next_rosc_code(uint32_t code)
{
return ((code | 0x08888888u) + 1u) & 0xf7777777u;
}
uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz) {
// TODO: This could be a lot better
rosc_set_div(1);
for (uint32_t code = 0; code <= 0x77777777u; code = next_rosc_code(code)) {
rosc_set_freq(code);
uint rosc_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC) / 1000;
if ((rosc_mhz >= low_mhz) && (rosc_mhz <= high_mhz)) {
return rosc_mhz;
uint rosc_find_freq(uint32_t low_mhz, uint32_t high_mhz)
{
// TODO: This could be a lot better
rosc_set_div(1);
for (uint32_t code = 0; code <= 0x77777777u; code = next_rosc_code(code)) {
rosc_set_freq(code);
uint rosc_mhz = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC) / 1000;
if ((rosc_mhz >= low_mhz) && (rosc_mhz <= high_mhz)) {
return rosc_mhz;
}
}
}
return 0;
return 0;
}
void rosc_set_div(uint32_t div) {
assert(div <= 31 && div >= 1);
rosc_write(&rosc_hw->div, ROSC_DIV_VALUE_PASS + div);
void rosc_set_div(uint32_t div)
{
assert(div <= 31 && div >= 1);
rosc_write(&rosc_hw->div, ROSC_DIV_VALUE_PASS + div);
}
void rosc_set_freq(uint32_t code) {
rosc_write(&rosc_hw->freqa, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code & 0xffffu));
rosc_write(&rosc_hw->freqb, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code >> 16u));
void rosc_set_freq(uint32_t code)
{
rosc_write(&rosc_hw->freqa, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code & 0xffffu));
rosc_write(&rosc_hw->freqb, (ROSC_FREQA_PASSWD_VALUE_PASS << ROSC_FREQA_PASSWD_LSB) | (code >> 16u));
}
void rosc_set_range(uint range) {
// Range should use enumvals from the headers and thus have the password correct
rosc_write(&rosc_hw->ctrl, (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range);
void rosc_set_range(uint range)
{
// Range should use enumvals from the headers and thus have the password correct
rosc_write(&rosc_hw->ctrl, (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range);
}
void rosc_disable(void) {
uint32_t tmp = rosc_hw->ctrl;
tmp &= (~ROSC_CTRL_ENABLE_BITS);
tmp |= (ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB);
rosc_write(&rosc_hw->ctrl, tmp);
// Wait for stable to go away
while (rosc_hw->status & ROSC_STATUS_STABLE_BITS)
;
void rosc_disable(void)
{
uint32_t tmp = rosc_hw->ctrl;
tmp &= (~ROSC_CTRL_ENABLE_BITS);
tmp |= (ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB);
rosc_write(&rosc_hw->ctrl, tmp);
// Wait for stable to go away
while (rosc_hw->status & ROSC_STATUS_STABLE_BITS)
;
}
void rosc_set_dormant(void) {
// WARNING: This stops the rosc until woken up by an irq
rosc_write(&rosc_hw->dormant, ROSC_DORMANT_VALUE_DORMANT);
// Wait for it to become stable once woken up
while (!(rosc_hw->status & ROSC_STATUS_STABLE_BITS))
;
void rosc_set_dormant(void)
{
// WARNING: This stops the rosc until woken up by an irq
rosc_write(&rosc_hw->dormant, ROSC_DORMANT_VALUE_DORMANT);
// Wait for it to become stable once woken up
while (!(rosc_hw->status & ROSC_STATUS_STABLE_BITS))
;
}
+113 -98
View File
@@ -10,133 +10,148 @@
static bool awake;
static void sleep_callback(void) { awake = true; }
void epoch_to_datetime(time_t epoch, datetime_t *dt) {
struct tm *tm_info;
tm_info = gmtime(&epoch);
dt->year = tm_info->tm_year;
dt->month = tm_info->tm_mon + 1;
dt->day = tm_info->tm_mday;
dt->dotw = tm_info->tm_wday;
dt->hour = tm_info->tm_hour;
dt->min = tm_info->tm_min;
dt->sec = tm_info->tm_sec;
static void sleep_callback(void)
{
awake = true;
}
void debug_date(datetime_t t) {
LOG_DEBUG("%d %d %d %d %d %d %d", t.year, t.month, t.day, t.hour, t.min, t.sec, t.dotw);
uart_default_tx_wait_blocking();
void epoch_to_datetime(time_t epoch, datetime_t *dt)
{
struct tm *tm_info;
tm_info = gmtime(&epoch);
dt->year = tm_info->tm_year;
dt->month = tm_info->tm_mon + 1;
dt->day = tm_info->tm_mday;
dt->dotw = tm_info->tm_wday;
dt->hour = tm_info->tm_hour;
dt->min = tm_info->tm_min;
dt->sec = tm_info->tm_sec;
}
void cpuDeepSleep(uint32_t msecs) {
void debug_date(datetime_t t)
{
LOG_DEBUG("%d %d %d %d %d %d %d", t.year, t.month, t.day, t.hour, t.min, t.sec, t.dotw);
uart_default_tx_wait_blocking();
}
time_t seconds = (time_t)(msecs / 1000);
datetime_t t_init, t_alarm;
void cpuDeepSleep(uint32_t msecs)
{
awake = false;
// Start the RTC
rtc_init();
epoch_to_datetime(0, &t_init);
rtc_set_datetime(&t_init);
epoch_to_datetime(seconds, &t_alarm);
// debug_date(t_init);
// debug_date(t_alarm);
uart_default_tx_wait_blocking();
sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC);
sleep_goto_sleep_until(&t_alarm, &sleep_callback);
time_t seconds = (time_t)(msecs / 1000);
datetime_t t_init, t_alarm;
// Make sure we don't wake
while (!awake) {
delay(1);
}
awake = false;
// Start the RTC
rtc_init();
epoch_to_datetime(0, &t_init);
rtc_set_datetime(&t_init);
epoch_to_datetime(seconds, &t_alarm);
// debug_date(t_init);
// debug_date(t_alarm);
uart_default_tx_wait_blocking();
sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC);
sleep_goto_sleep_until(&t_alarm, &sleep_callback);
/* For now, I don't know how to revert this state
We just reboot in order to get back operational */
rp2040.reboot();
// Make sure we don't wake
while (!awake) {
delay(1);
}
/* Set RP2040 in dormant mode. Will not wake up. */
// xosc_dormant();
/* For now, I don't know how to revert this state
We just reboot in order to get back operational */
rp2040.reboot();
/* Set RP2040 in dormant mode. Will not wake up. */
// xosc_dormant();
}
#else
void cpuDeepSleep(uint32_t msecs) {
/* Set RP2040 in dormant mode. Will not wake up. */
xosc_dormant();
void cpuDeepSleep(uint32_t msecs)
{
/* Set RP2040 in dormant mode. Will not wake up. */
xosc_dormant();
}
#endif
void setBluetoothEnable(bool enable) {
// not needed
void setBluetoothEnable(bool enable)
{
// not needed
}
void updateBatteryLevel(uint8_t level) {
// 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[7];
dmac[4] = src.id[6];
dmac[3] = src.id[5];
dmac[2] = src.id[4];
dmac[1] = src.id[3];
dmac[0] = src.id[2];
void getMacAddr(uint8_t *dmac)
{
pico_unique_board_id_t src;
pico_get_unique_board_id(&src);
dmac[5] = src.id[7];
dmac[4] = src.id[6];
dmac[3] = src.id[5];
dmac[2] = src.id[4];
dmac[1] = src.id[3];
dmac[0] = src.id[2];
}
void rp2040Setup() {
/* Sets a random seed to make sure we get different random numbers on each boot.
Taken from CPU cycle counter and ROSC oscillator, so should be pretty random.
*/
randomSeed(rp2040.hwrand32());
void rp2040Setup()
{
/* Sets a random seed to make sure we get different random numbers on each boot.
Taken from CPU cycle counter and ROSC oscillator, so should be pretty random.
*/
randomSeed(rp2040.hwrand32());
#ifdef RP2040_SLOW_CLOCK
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);
LOG_INFO("Clock speed:");
LOG_INFO("pll_sys = %dkHz", f_pll_sys);
LOG_INFO("pll_usb = %dkHz", f_pll_usb);
LOG_INFO("rosc = %dkHz", f_rosc);
LOG_INFO("clk_sys = %dkHz", f_clk_sys);
LOG_INFO("clk_peri = %dkHz", f_clk_peri);
LOG_INFO("clk_usb = %dkHz", f_clk_usb);
LOG_INFO("clk_adc = %dkHz", f_clk_adc);
LOG_INFO("clk_rtc = %dkHz", f_clk_rtc);
LOG_INFO("Clock speed:");
LOG_INFO("pll_sys = %dkHz", f_pll_sys);
LOG_INFO("pll_usb = %dkHz", f_pll_usb);
LOG_INFO("rosc = %dkHz", f_rosc);
LOG_INFO("clk_sys = %dkHz", f_clk_sys);
LOG_INFO("clk_peri = %dkHz", f_clk_peri);
LOG_INFO("clk_usb = %dkHz", f_clk_usb);
LOG_INFO("clk_adc = %dkHz", f_clk_adc);
LOG_INFO("clk_rtc = %dkHz", f_clk_rtc);
#endif
}
void enterDfuMode() { reset_usb_boot(0, 0); }
void enterDfuMode()
{
reset_usb_boot(0, 0);
}
/* Init in early boot state. */
#ifdef RP2040_SLOW_CLOCK
void initVariant() {
/* Set the system frequency to 18 MHz. */
set_sys_clock_khz(18 * KHZ, false);
/* The previous line automatically detached clk_peri from clk_sys, and
attached it to pll_usb. We need to attach clk_peri back to system PLL to keep SPI
working at this low speed.
For details see https://github.com/jgromes/RadioLib/discussions/938
*/
clock_configure(clk_peri,
0, // No glitchless mux
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
18 * MHZ, // Input frequency
18 * MHZ // Output (must be same as no divider)
);
/* Run also ADC on lower clk_sys. */
clock_configure(clk_adc, 0, CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, 18 * MHZ, 18 * MHZ);
/* Run RTC from XOSC since USB clock is off */
clock_configure(clk_rtc, 0, CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC, 12 * MHZ, 47 * KHZ);
/* Turn off USB PLL */
pll_deinit(pll_usb);
void initVariant()
{
/* Set the system frequency to 18 MHz. */
set_sys_clock_khz(18 * KHZ, false);
/* The previous line automatically detached clk_peri from clk_sys, and
attached it to pll_usb. We need to attach clk_peri back to system PLL to keep SPI
working at this low speed.
For details see https://github.com/jgromes/RadioLib/discussions/938
*/
clock_configure(clk_peri,
0, // No glitchless mux
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
18 * MHZ, // Input frequency
18 * MHZ // Output (must be same as no divider)
);
/* Run also ADC on lower clk_sys. */
clock_configure(clk_adc, 0, CLOCKS_CLK_ADC_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, 18 * MHZ, 18 * MHZ);
/* Run RTC from XOSC since USB clock is off */
clock_configure(clk_rtc, 0, CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC, 12 * MHZ, 47 * KHZ);
/* Turn off USB PLL */
pll_deinit(pll_usb);
}
#endif
@@ -42,12 +42,18 @@ void sleep_run_from_dormant_source(dormant_source_t dormant_source);
/*! \brief Set the dormant clock source to be the crystal oscillator
* \ingroup hardware_sleep
*/
static inline void sleep_run_from_xosc(void) { sleep_run_from_dormant_source(DORMANT_SOURCE_XOSC); }
static inline void sleep_run_from_xosc(void)
{
sleep_run_from_dormant_source(DORMANT_SOURCE_XOSC);
}
/*! \brief Set the dormant clock source to be the ring oscillator
* \ingroup hardware_sleep
*/
static inline void sleep_run_from_rosc(void) { sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC); }
static inline void sleep_run_from_rosc(void)
{
sleep_run_from_dormant_source(DORMANT_SOURCE_ROSC);
}
/*! \brief Send system to sleep until the specified time
* \ingroup hardware_sleep
@@ -77,7 +83,10 @@ void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high);
*
* \param gpio_pin The pin to provide the wake up
*/
static inline void sleep_goto_dormant_until_edge_high(uint gpio_pin) { sleep_goto_dormant_until_pin(gpio_pin, true, true); }
static inline void sleep_goto_dormant_until_edge_high(uint gpio_pin)
{
sleep_goto_dormant_until_pin(gpio_pin, true, true);
}
/*! \brief Send system to sleep until a high level is detected on GPIO
* \ingroup hardware_sleep
@@ -86,7 +95,10 @@ static inline void sleep_goto_dormant_until_edge_high(uint gpio_pin) { sleep_got
*
* \param gpio_pin The pin to provide the wake up
*/
static inline void sleep_goto_dormant_until_level_high(uint gpio_pin) { sleep_goto_dormant_until_pin(gpio_pin, false, true); }
static inline void sleep_goto_dormant_until_level_high(uint gpio_pin)
{
sleep_goto_dormant_until_pin(gpio_pin, false, true);
}
#ifdef __cplusplus
}
+85 -80
View File
@@ -33,118 +33,123 @@
static dormant_source_t _dormant_source;
bool dormant_source_valid(dormant_source_t dormant_source) {
return (dormant_source == DORMANT_SOURCE_XOSC) || (dormant_source == DORMANT_SOURCE_ROSC);
bool dormant_source_valid(dormant_source_t dormant_source)
{
return (dormant_source == DORMANT_SOURCE_XOSC) || (dormant_source == DORMANT_SOURCE_ROSC);
}
// In order to go into dormant mode we need to be running from a stoppable clock source:
// either the xosc or rosc with no PLLs running. This means we disable the USB and ADC clocks
// and all PLLs
void sleep_run_from_dormant_source(dormant_source_t dormant_source) {
assert(dormant_source_valid(dormant_source));
_dormant_source = dormant_source;
void sleep_run_from_dormant_source(dormant_source_t dormant_source)
{
assert(dormant_source_valid(dormant_source));
_dormant_source = dormant_source;
// FIXME: Just defining average rosc freq here.
uint src_hz = (dormant_source == DORMANT_SOURCE_XOSC) ? XOSC_HZ : 6.5 * MHZ;
uint clk_ref_src =
(dormant_source == DORMANT_SOURCE_XOSC) ? CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC : CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH;
// FIXME: Just defining average rosc freq here.
uint src_hz = (dormant_source == DORMANT_SOURCE_XOSC) ? XOSC_HZ : 6.5 * MHZ;
uint clk_ref_src = (dormant_source == DORMANT_SOURCE_XOSC) ? CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC
: CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH;
// CLK_REF = XOSC or ROSC
clock_configure(clk_ref, clk_ref_src,
0, // No aux mux
src_hz, src_hz);
// CLK_REF = XOSC or ROSC
clock_configure(clk_ref, clk_ref_src,
0, // No aux mux
src_hz, src_hz);
// CLK SYS = CLK_REF
clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
0, // Using glitchless mux
src_hz, src_hz);
// CLK SYS = CLK_REF
clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
0, // Using glitchless mux
src_hz, src_hz);
// CLK USB = 0MHz
clock_stop(clk_usb);
// CLK USB = 0MHz
clock_stop(clk_usb);
// CLK ADC = 0MHz
clock_stop(clk_adc);
// CLK ADC = 0MHz
clock_stop(clk_adc);
// CLK RTC = ideally XOSC (12MHz) / 256 = 46875Hz but could be rosc
uint clk_rtc_src =
(dormant_source == DORMANT_SOURCE_XOSC) ? CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC : CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH;
// CLK RTC = ideally XOSC (12MHz) / 256 = 46875Hz but could be rosc
uint clk_rtc_src = (dormant_source == DORMANT_SOURCE_XOSC) ? CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_XOSC_CLKSRC
: CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH;
clock_configure(clk_rtc,
0, // No GLMUX
clk_rtc_src, src_hz, 46875);
clock_configure(clk_rtc,
0, // No GLMUX
clk_rtc_src, src_hz, 46875);
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, src_hz, src_hz);
// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, src_hz, src_hz);
pll_deinit(pll_sys);
pll_deinit(pll_usb);
pll_deinit(pll_sys);
pll_deinit(pll_usb);
// Assuming both xosc and rosc are running at the moment
if (dormant_source == DORMANT_SOURCE_XOSC) {
// Can disable rosc
rosc_disable();
} else {
// Can disable xosc
xosc_disable();
}
// Assuming both xosc and rosc are running at the moment
if (dormant_source == DORMANT_SOURCE_XOSC) {
// Can disable rosc
rosc_disable();
} else {
// Can disable xosc
xosc_disable();
}
// Reconfigure uart with new clocks
/* This dones not work with our current core */
// setup_default_uart();
// Reconfigure uart with new clocks
/* This dones not work with our current core */
// setup_default_uart();
}
// Go to sleep until woken up by the RTC
void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback) {
// We should have already called the sleep_run_from_dormant_source function
assert(dormant_source_valid(_dormant_source));
void sleep_goto_sleep_until(datetime_t *t, rtc_callback_t callback)
{
// We should have already called the sleep_run_from_dormant_source function
assert(dormant_source_valid(_dormant_source));
// Turn off all clocks when in sleep mode except for RTC
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
clocks_hw->sleep_en1 = 0x0;
// Turn off all clocks when in sleep mode except for RTC
clocks_hw->sleep_en0 = CLOCKS_SLEEP_EN0_CLK_RTC_RTC_BITS;
clocks_hw->sleep_en1 = 0x0;
rtc_set_alarm(t, callback);
rtc_set_alarm(t, callback);
uint save = scb_hw->scr;
// Enable deep sleep at the proc
scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;
uint save = scb_hw->scr;
// Enable deep sleep at the proc
scb_hw->scr = save | M0PLUS_SCR_SLEEPDEEP_BITS;
// Go to sleep
__wfi();
// Go to sleep
__wfi();
}
static void _go_dormant(void) {
assert(dormant_source_valid(_dormant_source));
static void _go_dormant(void)
{
assert(dormant_source_valid(_dormant_source));
if (_dormant_source == DORMANT_SOURCE_XOSC) {
xosc_dormant();
} else {
rosc_set_dormant();
}
if (_dormant_source == DORMANT_SOURCE_XOSC) {
xosc_dormant();
} else {
rosc_set_dormant();
}
}
void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high) {
bool low = !high;
bool level = !edge;
void sleep_goto_dormant_until_pin(uint gpio_pin, bool edge, bool high)
{
bool low = !high;
bool level = !edge;
// Configure the appropriate IRQ at IO bank 0
assert(gpio_pin < NUM_BANK0_GPIOS);
// Configure the appropriate IRQ at IO bank 0
assert(gpio_pin < NUM_BANK0_GPIOS);
uint32_t event = 0;
uint32_t event = 0;
if (level && low)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS;
if (level && high)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS;
if (edge && high)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS;
if (edge && low)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS;
if (level && low)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_LOW_BITS;
if (level && high)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_LEVEL_HIGH_BITS;
if (edge && high)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_HIGH_BITS;
if (edge && low)
event = IO_BANK0_DORMANT_WAKE_INTE0_GPIO0_EDGE_LOW_BITS;
gpio_set_dormant_irq_enabled(gpio_pin, event, true);
gpio_set_dormant_irq_enabled(gpio_pin, event, true);
_go_dormant();
// Execution stops here until woken up
_go_dormant();
// Execution stops here until woken up
// Clear the irq so we can go back to dormant mode again if we want
gpio_acknowledge_irq(gpio_pin, event);
// Clear the irq so we can go back to dormant mode again if we want
gpio_acknowledge_irq(gpio_pin, event);
}
+82 -77
View File
@@ -55,92 +55,96 @@
// LFS Disk IO
//--------------------------------------------------------------------+
static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
LFS_UNUSED(c);
static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
{
LFS_UNUSED(c);
if (!buffer || !size) {
_LFS_DBG("%s Invalid parameter!\r\n", __func__);
return LFS_ERR_INVAL;
}
if (!buffer || !size) {
_LFS_DBG("%s Invalid parameter!\r\n", __func__);
return LFS_ERR_INVAL;
}
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * STM32WL_PAGE_SIZE + off);
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * STM32WL_PAGE_SIZE + off);
memcpy(buffer, (void *)address, size);
memcpy(buffer, (void *)address, size);
return LFS_ERR_OK;
return LFS_ERR_OK;
}
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * STM32WL_PAGE_SIZE + off);
HAL_StatusTypeDef hal_rc = HAL_OK;
uint32_t dw_count = size / 8;
uint64_t *bufp = (uint64_t *)buffer;
static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size)
{
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * STM32WL_PAGE_SIZE + off);
HAL_StatusTypeDef hal_rc = HAL_OK;
uint32_t dw_count = size / 8;
uint64_t *bufp = (uint64_t *)buffer;
LFS_UNUSED(c);
LFS_UNUSED(c);
_LFS_DBG("Programming %d bytes/%d doublewords at address 0x%08x/block %d, offset %d.", size, dw_count, address, block, off);
if (HAL_FLASH_Unlock() != HAL_OK) {
return LFS_ERR_IO;
}
for (uint32_t i = 0; i < dw_count; i++) {
if ((address < LFS_FLASH_ADDR_BASE) || (address > LFS_FLASH_ADDR_END)) {
_LFS_DBG("Wanted to program out of bound of FLASH: 0x%08x.\n", address);
HAL_FLASH_Lock();
return LFS_ERR_INVAL;
_LFS_DBG("Programming %d bytes/%d doublewords at address 0x%08x/block %d, offset %d.", size, dw_count, address, block, off);
if (HAL_FLASH_Unlock() != HAL_OK) {
return LFS_ERR_IO;
}
hal_rc = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, *bufp);
if (hal_rc != HAL_OK) {
/* Error occurred while writing data in Flash memory.
* User can add here some code to deal with this error.
*/
_LFS_DBG("Program error at (0x%08x), 0x%X, error: 0x%08x\n", address, hal_rc, HAL_FLASH_GetError());
for (uint32_t i = 0; i < dw_count; i++) {
if ((address < LFS_FLASH_ADDR_BASE) || (address > LFS_FLASH_ADDR_END)) {
_LFS_DBG("Wanted to program out of bound of FLASH: 0x%08x.\n", address);
HAL_FLASH_Lock();
return LFS_ERR_INVAL;
}
hal_rc = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, *bufp);
if (hal_rc != HAL_OK) {
/* Error occurred while writing data in Flash memory.
* User can add here some code to deal with this error.
*/
_LFS_DBG("Program error at (0x%08x), 0x%X, error: 0x%08x\n", address, hal_rc, HAL_FLASH_GetError());
}
address += 8;
bufp += 1;
}
if (HAL_FLASH_Lock() != HAL_OK) {
return LFS_ERR_IO;
}
address += 8;
bufp += 1;
}
if (HAL_FLASH_Lock() != HAL_OK) {
return LFS_ERR_IO;
}
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; // If HAL_OK, return LFS_ERR_OK, else return LFS_ERR_IO
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; // If HAL_OK, return LFS_ERR_OK, else return LFS_ERR_IO
}
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block) {
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * STM32WL_PAGE_SIZE);
HAL_StatusTypeDef hal_rc;
FLASH_EraseInitTypeDef EraseInitStruct = {.TypeErase = FLASH_TYPEERASE_PAGES, .Page = 0, .NbPages = 1};
uint32_t PAGEError = 0;
static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block)
{
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * STM32WL_PAGE_SIZE);
HAL_StatusTypeDef hal_rc;
FLASH_EraseInitTypeDef EraseInitStruct = {.TypeErase = FLASH_TYPEERASE_PAGES, .Page = 0, .NbPages = 1};
uint32_t PAGEError = 0;
LFS_UNUSED(c);
LFS_UNUSED(c);
if ((address < LFS_FLASH_ADDR_BASE) || (address > LFS_FLASH_ADDR_END)) {
_LFS_DBG("Wanted to erase out of bound of FLASH: 0x%08x.\n", address);
return LFS_ERR_INVAL;
}
/* calculate the absolute page, i.e. what the ST wants */
EraseInitStruct.Page = (address - STM32WL_FLASH_BASE) / STM32WL_PAGE_SIZE;
_LFS_DBG("Erasing block %d at 0x%08x... ", block, address);
HAL_FLASH_Unlock();
hal_rc = HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
if ((address < LFS_FLASH_ADDR_BASE) || (address > LFS_FLASH_ADDR_END)) {
_LFS_DBG("Wanted to erase out of bound of FLASH: 0x%08x.\n", address);
return LFS_ERR_INVAL;
}
/* calculate the absolute page, i.e. what the ST wants */
EraseInitStruct.Page = (address - STM32WL_FLASH_BASE) / STM32WL_PAGE_SIZE;
_LFS_DBG("Erasing block %d at 0x%08x... ", block, address);
HAL_FLASH_Unlock();
hal_rc = HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
HAL_FLASH_Lock();
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; // If HAL_OK, return LFS_ERR_OK, else return LFS_ERR_IO
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; // If HAL_OK, return LFS_ERR_OK, else return LFS_ERR_IO
}
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
static int _internal_flash_sync(const struct lfs_config *c) {
LFS_UNUSED(c);
// write function performs no caching. No need for sync.
static int _internal_flash_sync(const struct lfs_config *c)
{
LFS_UNUSED(c);
// write function performs no caching. No need for sync.
return LFS_ERR_OK;
return LFS_ERR_OK;
}
static struct lfs_config _InternalFSConfig = {.context = NULL,
@@ -169,25 +173,26 @@ LittleFS InternalFS;
LittleFS::LittleFS(void) : STM32_LittleFS(&_InternalFSConfig) {}
bool LittleFS::begin(void) {
if (FLASH_BASE >= LFS_FLASH_ADDR_BASE) {
/* There is not enough space on this device for a filesystem. */
return false;
}
// failed to mount, erase all pages then format and mount again
if (!STM32_LittleFS::begin()) {
// Erase all pages of internal flash region for Filesystem.
for (uint32_t addr = LFS_FLASH_ADDR_BASE; addr < (LFS_FLASH_ADDR_END + 1); addr += STM32WL_PAGE_SIZE) {
_internal_flash_erase(&_InternalFSConfig, (addr - LFS_FLASH_ADDR_BASE) / STM32WL_PAGE_SIZE);
bool LittleFS::begin(void)
{
if (FLASH_BASE >= LFS_FLASH_ADDR_BASE) {
/* There is not enough space on this device for a filesystem. */
return false;
}
// failed to mount, erase all pages then format and mount again
if (!STM32_LittleFS::begin()) {
// Erase all pages of internal flash region for Filesystem.
for (uint32_t addr = LFS_FLASH_ADDR_BASE; addr < (LFS_FLASH_ADDR_END + 1); addr += STM32WL_PAGE_SIZE) {
_internal_flash_erase(&_InternalFSConfig, (addr - LFS_FLASH_ADDR_BASE) / STM32WL_PAGE_SIZE);
}
// lfs format
this->format();
// mount again if still failed, give up
if (!STM32_LittleFS::begin())
return false;
}
// lfs format
this->format();
// mount again if still failed, give up
if (!STM32_LittleFS::begin())
return false;
}
return true;
return true;
}
+6 -5
View File
@@ -27,12 +27,13 @@
#include "STM32_LittleFS.h"
class LittleFS : public STM32_LittleFS {
public:
LittleFS(void);
class LittleFS : public STM32_LittleFS
{
public:
LittleFS(void);
// overwrite to also perform low level format (sector erase of whole flash region)
bool begin(void);
// overwrite to also perform low level format (sector erase of whole flash region)
bool begin(void);
};
extern LittleFS InternalFS;
+178 -166
View File
@@ -37,10 +37,11 @@ using namespace STM32_LittleFS_Namespace;
STM32_LittleFS::STM32_LittleFS(void) : STM32_LittleFS(NULL) {}
STM32_LittleFS::STM32_LittleFS(struct lfs_config *cfg) {
varclr(&_lfs);
_lfs_cfg = cfg;
_mounted = false;
STM32_LittleFS::STM32_LittleFS(struct lfs_config *cfg)
{
varclr(&_lfs);
_lfs_cfg = cfg;
_mounted = false;
}
STM32_LittleFS::~STM32_LittleFS() {}
@@ -48,224 +49,235 @@ STM32_LittleFS::~STM32_LittleFS() {}
// Initialize and mount the file system
// Return true if mounted successfully else probably corrupted.
// User should format the disk and try again
bool STM32_LittleFS::begin(struct lfs_config *cfg) {
_lockFS();
bool STM32_LittleFS::begin(struct lfs_config *cfg)
{
_lockFS();
bool ret;
// not a loop, just an quick way to short-circuit on error
do {
if (_mounted) {
ret = true;
break;
}
if (cfg) {
_lfs_cfg = cfg;
}
if (nullptr == _lfs_cfg) {
ret = false;
break;
}
// actually attempt to mount, and log error if one occurs
int err = lfs_mount(&_lfs, _lfs_cfg);
PRINT_LFS_ERR(err);
_mounted = (err == LFS_ERR_OK);
ret = _mounted;
} while (0);
bool ret;
// not a loop, just an quick way to short-circuit on error
do {
if (_mounted) {
ret = true;
break;
}
if (cfg) {
_lfs_cfg = cfg;
}
if (nullptr == _lfs_cfg) {
ret = false;
break;
}
// actually attempt to mount, and log error if one occurs
int err = lfs_mount(&_lfs, _lfs_cfg);
PRINT_LFS_ERR(err);
_mounted = (err == LFS_ERR_OK);
ret = _mounted;
} while (0);
_unlockFS();
return ret;
_unlockFS();
return ret;
}
// Tear down and unmount file system
void STM32_LittleFS::end(void) {
_lockFS();
void STM32_LittleFS::end(void)
{
_lockFS();
if (_mounted) {
_mounted = false;
int err = lfs_unmount(&_lfs);
PRINT_LFS_ERR(err);
(void)err;
}
if (_mounted) {
_mounted = false;
int err = lfs_unmount(&_lfs);
PRINT_LFS_ERR(err);
(void)err;
}
_unlockFS();
_unlockFS();
}
bool STM32_LittleFS::format(void) {
_lockFS();
bool STM32_LittleFS::format(void)
{
_lockFS();
int err = LFS_ERR_OK;
bool attemptMount = _mounted;
// not a loop, just an quick way to short-circuit on error
do {
// if already mounted: umount first -> format -> remount
if (_mounted) {
_mounted = false;
err = lfs_unmount(&_lfs);
if (LFS_ERR_OK != err) {
PRINT_LFS_ERR(err);
break;
}
}
err = lfs_format(&_lfs, _lfs_cfg);
if (LFS_ERR_OK != err) {
PRINT_LFS_ERR(err);
break;
}
int err = LFS_ERR_OK;
bool attemptMount = _mounted;
// not a loop, just an quick way to short-circuit on error
do {
// if already mounted: umount first -> format -> remount
if (_mounted) {
_mounted = false;
err = lfs_unmount(&_lfs);
if (LFS_ERR_OK != err) {
PRINT_LFS_ERR(err);
break;
}
}
err = lfs_format(&_lfs, _lfs_cfg);
if (LFS_ERR_OK != err) {
PRINT_LFS_ERR(err);
break;
}
if (attemptMount) {
err = lfs_mount(&_lfs, _lfs_cfg);
if (LFS_ERR_OK != err) {
PRINT_LFS_ERR(err);
break;
}
_mounted = true;
}
// success!
} while (0);
if (attemptMount) {
err = lfs_mount(&_lfs, _lfs_cfg);
if (LFS_ERR_OK != err) {
PRINT_LFS_ERR(err);
break;
}
_mounted = true;
}
// success!
} while (0);
_unlockFS();
return LFS_ERR_OK == err;
_unlockFS();
return LFS_ERR_OK == err;
}
// Open a file or folder
STM32_LittleFS_Namespace::File STM32_LittleFS::open(char const *filepath, uint8_t mode) {
// No lock is required here ... the File() object will synchronize with the mutex provided
return STM32_LittleFS_Namespace::File(filepath, mode, *this);
STM32_LittleFS_Namespace::File STM32_LittleFS::open(char const *filepath, uint8_t mode)
{
// No lock is required here ... the File() object will synchronize with the mutex provided
return STM32_LittleFS_Namespace::File(filepath, mode, *this);
}
// Check if file or folder exists
bool STM32_LittleFS::exists(char const *filepath) {
struct lfs_info info;
_lockFS();
bool STM32_LittleFS::exists(char const *filepath)
{
struct lfs_info info;
_lockFS();
bool ret = (0 == lfs_stat(&_lfs, filepath, &info));
bool ret = (0 == lfs_stat(&_lfs, filepath, &info));
_unlockFS();
return ret;
_unlockFS();
return ret;
}
// Create a directory, create intermediate parent if needed
bool STM32_LittleFS::mkdir(char const *filepath) {
bool ret = true;
const char *slash = filepath;
if (slash[0] == '/')
slash++; // skip root '/'
bool STM32_LittleFS::mkdir(char const *filepath)
{
bool ret = true;
const char *slash = filepath;
if (slash[0] == '/')
slash++; // skip root '/'
_lockFS();
_lockFS();
// make intermediate parent directory(ies)
while (NULL != (slash = strchr(slash, '/'))) {
char parent[slash - filepath + 1] = {0};
memcpy(parent, filepath, slash - filepath);
// make intermediate parent directory(ies)
while (NULL != (slash = strchr(slash, '/'))) {
char parent[slash - filepath + 1] = {0};
memcpy(parent, filepath, slash - filepath);
int rc = lfs_mkdir(&_lfs, parent);
if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) {
PRINT_LFS_ERR(rc);
ret = false;
break;
int rc = lfs_mkdir(&_lfs, parent);
if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) {
PRINT_LFS_ERR(rc);
ret = false;
break;
}
slash++;
}
slash++;
}
// make the final requested directory
if (ret) {
int rc = lfs_mkdir(&_lfs, filepath);
if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) {
PRINT_LFS_ERR(rc);
ret = false;
// make the final requested directory
if (ret) {
int rc = lfs_mkdir(&_lfs, filepath);
if (rc != LFS_ERR_OK && rc != LFS_ERR_EXIST) {
PRINT_LFS_ERR(rc);
ret = false;
}
}
}
_unlockFS();
return ret;
_unlockFS();
return ret;
}
// Remove a file
bool STM32_LittleFS::remove(char const *filepath) {
_lockFS();
bool STM32_LittleFS::remove(char const *filepath)
{
_lockFS();
int err = lfs_remove(&_lfs, filepath);
PRINT_LFS_ERR(err);
int err = lfs_remove(&_lfs, filepath);
PRINT_LFS_ERR(err);
_unlockFS();
return LFS_ERR_OK == err;
_unlockFS();
return LFS_ERR_OK == err;
}
// Rename a file
bool STM32_LittleFS::rename(char const *oldfilepath, char const *newfilepath) {
_lockFS();
bool STM32_LittleFS::rename(char const *oldfilepath, char const *newfilepath)
{
_lockFS();
int err = lfs_rename(&_lfs, oldfilepath, newfilepath);
PRINT_LFS_ERR(err);
int err = lfs_rename(&_lfs, oldfilepath, newfilepath);
PRINT_LFS_ERR(err);
_unlockFS();
return LFS_ERR_OK == err;
_unlockFS();
return LFS_ERR_OK == err;
}
// Remove a folder
bool STM32_LittleFS::rmdir(char const *filepath) {
_lockFS();
bool STM32_LittleFS::rmdir(char const *filepath)
{
_lockFS();
int err = lfs_remove(&_lfs, filepath);
PRINT_LFS_ERR(err);
int err = lfs_remove(&_lfs, filepath);
PRINT_LFS_ERR(err);
_unlockFS();
return LFS_ERR_OK == err;
_unlockFS();
return LFS_ERR_OK == err;
}
// Remove a folder recursively
bool STM32_LittleFS::rmdir_r(char const *filepath) {
/* lfs is modified to remove non-empty folder,
According to below issue, comment these 2 line won't corrupt filesystem
at least when using LFS v1. If moving to LFS v2, see tracked issue
to see if issues (such as the orphans in threaded linked list) are resolved.
https://github.com/ARMmbed/littlefs/issues/43
*/
_lockFS();
bool STM32_LittleFS::rmdir_r(char const *filepath)
{
/* lfs is modified to remove non-empty folder,
According to below issue, comment these 2 line won't corrupt filesystem
at least when using LFS v1. If moving to LFS v2, see tracked issue
to see if issues (such as the orphans in threaded linked list) are resolved.
https://github.com/ARMmbed/littlefs/issues/43
*/
_lockFS();
int err = lfs_remove(&_lfs, filepath);
PRINT_LFS_ERR(err);
int err = lfs_remove(&_lfs, filepath);
PRINT_LFS_ERR(err);
_unlockFS();
return LFS_ERR_OK == err;
_unlockFS();
return LFS_ERR_OK == err;
}
//------------- Debug -------------//
#if CFG_DEBUG
const char *dbg_strerr_lfs(int32_t err) {
switch (err) {
case LFS_ERR_OK:
return "LFS_ERR_OK";
case LFS_ERR_IO:
return "LFS_ERR_IO";
case LFS_ERR_CORRUPT:
return "LFS_ERR_CORRUPT";
case LFS_ERR_NOENT:
return "LFS_ERR_NOENT";
case LFS_ERR_EXIST:
return "LFS_ERR_EXIST";
case LFS_ERR_NOTDIR:
return "LFS_ERR_NOTDIR";
case LFS_ERR_ISDIR:
return "LFS_ERR_ISDIR";
case LFS_ERR_NOTEMPTY:
return "LFS_ERR_NOTEMPTY";
case LFS_ERR_BADF:
return "LFS_ERR_BADF";
case LFS_ERR_INVAL:
return "LFS_ERR_INVAL";
case LFS_ERR_NOSPC:
return "LFS_ERR_NOSPC";
case LFS_ERR_NOMEM:
return "LFS_ERR_NOMEM";
const char *dbg_strerr_lfs(int32_t err)
{
switch (err) {
case LFS_ERR_OK:
return "LFS_ERR_OK";
case LFS_ERR_IO:
return "LFS_ERR_IO";
case LFS_ERR_CORRUPT:
return "LFS_ERR_CORRUPT";
case LFS_ERR_NOENT:
return "LFS_ERR_NOENT";
case LFS_ERR_EXIST:
return "LFS_ERR_EXIST";
case LFS_ERR_NOTDIR:
return "LFS_ERR_NOTDIR";
case LFS_ERR_ISDIR:
return "LFS_ERR_ISDIR";
case LFS_ERR_NOTEMPTY:
return "LFS_ERR_NOTEMPTY";
case LFS_ERR_BADF:
return "LFS_ERR_BADF";
case LFS_ERR_INVAL:
return "LFS_ERR_INVAL";
case LFS_ERR_NOSPC:
return "LFS_ERR_NOSPC";
case LFS_ERR_NOMEM:
return "LFS_ERR_NOMEM";
default:
static char errcode[10];
sprintf(errcode, "%ld", err);
return errcode;
}
default:
static char errcode[10];
sprintf(errcode, "%ld", err);
return errcode;
}
return NULL;
return NULL;
}
#endif
+49 -46
View File
@@ -33,57 +33,60 @@
#include "STM32_LittleFS_File.h"
#include "littlefs/lfs.h"
class STM32_LittleFS {
public:
STM32_LittleFS(void);
explicit STM32_LittleFS(struct lfs_config *cfg);
virtual ~STM32_LittleFS();
class STM32_LittleFS
{
public:
STM32_LittleFS(void);
explicit STM32_LittleFS(struct lfs_config *cfg);
virtual ~STM32_LittleFS();
bool begin(struct lfs_config *cfg = NULL);
void end(void);
bool begin(struct lfs_config *cfg = NULL);
void end(void);
// Open the specified file/directory with the supplied mode (e.g. read or
// write, etc). Returns a File object for interacting with the file.
// Note that currently only one file can be open at a time.
STM32_LittleFS_Namespace::File open(char const *filename, uint8_t mode = STM32_LittleFS_Namespace::FILE_O_READ);
// Open the specified file/directory with the supplied mode (e.g. read or
// write, etc). Returns a File object for interacting with the file.
// Note that currently only one file can be open at a time.
STM32_LittleFS_Namespace::File open(char const *filename, uint8_t mode = STM32_LittleFS_Namespace::FILE_O_READ);
// Methods to determine if the requested file path exists.
bool exists(char const *filepath);
// Methods to determine if the requested file path exists.
bool exists(char const *filepath);
// Create the requested directory hierarchy--if intermediate directories
// do not exist they will be created.
bool mkdir(char const *filepath);
// Create the requested directory hierarchy--if intermediate directories
// do not exist they will be created.
bool mkdir(char const *filepath);
// Delete the file.
bool remove(char const *filepath);
// Delete the file.
bool remove(char const *filepath);
// Rename the file.
bool rename(char const *oldfilepath, char const *newfilepath);
// Rename the file.
bool rename(char const *oldfilepath, char const *newfilepath);
// Delete a folder (must be empty)
bool rmdir(char const *filepath);
// Delete a folder (must be empty)
bool rmdir(char const *filepath);
// Delete a folder (recursively)
bool rmdir_r(char const *filepath);
// Delete a folder (recursively)
bool rmdir_r(char const *filepath);
// format file system
bool format(void);
// format file system
bool format(void);
/*------------------------------------------------------------------*/
/* INTERNAL USAGE ONLY
* Although declare as public, it is meant to be invoked by internal
* code. User should not call these directly
*------------------------------------------------------------------*/
lfs_t *_getFS(void) { return &_lfs; }
void _lockFS(void) { /* no-op */
}
void _unlockFS(void) { /* no-op */
}
/*------------------------------------------------------------------*/
/* INTERNAL USAGE ONLY
* Although declare as public, it is meant to be invoked by internal
* code. User should not call these directly
*------------------------------------------------------------------*/
lfs_t *_getFS(void) { return &_lfs; }
void _lockFS(void)
{ /* no-op */
}
void _unlockFS(void)
{ /* no-op */
}
protected:
bool _mounted;
struct lfs_config *_lfs_cfg;
lfs_t _lfs;
protected:
bool _mounted;
struct lfs_config *_lfs_cfg;
lfs_t _lfs;
};
#if !CFG_DEBUG
@@ -91,12 +94,12 @@ protected:
#define PRINT_LFS_ERR(_err)
#else
#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, dbg_strerr_lfs)
#define PRINT_LFS_ERR(_err) \
do { \
if (_err) { \
printf("%s:%d, LFS error: %d\n", __FILE__, __LINE__, _err); \
} \
} while (0) // LFS_ERR are of type int, VERIFY_MESS expects long_int
#define PRINT_LFS_ERR(_err) \
do { \
if (_err) { \
printf("%s:%d, LFS error: %d\n", __FILE__, __LINE__, _err); \
} \
} while (0) // LFS_ERR are of type int, VERIFY_MESS expects long_int
const char *dbg_strerr_lfs(int32_t err);
#endif
+314 -278
View File
@@ -34,324 +34,360 @@
using namespace STM32_LittleFS_Namespace;
File::File(STM32_LittleFS &fs) {
_fs = &fs;
_is_dir = false;
_name[0] = 0;
_name[LFS_NAME_MAX] = 0;
_dir_path = NULL;
File::File(STM32_LittleFS &fs)
{
_fs = &fs;
_is_dir = false;
_name[0] = 0;
_name[LFS_NAME_MAX] = 0;
_dir_path = NULL;
_dir = NULL;
_file = NULL;
_dir = NULL;
_file = NULL;
}
File::File(char const *filename, uint8_t mode, STM32_LittleFS &fs) : File(fs) {
// public constructor calls public API open(), which will obtain the mutex
this->open(filename, mode);
File::File(char const *filename, uint8_t mode, STM32_LittleFS &fs) : File(fs)
{
// public constructor calls public API open(), which will obtain the mutex
this->open(filename, mode);
}
bool File::_open_file(char const *filepath, uint8_t mode) {
int flags = (mode == FILE_O_READ) ? LFS_O_RDONLY : (mode == FILE_O_WRITE) ? (LFS_O_RDWR | LFS_O_CREAT) : 0;
bool File::_open_file(char const *filepath, uint8_t mode)
{
int flags = (mode == FILE_O_READ) ? LFS_O_RDONLY : (mode == FILE_O_WRITE) ? (LFS_O_RDWR | LFS_O_CREAT) : 0;
if (flags) {
_file = (lfs_file_t *)rtos_malloc(sizeof(lfs_file_t));
if (!_file)
return false;
if (flags) {
_file = (lfs_file_t *)rtos_malloc(sizeof(lfs_file_t));
if (!_file)
return false;
int rc = lfs_file_open(_fs->_getFS(), _file, filepath, flags);
int rc = lfs_file_open(_fs->_getFS(), _file, filepath, flags);
if (rc) {
// failed to open
PRINT_LFS_ERR(rc);
// free memory
rtos_free(_file);
_file = NULL;
return false;
}
// move to end of file
if (mode == FILE_O_WRITE)
lfs_file_seek(_fs->_getFS(), _file, 0, LFS_SEEK_END);
_is_dir = false;
}
return true;
}
bool File::_open_dir(char const *filepath)
{
_dir = (lfs_dir_t *)rtos_malloc(sizeof(lfs_dir_t));
if (!_dir)
return false;
int rc = lfs_dir_open(_fs->_getFS(), _dir, filepath);
if (rc) {
// failed to open
PRINT_LFS_ERR(rc);
// free memory
rtos_free(_file);
_file = NULL;
return false;
// failed to open
PRINT_LFS_ERR(rc);
// free memory
rtos_free(_dir);
_dir = NULL;
return false;
}
// move to end of file
if (mode == FILE_O_WRITE)
lfs_file_seek(_fs->_getFS(), _file, 0, LFS_SEEK_END);
_is_dir = true;
_is_dir = false;
}
_dir_path = (char *)rtos_malloc(strlen(filepath) + 1);
strcpy(_dir_path, filepath);
return true;
return true;
}
bool File::_open_dir(char const *filepath) {
_dir = (lfs_dir_t *)rtos_malloc(sizeof(lfs_dir_t));
if (!_dir)
return false;
bool File::open(char const *filepath, uint8_t mode)
{
bool ret = false;
_fs->_lockFS();
int rc = lfs_dir_open(_fs->_getFS(), _dir, filepath);
ret = this->_open(filepath, mode);
if (rc) {
// failed to open
PRINT_LFS_ERR(rc);
// free memory
rtos_free(_dir);
_dir = NULL;
return false;
}
_is_dir = true;
_dir_path = (char *)rtos_malloc(strlen(filepath) + 1);
strcpy(_dir_path, filepath);
return true;
_fs->_unlockFS();
return ret;
}
bool File::open(char const *filepath, uint8_t mode) {
bool ret = false;
_fs->_lockFS();
bool File::_open(char const *filepath, uint8_t mode)
{
bool ret = false;
ret = this->_open(filepath, mode);
// close if currently opened
if (this->isOpen())
_close();
_fs->_unlockFS();
return ret;
}
struct lfs_info info;
int rc = lfs_stat(_fs->_getFS(), filepath, &info);
bool File::_open(char const *filepath, uint8_t mode) {
bool ret = false;
// close if currently opened
if (this->isOpen())
_close();
struct lfs_info info;
int rc = lfs_stat(_fs->_getFS(), filepath, &info);
if (LFS_ERR_OK == rc) {
// file existed, open file or directory accordingly
ret = (info.type == LFS_TYPE_REG) ? _open_file(filepath, mode) : _open_dir(filepath);
} else if (LFS_ERR_NOENT == rc) {
// file not existed, only proceed with FILE_O_WRITE mode
if (mode == FILE_O_WRITE)
ret = _open_file(filepath, mode);
} else {
PRINT_LFS_ERR(rc);
}
// save bare file name
if (ret) {
char const *splash = strrchr(filepath, '/');
strncpy(_name, splash ? (splash + 1) : filepath, LFS_NAME_MAX);
}
return ret;
}
size_t File::write(uint8_t ch) { return write(&ch, 1); }
size_t File::write(uint8_t const *buf, size_t size) {
lfs_ssize_t wrcount = 0;
_fs->_lockFS();
if (!this->_is_dir) {
wrcount = lfs_file_write(_fs->_getFS(), _file, buf, size);
if (wrcount < 0) {
wrcount = 0;
}
}
_fs->_unlockFS();
return wrcount;
}
int File::read(void) {
// this thin wrapper relies on called function to synchronize
int ret = -1;
uint8_t ch;
if (read(&ch, 1) > 0) {
ret = static_cast<int>(ch);
}
return ret;
}
int File::read(void *buf, uint16_t nbyte) {
int ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_read(_fs->_getFS(), _file, buf, nbyte);
}
_fs->_unlockFS();
return ret;
}
int File::peek(void) {
int ret = -1;
_fs->_lockFS();
if (!this->_is_dir) {
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
uint8_t ch = 0;
if (lfs_file_read(_fs->_getFS(), _file, &ch, 1) > 0) {
ret = static_cast<int>(ch);
}
(void)lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET);
}
_fs->_unlockFS();
return ret;
}
int File::available(void) {
int ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
uint32_t file_size = lfs_file_size(_fs->_getFS(), _file);
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
ret = file_size - pos;
}
_fs->_unlockFS();
return ret;
}
bool File::seek(uint32_t pos) {
bool ret = false;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET) >= 0;
}
_fs->_unlockFS();
return ret;
}
uint32_t File::position(void) {
uint32_t ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_tell(_fs->_getFS(), _file);
}
_fs->_unlockFS();
return ret;
}
uint32_t File::size(void) {
uint32_t ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_size(_fs->_getFS(), _file);
}
_fs->_unlockFS();
return ret;
}
bool File::truncate(uint32_t pos) {
int32_t ret = LFS_ERR_ISDIR;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_truncate(_fs->_getFS(), _file, pos);
}
_fs->_unlockFS();
return (ret == 0);
}
bool File::truncate(void) {
int32_t ret = LFS_ERR_ISDIR;
_fs->_lockFS();
if (!this->_is_dir) {
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
ret = lfs_file_truncate(_fs->_getFS(), _file, pos);
}
_fs->_unlockFS();
return (ret == 0);
}
void File::flush(void) {
_fs->_lockFS();
if (!this->_is_dir) {
lfs_file_sync(_fs->_getFS(), _file);
}
_fs->_unlockFS();
return;
}
void File::close(void) {
_fs->_lockFS();
this->_close();
_fs->_unlockFS();
}
void File::_close(void) {
if (this->isOpen()) {
if (this->_is_dir) {
lfs_dir_close(_fs->_getFS(), _dir);
rtos_free(_dir);
_dir = NULL;
if (this->_dir_path)
rtos_free(_dir_path);
_dir_path = NULL;
if (LFS_ERR_OK == rc) {
// file existed, open file or directory accordingly
ret = (info.type == LFS_TYPE_REG) ? _open_file(filepath, mode) : _open_dir(filepath);
} else if (LFS_ERR_NOENT == rc) {
// file not existed, only proceed with FILE_O_WRITE mode
if (mode == FILE_O_WRITE)
ret = _open_file(filepath, mode);
} else {
lfs_file_close(this->_fs->_getFS(), _file);
rtos_free(_file);
_file = NULL;
PRINT_LFS_ERR(rc);
}
}
// save bare file name
if (ret) {
char const *splash = strrchr(filepath, '/');
strncpy(_name, splash ? (splash + 1) : filepath, LFS_NAME_MAX);
}
return ret;
}
File::operator bool(void) { return isOpen(); }
size_t File::write(uint8_t ch)
{
return write(&ch, 1);
}
bool File::isOpen(void) { return (_file != NULL) || (_dir != NULL); }
size_t File::write(uint8_t const *buf, size_t size)
{
lfs_ssize_t wrcount = 0;
_fs->_lockFS();
if (!this->_is_dir) {
wrcount = lfs_file_write(_fs->_getFS(), _file, buf, size);
if (wrcount < 0) {
wrcount = 0;
}
}
_fs->_unlockFS();
return wrcount;
}
int File::read(void)
{
// this thin wrapper relies on called function to synchronize
int ret = -1;
uint8_t ch;
if (read(&ch, 1) > 0) {
ret = static_cast<int>(ch);
}
return ret;
}
int File::read(void *buf, uint16_t nbyte)
{
int ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_read(_fs->_getFS(), _file, buf, nbyte);
}
_fs->_unlockFS();
return ret;
}
int File::peek(void)
{
int ret = -1;
_fs->_lockFS();
if (!this->_is_dir) {
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
uint8_t ch = 0;
if (lfs_file_read(_fs->_getFS(), _file, &ch, 1) > 0) {
ret = static_cast<int>(ch);
}
(void)lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET);
}
_fs->_unlockFS();
return ret;
}
int File::available(void)
{
int ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
uint32_t file_size = lfs_file_size(_fs->_getFS(), _file);
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
ret = file_size - pos;
}
_fs->_unlockFS();
return ret;
}
bool File::seek(uint32_t pos)
{
bool ret = false;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET) >= 0;
}
_fs->_unlockFS();
return ret;
}
uint32_t File::position(void)
{
uint32_t ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_tell(_fs->_getFS(), _file);
}
_fs->_unlockFS();
return ret;
}
uint32_t File::size(void)
{
uint32_t ret = 0;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_size(_fs->_getFS(), _file);
}
_fs->_unlockFS();
return ret;
}
bool File::truncate(uint32_t pos)
{
int32_t ret = LFS_ERR_ISDIR;
_fs->_lockFS();
if (!this->_is_dir) {
ret = lfs_file_truncate(_fs->_getFS(), _file, pos);
}
_fs->_unlockFS();
return (ret == 0);
}
bool File::truncate(void)
{
int32_t ret = LFS_ERR_ISDIR;
_fs->_lockFS();
if (!this->_is_dir) {
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file);
ret = lfs_file_truncate(_fs->_getFS(), _file, pos);
}
_fs->_unlockFS();
return (ret == 0);
}
void File::flush(void)
{
_fs->_lockFS();
if (!this->_is_dir) {
lfs_file_sync(_fs->_getFS(), _file);
}
_fs->_unlockFS();
return;
}
void File::close(void)
{
_fs->_lockFS();
this->_close();
_fs->_unlockFS();
}
void File::_close(void)
{
if (this->isOpen()) {
if (this->_is_dir) {
lfs_dir_close(_fs->_getFS(), _dir);
rtos_free(_dir);
_dir = NULL;
if (this->_dir_path)
rtos_free(_dir_path);
_dir_path = NULL;
} else {
lfs_file_close(this->_fs->_getFS(), _file);
rtos_free(_file);
_file = NULL;
}
}
}
File::operator bool(void)
{
return isOpen();
}
bool File::isOpen(void)
{
return (_file != NULL) || (_dir != NULL);
}
// WARNING -- although marked as `const`, the values pointed
// to may change. For example, if the same File
// object has `open()` called with a different
// file or directory name, this same pointer will
// suddenly (unexpectedly?) have different values.
char const *File::name(void) { return this->_name; }
char const *File::name(void)
{
return this->_name;
}
bool File::isDirectory(void) { return this->_is_dir; }
bool File::isDirectory(void)
{
return this->_is_dir;
}
File File::openNextFile(uint8_t mode) {
_fs->_lockFS();
File File::openNextFile(uint8_t mode)
{
_fs->_lockFS();
File ret(*_fs);
if (this->_is_dir) {
struct lfs_info info;
int rc;
File ret(*_fs);
if (this->_is_dir) {
struct lfs_info info;
int rc;
// lfs_dir_read returns 0 when reaching end of directory, 1 if found an entry
// Skip the "." and ".." entries ...
do {
rc = lfs_dir_read(_fs->_getFS(), _dir, &info);
} while (rc == 1 && (!strcmp(".", info.name) || !strcmp("..", info.name)));
// lfs_dir_read returns 0 when reaching end of directory, 1 if found an entry
// Skip the "." and ".." entries ...
do {
rc = lfs_dir_read(_fs->_getFS(), _dir, &info);
} while (rc == 1 && (!strcmp(".", info.name) || !strcmp("..", info.name)));
if (rc == 1) {
// string cat name with current folder
char filepath[strlen(_dir_path) + 1 + strlen(info.name) + 1]; // potential for significant stack usage
strcpy(filepath, _dir_path);
if (!(_dir_path[0] == '/' && _dir_path[1] == 0))
strcat(filepath, "/"); // only add '/' if cwd is not root
strcat(filepath, info.name);
if (rc == 1) {
// string cat name with current folder
char filepath[strlen(_dir_path) + 1 + strlen(info.name) + 1]; // potential for significant stack usage
strcpy(filepath, _dir_path);
if (!(_dir_path[0] == '/' && _dir_path[1] == 0))
strcat(filepath, "/"); // only add '/' if cwd is not root
strcat(filepath, info.name);
(void)ret._open(filepath, mode); // return value is ignored ... caller is expected to check isOpened()
} else if (rc < 0) {
PRINT_LFS_ERR(rc);
(void)ret._open(filepath, mode); // return value is ignored ... caller is expected to check isOpened()
} else if (rc < 0) {
PRINT_LFS_ERR(rc);
}
}
}
_fs->_unlockFS();
return ret;
_fs->_unlockFS();
return ret;
}
void File::rewindDirectory(void) {
_fs->_lockFS();
if (this->_is_dir) {
lfs_dir_rewind(_fs->_getFS(), _dir);
}
_fs->_unlockFS();
void File::rewindDirectory(void)
{
_fs->_lockFS();
if (this->_is_dir) {
lfs_dir_rewind(_fs->_getFS(), _dir);
}
_fs->_unlockFS();
}
+51 -48
View File
@@ -30,74 +30,77 @@
// Forward declaration
class STM32_LittleFS;
namespace STM32_LittleFS_Namespace {
namespace STM32_LittleFS_Namespace
{
// avoid conflict with other FileSystem FILE_READ/FILE_WRITE
enum {
FILE_O_READ = 0,
FILE_O_WRITE = 1,
FILE_O_READ = 0,
FILE_O_WRITE = 1,
};
class File : public Stream {
public:
explicit File(STM32_LittleFS &fs);
File(char const *filename, uint8_t mode, STM32_LittleFS &fs);
class File : public Stream
{
public:
explicit File(STM32_LittleFS &fs);
File(char const *filename, uint8_t mode, STM32_LittleFS &fs);
public:
bool open(char const *filename, uint8_t mode);
public:
bool open(char const *filename, uint8_t mode);
//------------- Stream API -------------//
virtual size_t write(uint8_t ch);
virtual size_t write(uint8_t const *buf, size_t size);
size_t write(const char *str) {
if (str == NULL)
return 0;
return write((const uint8_t *)str, strlen(str));
}
size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
//------------- Stream API -------------//
virtual size_t write(uint8_t ch);
virtual size_t write(uint8_t const *buf, size_t size);
size_t write(const char *str)
{
if (str == NULL)
return 0;
return write((const uint8_t *)str, strlen(str));
}
size_t write(const char *buffer, size_t size) { return write((const uint8_t *)buffer, size); }
virtual int read(void);
int read(void *buf, uint16_t nbyte);
virtual int read(void);
int read(void *buf, uint16_t nbyte);
virtual int peek(void);
virtual int available(void);
virtual void flush(void);
virtual int peek(void);
virtual int available(void);
virtual void flush(void);
bool seek(uint32_t pos);
uint32_t position(void);
uint32_t size(void);
bool seek(uint32_t pos);
uint32_t position(void);
uint32_t size(void);
bool truncate(uint32_t pos);
bool truncate(void);
bool truncate(uint32_t pos);
bool truncate(void);
void close(void);
void close(void);
operator bool(void);
operator bool(void);
bool isOpen(void);
char const *name(void);
bool isOpen(void);
char const *name(void);
bool isDirectory(void);
File openNextFile(uint8_t mode = FILE_O_READ);
void rewindDirectory(void);
bool isDirectory(void);
File openNextFile(uint8_t mode = FILE_O_READ);
void rewindDirectory(void);
private:
STM32_LittleFS *_fs;
private:
STM32_LittleFS *_fs;
bool _is_dir;
bool _is_dir;
union {
lfs_file_t *_file;
lfs_dir_t *_dir;
};
union {
lfs_file_t *_file;
lfs_dir_t *_dir;
};
char *_dir_path;
char _name[LFS_NAME_MAX + 1];
char *_dir_path;
char _name[LFS_NAME_MAX + 1];
bool _open(char const *filepath, uint8_t mode);
bool _open_file(char const *filepath, uint8_t mode);
bool _open_dir(char const *filepath);
void _close(void);
bool _open(char const *filepath, uint8_t mode);
bool _open_file(char const *filepath, uint8_t mode);
bool _open_dir(char const *filepath);
void _close(void);
};
} // namespace STM32_LittleFS_Namespace
+1 -1
View File
@@ -34,6 +34,6 @@
#define SX126X_BUSY 1004
#if !defined(DEBUG_MUTE) && !defined(PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF)
#error \
#error \
"You MUST enable PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF if debug prints are enabled. printf will print uninitialized garbage instead of floats."
#endif
File diff suppressed because it is too large Load Diff
+152 -152
View File
@@ -49,230 +49,230 @@ typedef uint32_t lfs_block_t;
// Possible error codes, these are negative to allow
// valid positive return values
enum lfs_error {
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -52, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
LFS_ERR_OK = 0, // No error
LFS_ERR_IO = -5, // Error during device operation
LFS_ERR_CORRUPT = -52, // Corrupted
LFS_ERR_NOENT = -2, // No directory entry
LFS_ERR_EXIST = -17, // Entry already exists
LFS_ERR_NOTDIR = -20, // Entry is not a dir
LFS_ERR_ISDIR = -21, // Entry is a dir
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
LFS_ERR_BADF = -9, // Bad file number
LFS_ERR_INVAL = -22, // Invalid parameter
LFS_ERR_NOSPC = -28, // No space left on device
LFS_ERR_NOMEM = -12, // No more memory available
};
// File types
enum lfs_type {
LFS_TYPE_REG = 0x11,
LFS_TYPE_DIR = 0x22,
LFS_TYPE_SUPERBLOCK = 0x2e,
LFS_TYPE_REG = 0x11,
LFS_TYPE_DIR = 0x22,
LFS_TYPE_SUPERBLOCK = 0x2e,
};
// File open flags
enum lfs_open_flags {
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
// open flags
LFS_O_RDONLY = 1, // Open a file as read only
LFS_O_WRONLY = 2, // Open a file as write only
LFS_O_RDWR = 3, // Open a file as read and write
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
// internally used flags
LFS_F_DIRTY = 0x10000, // File does not match storage
LFS_F_WRITING = 0x20000, // File has been written since last flush
LFS_F_READING = 0x40000, // File has been read since last flush
LFS_F_ERRED = 0x80000, // An error occured during write
// internally used flags
LFS_F_DIRTY = 0x10000, // File does not match storage
LFS_F_WRITING = 0x20000, // File has been written since last flush
LFS_F_READING = 0x40000, // File has been read since last flush
LFS_F_ERRED = 0x80000, // An error occured during write
};
// File seek flags
enum lfs_whence_flags {
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
LFS_SEEK_SET = 0, // Seek relative to an absolute position
LFS_SEEK_CUR = 1, // Seek relative to the current file position
LFS_SEEK_END = 2, // Seek relative to the end of the file
};
// Configuration provided during initialization of the littlefs
struct lfs_config {
// Opaque user provided context that can be used to pass
// information to the block device operations
void *context;
// Opaque user provided context that can be used to pass
// information to the block device operations
void *context;
// Read a region in a block. Negative error codes are propogated
// to the user.
int (*read)(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size);
// Read a region in a block. Negative error codes are propogated
// to the user.
int (*read)(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size);
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*prog)(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size);
// Program a region in a block. The block must have previously
// been erased. Negative error codes are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*prog)(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*erase)(const struct lfs_config *c, lfs_block_t block);
// Erase a block. A block must be erased before being programmed.
// The state of an erased block is undefined. Negative error codes
// are propogated to the user.
// May return LFS_ERR_CORRUPT if the block should be considered bad.
int (*erase)(const struct lfs_config *c, lfs_block_t block);
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
int (*sync)(const struct lfs_config *c);
// Sync the state of the underlying block device. Negative error codes
// are propogated to the user.
int (*sync)(const struct lfs_config *c);
// Minimum size of a block read. This determines the size of read buffers.
// This may be larger than the physical read size to improve performance
// by caching more of the block device.
lfs_size_t read_size;
// Minimum size of a block read. This determines the size of read buffers.
// This may be larger than the physical read size to improve performance
// by caching more of the block device.
lfs_size_t read_size;
// Minimum size of a block program. This determines the size of program
// buffers. This may be larger than the physical program size to improve
// performance by caching more of the block device.
// Must be a multiple of the read size.
lfs_size_t prog_size;
// Minimum size of a block program. This determines the size of program
// buffers. This may be larger than the physical program size to improve
// performance by caching more of the block device.
// Must be a multiple of the read size.
lfs_size_t prog_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, this should be
// kept small as each file currently takes up an entire block.
// Must be a multiple of the program size.
lfs_size_t block_size;
// Size of an erasable block. This does not impact ram consumption and
// may be larger than the physical erase size. However, this should be
// kept small as each file currently takes up an entire block.
// Must be a multiple of the program size.
lfs_size_t block_size;
// Number of erasable blocks on the device.
lfs_size_t block_count;
// Number of erasable blocks on the device.
lfs_size_t block_count;
// Number of blocks to lookahead during block allocation. A larger
// lookahead reduces the number of passes required to allocate a block.
// The lookahead buffer requires only 1 bit per block so it can be quite
// large with little ram impact. Should be a multiple of 32.
lfs_size_t lookahead;
// Number of blocks to lookahead during block allocation. A larger
// lookahead reduces the number of passes required to allocate a block.
// The lookahead buffer requires only 1 bit per block so it can be quite
// large with little ram impact. Should be a multiple of 32.
lfs_size_t lookahead;
// Optional, statically allocated read buffer. Must be read sized.
void *read_buffer;
// Optional, statically allocated read buffer. Must be read sized.
void *read_buffer;
// Optional, statically allocated program buffer. Must be program sized.
void *prog_buffer;
// Optional, statically allocated program buffer. Must be program sized.
void *prog_buffer;
// Optional, statically allocated lookahead buffer. Must be 1 bit per
// lookahead block.
void *lookahead_buffer;
// Optional, statically allocated lookahead buffer. Must be 1 bit per
// lookahead block.
void *lookahead_buffer;
// Optional, statically allocated buffer for files. Must be program sized.
// If enabled, only one file may be opened at a time.
void *file_buffer;
// Optional, statically allocated buffer for files. Must be program sized.
// If enabled, only one file may be opened at a time.
void *file_buffer;
};
// Optional configuration provided during lfs_file_opencfg
struct lfs_file_config {
// Optional, statically allocated buffer for files. Must be program sized.
// If NULL, malloc will be used by default.
void *buffer;
// Optional, statically allocated buffer for files. Must be program sized.
// If NULL, malloc will be used by default.
void *buffer;
};
// File info structure
struct lfs_info {
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
uint8_t type;
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
uint8_t type;
// Size of the file, only valid for REG files
lfs_size_t size;
// Size of the file, only valid for REG files
lfs_size_t size;
// Name of the file stored as a null-terminated string
char name[LFS_NAME_MAX + 1];
// Name of the file stored as a null-terminated string
char name[LFS_NAME_MAX + 1];
};
/// littlefs data structures ///
typedef struct lfs_entry {
lfs_off_t off;
lfs_off_t off;
struct lfs_disk_entry {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
union {
struct {
lfs_block_t head;
lfs_size_t size;
} file;
lfs_block_t dir[2];
} u;
} d;
struct lfs_disk_entry {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
union {
struct {
lfs_block_t head;
lfs_size_t size;
} file;
lfs_block_t dir[2];
} u;
} d;
} lfs_entry_t;
typedef struct lfs_cache {
lfs_block_t block;
lfs_off_t off;
uint8_t *buffer;
lfs_block_t block;
lfs_off_t off;
uint8_t *buffer;
} lfs_cache_t;
typedef struct lfs_file {
struct lfs_file *next;
lfs_block_t pair[2];
lfs_off_t poff;
struct lfs_file *next;
lfs_block_t pair[2];
lfs_off_t poff;
lfs_block_t head;
lfs_size_t size;
lfs_block_t head;
lfs_size_t size;
const struct lfs_file_config *cfg;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
const struct lfs_file_config *cfg;
uint32_t flags;
lfs_off_t pos;
lfs_block_t block;
lfs_off_t off;
lfs_cache_t cache;
} lfs_file_t;
typedef struct lfs_dir {
struct lfs_dir *next;
lfs_block_t pair[2];
lfs_off_t off;
struct lfs_dir *next;
lfs_block_t pair[2];
lfs_off_t off;
lfs_block_t head[2];
lfs_off_t pos;
lfs_block_t head[2];
lfs_off_t pos;
struct lfs_disk_dir {
uint32_t rev;
lfs_size_t size;
lfs_block_t tail[2];
} d;
struct lfs_disk_dir {
uint32_t rev;
lfs_size_t size;
lfs_block_t tail[2];
} d;
} lfs_dir_t;
typedef struct lfs_superblock {
lfs_off_t off;
lfs_off_t off;
struct lfs_disk_superblock {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
lfs_block_t root[2];
uint32_t block_size;
uint32_t block_count;
uint32_t version;
char magic[8];
} d;
struct lfs_disk_superblock {
uint8_t type;
uint8_t elen;
uint8_t alen;
uint8_t nlen;
lfs_block_t root[2];
uint32_t block_size;
uint32_t block_count;
uint32_t version;
char magic[8];
} d;
} lfs_superblock_t;
typedef struct lfs_free {
lfs_block_t off;
lfs_block_t size;
lfs_block_t i;
lfs_block_t ack;
uint32_t *buffer;
lfs_block_t off;
lfs_block_t size;
lfs_block_t i;
lfs_block_t ack;
uint32_t *buffer;
} lfs_free_t;
// The littlefs type
typedef struct lfs {
const struct lfs_config *cfg;
const struct lfs_config *cfg;
lfs_block_t root[2];
lfs_file_t *files;
lfs_dir_t *dirs;
lfs_block_t root[2];
lfs_file_t *files;
lfs_dir_t *dirs;
lfs_cache_t rcache;
lfs_cache_t pcache;
lfs_cache_t rcache;
lfs_cache_t pcache;
lfs_free_t free;
bool deorphaned;
lfs_free_t free;
bool deorphaned;
} lfs_t;
/// Filesystem functions ///
+11 -10
View File
@@ -10,18 +10,19 @@
#ifndef LFS_CONFIG
// Software CRC implementation with small lookup table
void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) {
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
};
void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size)
{
static const uint32_t rtable[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
};
const uint8_t *data = buffer;
const uint8_t *data = buffer;
for (size_t i = 0; i < size; i++) {
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
}
for (size_t i = 0; i < size; i++) {
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
}
}
#endif
+64 -46
View File
@@ -80,96 +80,114 @@ extern "C" {
// expensive basic C implementation for debugging purposes
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs_max(uint32_t a, uint32_t b) { return (a > b) ? a : b; }
static inline uint32_t lfs_max(uint32_t a, uint32_t b)
{
return (a > b) ? a : b;
}
static inline uint32_t lfs_min(uint32_t a, uint32_t b) { return (a < b) ? a : b; }
static inline uint32_t lfs_min(uint32_t a, uint32_t b)
{
return (a < b) ? a : b;
}
// Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) {
static inline uint32_t lfs_npw2(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a - 1);
return 32 - __builtin_clz(a - 1);
#else
uint32_t r = 0;
uint32_t s;
a -= 1;
s = (a > 0xffff) << 4;
a >>= s;
r |= s;
s = (a > 0xff) << 3;
a >>= s;
r |= s;
s = (a > 0xf) << 2;
a >>= s;
r |= s;
s = (a > 0x3) << 1;
a >>= s;
r |= s;
return (r | (a >> 1)) + 1;
uint32_t r = 0;
uint32_t s;
a -= 1;
s = (a > 0xffff) << 4;
a >>= s;
r |= s;
s = (a > 0xff) << 3;
a >>= s;
r |= s;
s = (a > 0xf) << 2;
a >>= s;
r |= s;
s = (a > 0x3) << 1;
a >>= s;
r |= s;
return (r | (a >> 1)) + 1;
#endif
}
// Count the number of trailing binary zeros in a
// lfs_ctz(0) may be undefined
static inline uint32_t lfs_ctz(uint32_t a) {
static inline uint32_t lfs_ctz(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
return __builtin_ctz(a);
return __builtin_ctz(a);
#else
return lfs_npw2((a & -a) + 1) - 1;
return lfs_npw2((a & -a) + 1) - 1;
#endif
}
// Count the number of binary ones in a
static inline uint32_t lfs_popc(uint32_t a) {
static inline uint32_t lfs_popc(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return __builtin_popcount(a);
return __builtin_popcount(a);
#else
a = a - ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
a = a - ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
#endif
}
// Find the sequence comparison of a and b, this is the distance
// between a and b ignoring overflow
static inline int lfs_scmp(uint32_t a, uint32_t b) { return (int)(unsigned)(a - b); }
static inline int lfs_scmp(uint32_t a, uint32_t b)
{
return (int)(unsigned)(a - b);
}
// Convert from 32-bit little-endian to native order
static inline uint32_t lfs_fromle32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
static inline uint32_t lfs_fromle32(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap32(a);
return __builtin_bswap32(a);
#else
return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) | (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24);
return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) | (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24);
#endif
}
// Convert to 32-bit little-endian from native order
static inline uint32_t lfs_tole32(uint32_t a) { return lfs_fromle32(a); }
static inline uint32_t lfs_tole32(uint32_t a)
{
return lfs_fromle32(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
static inline void *lfs_malloc(size_t size) {
static inline void *lfs_malloc(size_t size)
{
#ifndef LFS_NO_MALLOC
return malloc(size);
return malloc(size);
#else
(void)size;
return NULL;
(void)size;
return NULL;
#endif
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs_free(void *p) {
static inline void lfs_free(void *p)
{
#ifndef LFS_NO_MALLOC
free(p);
free(p);
#else
(void)p;
(void)p;
#endif
}
+28 -20
View File
@@ -9,19 +9,20 @@ void playStartMelody() {}
void updateBatteryLevel(uint8_t level) {}
void getMacAddr(uint8_t *dmac) {
// https://flit.github.io/2020/06/06/mcu-unique-id-survey.html
const uint32_t uid0 = HAL_GetUIDw0(); // X/Y coordinate on wafer
const uint32_t uid1 = HAL_GetUIDw1(); // [31:8] Lot number (23:0), [7:0] Wafer number
const uint32_t uid2 = HAL_GetUIDw2(); // Lot number (55:24)
void getMacAddr(uint8_t *dmac)
{
// https://flit.github.io/2020/06/06/mcu-unique-id-survey.html
const uint32_t uid0 = HAL_GetUIDw0(); // X/Y coordinate on wafer
const uint32_t uid1 = HAL_GetUIDw1(); // [31:8] Lot number (23:0), [7:0] Wafer number
const uint32_t uid2 = HAL_GetUIDw2(); // Lot number (55:24)
// Need to go from 96-bit to 48-bit unique ID
dmac[5] = (uint8_t)uid0;
dmac[4] = (uint8_t)(uid0 >> 16);
dmac[3] = (uint8_t)uid1;
dmac[2] = (uint8_t)(uid1 >> 8);
dmac[1] = (uint8_t)uid2;
dmac[0] = (uint8_t)(uid2 >> 8);
// Need to go from 96-bit to 48-bit unique ID
dmac[5] = (uint8_t)uid0;
dmac[4] = (uint8_t)(uid0 >> 16);
dmac[3] = (uint8_t)uid1;
dmac[2] = (uint8_t)(uid1 >> 8);
dmac[1] = (uint8_t)uid2;
dmac[0] = (uint8_t)(uid2 >> 8);
}
void cpuDeepSleep(uint32_t msecToWake) {}
@@ -29,20 +30,27 @@ void cpuDeepSleep(uint32_t msecToWake) {}
// Hacks to force more code and data out.
// By default __assert_func uses fiprintf which pulls in stdio.
extern "C" void __wrap___assert_func(const char *, int, const char *, const char *) {
while (true)
;
return;
extern "C" void __wrap___assert_func(const char *, int, const char *, const char *)
{
while (true)
;
return;
}
// By default strerror has a lot of strings we probably don't use. Make it return an empty string instead.
char empty = 0;
extern "C" char *__wrap_strerror(int) { return &empty; }
extern "C" char *__wrap_strerror(int)
{
return &empty;
}
#ifdef MESHTASTIC_EXCLUDE_TZ
struct _reent;
// Even if you don't use timezones, mktime will try to set the timezone anyway with _tzset_unlocked(), which pulls in
// scanf and friends. The timezone is initialized to UTC by default.
extern "C" void __wrap__tzset_unlocked_r(struct _reent *reent_ptr) { return; }
// Even if you don't use timezones, mktime will try to set the timezone anyway with _tzset_unlocked(), which pulls in scanf and
// friends. The timezone is initialized to UTC by default.
extern "C" void __wrap__tzset_unlocked_r(struct _reent *reent_ptr)
{
return;
}
#endif