Remove TactilityCore (#550)
This commit is contained in:
committed by
GitHub
parent
dfaeb9a2d9
commit
66a4eb66d6
@@ -6,7 +6,6 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c*)
|
||||
|
||||
list(APPEND REQUIRES_LIST
|
||||
TactilityKernel
|
||||
TactilityCore
|
||||
TactilityFreeRtos
|
||||
hal-device-module
|
||||
lvgl-module
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @brief key-value storage for general purpose.
|
||||
* Maps strings on a fixed set of data types.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace tt {
|
||||
|
||||
/**
|
||||
* A dictionary that maps keys (strings) onto several atomary types.
|
||||
*/
|
||||
class Bundle final {
|
||||
|
||||
typedef uint32_t Hash;
|
||||
|
||||
enum class Type {
|
||||
Bool,
|
||||
Int32,
|
||||
Int64,
|
||||
String,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Type type;
|
||||
union {
|
||||
bool value_bool;
|
||||
int32_t value_int32;
|
||||
int64_t value_int64;
|
||||
};
|
||||
std::string value_string;
|
||||
} Value;
|
||||
|
||||
std::unordered_map<std::string, Value> entries;
|
||||
|
||||
public:
|
||||
|
||||
Bundle() = default;
|
||||
|
||||
Bundle(const Bundle& bundle) {
|
||||
this->entries = bundle.entries;
|
||||
}
|
||||
|
||||
bool getBool(const std::string& key) const;
|
||||
int32_t getInt32(const std::string& key) const;
|
||||
int64_t getInt64(const std::string& key) const;
|
||||
std::string getString(const std::string& key) const;
|
||||
|
||||
bool hasBool(const std::string& key) const;
|
||||
bool hasInt32(const std::string& key) const;
|
||||
bool hasInt64(const std::string& key) const;
|
||||
bool hasString(const std::string& key) const;
|
||||
|
||||
bool optBool(const std::string& key, bool& out) const;
|
||||
bool optInt32(const std::string& key, int32_t& out) const;
|
||||
bool optInt64(const std::string& key, int64_t& out) const;
|
||||
bool optString(const std::string& key, std::string& out) const;
|
||||
|
||||
void putBool(const std::string& key, bool value);
|
||||
void putInt32(const std::string& key, int32_t value);
|
||||
void putInt64(const std::string& key, int64_t value);
|
||||
void putString(const std::string& key, const std::string& value);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#define TT_STRINGIFY(x) #x
|
||||
|
||||
// region Variable arguments support
|
||||
|
||||
// Adapted from https://stackoverflow.com/a/78848701/3848666
|
||||
#define TT_ARG_IGNORE(X)
|
||||
#define TT_ARG_CAT(X,Y) _TT_ARG_CAT(X,Y)
|
||||
#define _TT_ARG_CAT(X,Y) X ## Y
|
||||
#define TT_ARGCOUNT(...) _TT_ARGCOUNT ## __VA_OPT__(1(__VA_ARGS__) TT_ARG_IGNORE) (0)
|
||||
#define _TT_ARGCOUNT1(X,...) _TT_ARGCOUNT ## __VA_OPT__(2(__VA_ARGS__) TT_ARG_IGNORE) (1)
|
||||
#define _TT_ARGCOUNT2(X,...) _TT_ARGCOUNT ## __VA_OPT__(3(__VA_ARGS__) TT_ARG_IGNORE) (2)
|
||||
#define _TT_ARGCOUNT3(X,...) _TT_ARGCOUNT ## __VA_OPT__(4(__VA_ARGS__) TT_ARG_IGNORE) (3)
|
||||
#define _TT_ARGCOUNT4(X,...) _TT_ARGCOUNT ## __VA_OPT__(5(__VA_ARGS__) TT_ARG_IGNORE) (4)
|
||||
#define _TT_ARGCOUNT5(X,...) 5
|
||||
#define _TT_ARGCOUNT(X) X
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define TT_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define TT_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#pragma message("WARNING: TT_DEPRECATED is not implemented for this compiler")
|
||||
#define TT_DEPRECATED
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/freertoscompat/RTOS.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
typedef portBASE_TYPE CpuAffinity;
|
||||
|
||||
constexpr static CpuAffinity None = -1;
|
||||
|
||||
/**
|
||||
* Determines the preferred affinity for certain (sub)systems.
|
||||
*/
|
||||
struct CpuAffinityConfiguration {
|
||||
CpuAffinity system;
|
||||
CpuAffinity graphics; // Display, LVGL
|
||||
CpuAffinity wifi;
|
||||
CpuAffinity mainDispatcher;
|
||||
CpuAffinity apps;
|
||||
CpuAffinity timer; // Tactility Timer (based on FreeRTOS)
|
||||
};
|
||||
|
||||
void setCpuAffinityConfiguration(const CpuAffinityConfiguration& config);
|
||||
|
||||
const CpuAffinityConfiguration& getCpuAffinityConfiguration();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace tt::string {
|
||||
|
||||
/**
|
||||
* Given a filesystem path as input, try and get the parent path.
|
||||
* @param[in] path input path
|
||||
* @param[out] output an output buffer that is allocated to at least the size of "current"
|
||||
* @return true when successful
|
||||
*/
|
||||
bool getPathParent(const std::string& path, std::string& output);
|
||||
|
||||
/**
|
||||
* Given a filesystem path as input, get the last segment of a path
|
||||
* @param[in] path input path
|
||||
*/
|
||||
std::string getLastPathSegment(const std::string& path);
|
||||
|
||||
/**
|
||||
* Splits the provided input into separate pieces with delimiter as separator text.
|
||||
* When the input string is empty, the output list will be empty too.
|
||||
*
|
||||
* @param input the input to split up
|
||||
* @param delimiter a non-empty string to recognize as separator
|
||||
*/
|
||||
std::vector<std::string> split(const std::string& input, const std::string& delimiter);
|
||||
|
||||
/**
|
||||
* Splits the provided input into separate pieces with delimiter as separator text.
|
||||
* When the input string is empty, the output list will be empty too.
|
||||
*
|
||||
* @param input the input to split up
|
||||
* @param delimiter a non-empty string to recognize as separator
|
||||
* @param callback the callback function that receives the split parts
|
||||
*/
|
||||
void split(const std::string& input, const std::string& delimiter, std::function<void(const std::string&)> callback);
|
||||
|
||||
/**
|
||||
* Join a set of tokens into a single string, given a delimiter (separator).
|
||||
* If the input is an empty list, the result will be an empty string.
|
||||
* The delimeter is only placed inbetween tokens and not appended at the end of the resulting string.
|
||||
*
|
||||
* @param input the tokens to join together
|
||||
* @param delimiter the separator to join with
|
||||
*/
|
||||
std::string join(const std::vector<std::string>& input, const std::string& delimiter);
|
||||
|
||||
/**
|
||||
* Join a set of tokens into a single string, given a delimiter (separator).
|
||||
* If the input is an empty list, the result will be an empty string.
|
||||
* The delimeter is only placed inbetween tokens and not appended at the end of the resulting string.
|
||||
*
|
||||
* @param input the tokens to join together
|
||||
* @param delimiter the separator to join with
|
||||
*/
|
||||
std::string join(const std::vector<const char*>& input, const std::string& delimiter);
|
||||
|
||||
/**
|
||||
* Returns the lowercase value of a string.
|
||||
* @warning This only works for strings with 1 byte per character
|
||||
* @param[in] the string with lower and/or uppercase characters
|
||||
* @return a string with only lowercase characters
|
||||
*/
|
||||
template <typename T>
|
||||
std::basic_string<T> lowercase(const std::basic_string<T>& input) {
|
||||
std::basic_string<T> output = input;
|
||||
std::transform(
|
||||
output.begin(),
|
||||
output.end(),
|
||||
output.begin(),
|
||||
[](const T character) { return static_cast<T>(std::tolower(character)); }
|
||||
);
|
||||
return std::move(output);
|
||||
}
|
||||
|
||||
/** @return true when input only has hex characters: [a-z], [A-Z], [0-9] */
|
||||
bool isAsciiHexString(const std::string& input);
|
||||
|
||||
/** @return the first part of a file name right up (and excluding) the first period character. */
|
||||
std::string removeFileExtension(const std::string& input);
|
||||
|
||||
/**
|
||||
* Remove the given characters from the start and end of the specified string.
|
||||
* @param[in] input the text to trim
|
||||
* @param[in] characters the characters to remove from the input
|
||||
* @return the input where the specified characters are removed from the start and end of the input string
|
||||
*/
|
||||
std::string trim(const std::string& input, const std::string& characters);
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "CoreDefines.h"
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* All functions in this file can be safely called without manually applying file locks.
|
||||
* For calls to C stdlib APIs such as fopen(), always call file::getLock(path) first!
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Tactility/TactilityCore.h>
|
||||
#include <Tactility/Lock.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @warning SD card access requires a locking mechanism:
|
||||
* @warning When using this in the Tactility main project, use `file::getLock()` or `file::withLock()`
|
||||
*/
|
||||
namespace tt::file {
|
||||
|
||||
/** File types for `dirent`'s `d_type`. */
|
||||
enum {
|
||||
TT_DT_UNKNOWN = 0, // Unknown type
|
||||
TT_DT_FIFO = 1, // Named pipe or FIFO
|
||||
TT_DT_CHR = 2, // Character device
|
||||
TT_DT_DIR = 4, // Directory
|
||||
TT_DT_BLK = 6, // Block device
|
||||
TT_DT_REG = 8, // Regular file
|
||||
TT_DT_LNK = 10, // Symbolic link
|
||||
TT_DT_SOCK = 12, // Local-domain socket
|
||||
TT_DT_WHT = 14 // Whiteout inodes
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr char SEPARATOR = '\\';
|
||||
#else
|
||||
constexpr char SEPARATOR = '/';
|
||||
#endif
|
||||
|
||||
struct FileCloser {
|
||||
void operator()(FILE* file) {
|
||||
if (file != nullptr) {
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::function<std::shared_ptr<Lock>(const std::string&)> FindLockFunction;
|
||||
|
||||
/**
|
||||
* @param[in] path the path to get a lock for
|
||||
* @return a lock instance (never null)
|
||||
*/
|
||||
std::shared_ptr<Lock> getLock(const std::string& path);
|
||||
|
||||
void setFindLockFunction(const FindLockFunction& function);
|
||||
|
||||
long getSize(FILE* file);
|
||||
|
||||
/** Read a file and return its data.
|
||||
* @param[in] filepath the path of the file
|
||||
* @param[out] size the amount of bytes that were read
|
||||
* @return null on error, or an array of bytes in case of success
|
||||
*/
|
||||
std::unique_ptr<uint8_t[]> readBinary(const std::string& filepath, size_t& outSize);
|
||||
|
||||
/** Read a file and return a null-terminated string that represents its content.
|
||||
* @param[in] filepath the path of the file
|
||||
* @return null on error, or an array of bytes in case of success. Empty string returns as a single 0 character.
|
||||
*/
|
||||
std::unique_ptr<uint8_t[]> readString(const std::string& filepath);
|
||||
|
||||
/** Write text to a file
|
||||
* @param[in] path file path to write to
|
||||
* @param[in] content file content to write
|
||||
* @return true when operation is successful
|
||||
*/
|
||||
bool writeString(const std::string& filepath, const std::string& content);
|
||||
|
||||
/** Ensure a directory path exists.
|
||||
* @param[in] path the directory path to find, or to create recursively
|
||||
* @param[in] mode the mode to use when creating directories
|
||||
* @return true when the specified path was found, or otherwise creates the directories recursively with the specified mode.
|
||||
*/
|
||||
bool findOrCreateDirectory(const std::string& path, mode_t mode);
|
||||
|
||||
bool findOrCreateParentDirectory(const std::string& path, mode_t mode);
|
||||
|
||||
bool deleteRecursively(const std::string& path);
|
||||
|
||||
bool deleteFile(const std::string& path);
|
||||
|
||||
bool deleteDirectory(const std::string& path);
|
||||
|
||||
/**
|
||||
* Concatenate a child path with a parent path, ensuring proper slash inbetween
|
||||
* @param basePath an absolute path with or without trailing "/"
|
||||
* @param childPath the name of the child path (e.g. subfolder or file)
|
||||
* @return the concatenated path
|
||||
*/
|
||||
std::string getChildPath(const std::string& basePath, const std::string& childPath);
|
||||
|
||||
/**
|
||||
* Find the last part of the path. This can either be the filename or the deepest folder name.
|
||||
* @param path an absolute or relative path
|
||||
* @return the last segment of the specified path
|
||||
*/
|
||||
std::string getLastPathSegment(const std::string& path);
|
||||
|
||||
/**
|
||||
* Find the first part of the path. This is either the root folder, or a file if the latter has no parent folder.
|
||||
* @param path an absolute or relative path
|
||||
* @return the first segment of the specified path
|
||||
*/
|
||||
std::string getFirstPathSegment(const std::string& path);
|
||||
|
||||
typedef int (*ScandirFilter)(const dirent*);
|
||||
|
||||
typedef bool (*ScandirSort)(const dirent&, const dirent&);
|
||||
|
||||
/** Used for sorting by alphanumeric value and file type */
|
||||
bool direntSortAlphaAndType(const dirent& left, const dirent& right);
|
||||
|
||||
/** A filter for filtering out "." and ".." */
|
||||
int direntFilterDotEntries(const dirent* entry);
|
||||
|
||||
bool isFile(const std::string& path);
|
||||
|
||||
bool isDirectory(const std::string& path);
|
||||
|
||||
/**
|
||||
* A scandir()-like implementation that works on ESP32.
|
||||
* It does not return "." and ".." items but otherwise functions the same.
|
||||
* It returns an allocated output array with allocated dirent instances.
|
||||
* The caller is responsible for free-ing the memory of these.
|
||||
*
|
||||
* @param[in] path path the scan for files and directories
|
||||
* @param[out] onEntry a pointer to a function that accepts an entry
|
||||
* @return true if the directory exists and listing its contents was successful
|
||||
*/
|
||||
bool listDirectory(
|
||||
const std::string& path,
|
||||
std::function<void(const dirent&)> onEntry
|
||||
);
|
||||
|
||||
/**
|
||||
* A scandir()-like implementation that works on ESP32.
|
||||
* It does not return "." and ".." items but otherwise functions the same.
|
||||
* It returns an allocated output array with allocated dirent instances.
|
||||
* The caller is responsible for free-ing the memory of these.
|
||||
*
|
||||
* @param[in] path path the scan for files and directories
|
||||
* @param[out] outList a pointer to vector of dirent
|
||||
* @param[in] filter an optional filter to filter out specific items (nullable)
|
||||
* @param[in] sort an optional sorting function (nullable)
|
||||
* @return the amount of items that were stored in "output" or -1 when an error occurred
|
||||
*/
|
||||
int scandir(
|
||||
const std::string& path,
|
||||
std::vector<dirent>& outList,
|
||||
ScandirFilter filter = nullptr,
|
||||
ScandirSort sort = nullptr
|
||||
);
|
||||
|
||||
bool readLines(const std::string& filePath, bool stripNewLine, std::function<void(const char* line)> callback);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#define CRASH_DATA_CALLSTACK_LIMIT 64
|
||||
#define CRASH_DATA_INCLUDES_SP false
|
||||
|
||||
/** Represents a single frame on the callstack. */
|
||||
struct CallstackFrame {
|
||||
uint32_t pc = 0;
|
||||
#if CRASH_DATA_INCLUDES_SP
|
||||
uint32_t sp = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
/** Callstack-related crash data. */
|
||||
struct CrashData {
|
||||
bool callstackCorrupted = false;
|
||||
uint8_t callstackLength = 0;
|
||||
CallstackFrame callstack[CRASH_DATA_CALLSTACK_LIMIT];
|
||||
};
|
||||
|
||||
/** @return the crash data */
|
||||
const CrashData& getRtcCrashData();
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace tt::kernel {
|
||||
|
||||
/** Recognized platform types */
|
||||
typedef enum {
|
||||
PlatformEsp,
|
||||
PlatformSimulator
|
||||
} Platform;
|
||||
|
||||
/** @return the platform that Tactility currently is running on. */
|
||||
Platform getPlatform();
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <tactility/drivers/spi_controller.h>
|
||||
#include <tactility/device.h>
|
||||
|
||||
#include <Tactility/Lock.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
class SpiDeviceLock : public Lock {
|
||||
::Device* device;
|
||||
public:
|
||||
explicit SpiDeviceLock(::Device* device) : device(device) { }
|
||||
|
||||
bool lock(TickType_t timeout) const override {
|
||||
return spi_controller_try_lock(device, timeout) == ERROR_NONE;
|
||||
}
|
||||
|
||||
void unlock() const override {
|
||||
spi_controller_unlock(device);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace tt::kernel::critical {
|
||||
|
||||
struct CriticalInfo {
|
||||
uint32_t isrm;
|
||||
bool fromIsr;
|
||||
bool kernelRunning;
|
||||
};
|
||||
|
||||
/** Enter a critical section
|
||||
* @return info on the status
|
||||
*/
|
||||
CriticalInfo enter();
|
||||
|
||||
/**
|
||||
* Exit a critical section
|
||||
* @param[in] info the info from when the critical section was started
|
||||
*/
|
||||
void exit(CriticalInfo info);
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "Tactility/Bundle.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
bool Bundle::getBool(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_bool;
|
||||
}
|
||||
|
||||
int32_t Bundle::getInt32(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_int32;
|
||||
}
|
||||
|
||||
int64_t Bundle::getInt64(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_int64;
|
||||
}
|
||||
|
||||
std::string Bundle::getString(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_string;
|
||||
}
|
||||
|
||||
bool Bundle::hasBool(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::Bool;
|
||||
}
|
||||
|
||||
bool Bundle::hasInt32(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::Int32;
|
||||
}
|
||||
|
||||
bool Bundle::hasInt64(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::Int64;
|
||||
}
|
||||
|
||||
bool Bundle::hasString(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::String;
|
||||
}
|
||||
|
||||
bool Bundle::optBool(const std::string& key, bool& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::Bool) {
|
||||
out = entry->second.value_bool;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bundle::optInt32(const std::string& key, int32_t& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::Int32) {
|
||||
out = entry->second.value_int32;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bundle::optInt64(const std::string& key, int64_t& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::Int64) {
|
||||
out = entry->second.value_int64;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bundle::optString(const std::string& key, std::string& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::String) {
|
||||
out = entry->second.value_string;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Bundle::putBool(const std::string& key, bool value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::Bool,
|
||||
.value_bool = value,
|
||||
.value_string = ""
|
||||
};
|
||||
}
|
||||
|
||||
void Bundle::putInt32(const std::string& key, int32_t value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::Int32,
|
||||
.value_int32 = value,
|
||||
.value_string = ""
|
||||
};
|
||||
}
|
||||
|
||||
void Bundle::putInt64(const std::string& key, int64_t value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::Int64,
|
||||
.value_int64 = value,
|
||||
.value_string = ""
|
||||
};
|
||||
}
|
||||
|
||||
void Bundle::putString(const std::string& key, const std::string& value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::String,
|
||||
.value_bool = false,
|
||||
.value_string = value
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <sdkconfig.h>
|
||||
#endif
|
||||
|
||||
#include "Tactility/CpuAffinity.h"
|
||||
|
||||
#include <tactility/check.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_ENABLED
|
||||
|
||||
static CpuAffinity getEspWifiAffinity() {
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0)
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1)
|
||||
return 1;
|
||||
#else // Assume core 0 (risky, but safer than "None")
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Warning: Must watch ESP WiFi, as this task is used by WiFi
|
||||
static CpuAffinity getEspMainSchedulerAffinity() {
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0)
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1)
|
||||
return 1;
|
||||
#else
|
||||
// Default to core 0 when no explicit WiFi pinning is configured
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static CpuAffinity getFreeRtosTimerAffinity() {
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
return 0;
|
||||
#elif defined(CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY)
|
||||
return None;
|
||||
#elif defined(CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0)
|
||||
return 0;
|
||||
#elif defined(CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1)
|
||||
return 1;
|
||||
#else
|
||||
static_assert(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
static const CpuAffinityConfiguration esp = {
|
||||
.system = 0,
|
||||
.graphics = 0,
|
||||
.wifi = 0,
|
||||
.mainDispatcher = 0,
|
||||
.apps = 0,
|
||||
.timer = getFreeRtosTimerAffinity()
|
||||
};
|
||||
#elif CONFIG_FREERTOS_NUMBER_OF_CORES == 2
|
||||
static const CpuAffinityConfiguration esp = {
|
||||
.system = 0,
|
||||
.graphics = 1,
|
||||
#ifdef CONFIG_ESP_WIFI_ENABLED
|
||||
.wifi = getEspWifiAffinity(),
|
||||
#else
|
||||
.wifi = 0,
|
||||
#endif
|
||||
.mainDispatcher = getEspMainSchedulerAffinity(),
|
||||
.apps = 1,
|
||||
.timer = getFreeRtosTimerAffinity()
|
||||
};
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
static const CpuAffinityConfiguration simulator = {
|
||||
.system = None,
|
||||
.graphics = None,
|
||||
.wifi = None,
|
||||
.mainDispatcher = 0,
|
||||
.apps = None,
|
||||
.timer = None
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
const CpuAffinityConfiguration& getCpuAffinityConfiguration() {
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 2
|
||||
// WiFi uses the main dispatcher to defer operations in the background
|
||||
assert(esp.wifi == esp.mainDispatcher);
|
||||
#endif // CORES
|
||||
return esp;
|
||||
|
||||
#else
|
||||
return simulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "Tactility/StringUtils.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace tt::string {
|
||||
|
||||
bool getPathParent(const std::string& path, std::string& output) {
|
||||
auto index = path.find_last_of('/');
|
||||
if (index == std::string::npos) {
|
||||
return false;
|
||||
} else if (index == 0) {
|
||||
output = "/";
|
||||
return true;
|
||||
} else {
|
||||
output = path.substr(0, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getLastPathSegment(const std::string& path) {
|
||||
auto index = path.find_last_of('/');
|
||||
if (index != std::string::npos) {
|
||||
return path.substr(index + 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void split(const std::string& input, const std::string& delimiter, std::function<void(const std::string&)> callback) {
|
||||
size_t token_index = 0;
|
||||
size_t delimiter_index;
|
||||
const size_t delimiter_length = delimiter.length();
|
||||
|
||||
while ((delimiter_index = input.find(delimiter, token_index)) != std::string::npos) {
|
||||
std::string token = input.substr(token_index, delimiter_index - token_index);
|
||||
token_index = delimiter_index + delimiter_length;
|
||||
callback(token);
|
||||
}
|
||||
|
||||
auto end_token = input.substr(token_index);
|
||||
if (!end_token.empty()) {
|
||||
callback(end_token);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string&input, const std::string&delimiter) {
|
||||
std::vector<std::string> result;
|
||||
split(input, delimiter, [&result](const std::string& token) {
|
||||
result.push_back(token);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string join(const std::vector<const char*>& input, const std::string& delimiter) {
|
||||
std::stringstream stream;
|
||||
size_t size = input.size();
|
||||
|
||||
if (size == 0) {
|
||||
return "";
|
||||
} else if (size == 1) {
|
||||
return input.front();
|
||||
} else {
|
||||
auto iterator = input.begin();
|
||||
while (iterator != input.end()) {
|
||||
stream << *iterator;
|
||||
iterator++;
|
||||
if (iterator != input.end()) {
|
||||
stream << delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string join(const std::vector<std::string>& input, const std::string& delimiter) {
|
||||
std::stringstream stream;
|
||||
size_t size = input.size();
|
||||
|
||||
if (size == 0) {
|
||||
return "";
|
||||
} else if (size == 1) {
|
||||
return input.front();
|
||||
} else {
|
||||
auto iterator = input.begin();
|
||||
while (iterator != input.end()) {
|
||||
stream << *iterator;
|
||||
iterator++;
|
||||
if (iterator != input.end()) {
|
||||
stream << delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string removeFileExtension(const std::string& input) {
|
||||
auto index = input.find('.');
|
||||
if (index != std::string::npos) {
|
||||
return input.substr(0, index);
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
bool isAsciiHexString(const std::string& input) {
|
||||
// Find invalid characters
|
||||
return std::ranges::views::filter(input, [](char character){
|
||||
if (
|
||||
(('0' <= character) && ('9' >= character)) ||
|
||||
(('a' <= character) && ('f' >= character)) ||
|
||||
(('A' <= character) && ('F' >= character))
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).empty();
|
||||
}
|
||||
|
||||
std::string trim(const std::string& input, const std::string& characters) {
|
||||
auto index = input.find_first_not_of(characters);
|
||||
if (index == std::string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
auto end_index = input.find_last_not_of(characters);
|
||||
return input.substr(index, end_index - index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,391 @@
|
||||
#include <Tactility/file/File.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <tactility/log.h>
|
||||
#include <Tactility/StringUtils.h>
|
||||
|
||||
namespace tt::hal::sdcard {
|
||||
class SdCardDevice;
|
||||
}
|
||||
|
||||
namespace tt::file {
|
||||
|
||||
constexpr auto* TAG = "file";
|
||||
|
||||
class NoLock final : public Lock {
|
||||
bool lock(TickType_t timeout) const override { return true; }
|
||||
void unlock() const override { /* NO-OP */ }
|
||||
};
|
||||
|
||||
static std::shared_ptr<Lock> noLock = std::make_shared<NoLock>();
|
||||
static std::function<std::shared_ptr<Lock>(const std::string&)> findLockFunction = nullptr;
|
||||
|
||||
std::shared_ptr<Lock> getLock(const std::string& path) {
|
||||
if (findLockFunction == nullptr) {
|
||||
LOG_W(TAG, "File lock function not set!");
|
||||
return noLock;
|
||||
}
|
||||
|
||||
auto lock = findLockFunction(path);
|
||||
if (lock == nullptr) {
|
||||
return noLock;
|
||||
}
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
void setFindLockFunction(const FindLockFunction& function) {
|
||||
findLockFunction = function;
|
||||
}
|
||||
|
||||
std::string getChildPath(const std::string& basePath, const std::string& childPath) {
|
||||
// Postfix with "/" when the current path isn't "/"
|
||||
if (basePath.length() != 1) {
|
||||
return basePath + "/" + childPath;
|
||||
} else {
|
||||
return "/" + childPath;
|
||||
}
|
||||
}
|
||||
|
||||
int direntFilterDotEntries(const dirent* entry) {
|
||||
return (strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, ".") == 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
bool direntSortAlphaAndType(const dirent& left, const dirent& right) {
|
||||
bool left_is_dir = left.d_type == TT_DT_DIR || left.d_type == TT_DT_CHR;
|
||||
bool right_is_dir = right.d_type == TT_DT_DIR || right.d_type == TT_DT_CHR;
|
||||
if (left_is_dir == right_is_dir) {
|
||||
return strcmp(left.d_name, right.d_name) < 0;
|
||||
} else {
|
||||
return left_is_dir > right_is_dir;
|
||||
}
|
||||
}
|
||||
|
||||
bool listDirectory(
|
||||
const std::string& path,
|
||||
std::function<void(const dirent&)> onEntry
|
||||
) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
LOG_I(TAG, "listDir start %s", path.c_str());
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (dir == nullptr) {
|
||||
LOG_E(TAG, "Failed to open dir %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
dirent* current_entry;
|
||||
while ((current_entry = readdir(dir)) != nullptr) {
|
||||
onEntry(*current_entry);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
LOG_I(TAG, "listDir stop %s", path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
int scandir(
|
||||
const std::string& path,
|
||||
std::vector<dirent>& outList,
|
||||
ScandirFilter filterMethod,
|
||||
ScandirSort sortMethod
|
||||
) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
LOG_I(TAG, "scandir start");
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (dir == nullptr) {
|
||||
LOG_E(TAG, "Failed to open dir %s", path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
dirent* current_entry;
|
||||
while ((current_entry = readdir(dir)) != nullptr) {
|
||||
if (filterMethod == nullptr || filterMethod(current_entry) == 0) {
|
||||
outList.push_back(*current_entry);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
if (sortMethod != nullptr) {
|
||||
std::ranges::sort(outList, sortMethod);
|
||||
}
|
||||
|
||||
LOG_I(TAG, "scandir finish");
|
||||
return outList.size();
|
||||
}
|
||||
|
||||
|
||||
long getSize(FILE* file) {
|
||||
long original_offset = ftell(file);
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
LOG_E(TAG, "fseek failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long file_size = ftell(file);
|
||||
if (file_size == -1) {
|
||||
LOG_E(TAG, "Could not get file length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(file, original_offset, SEEK_SET) != 0) {
|
||||
LOG_E(TAG, "fseek Failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
/** Read a file.
|
||||
* @param[in] filepath
|
||||
* @param[out] outSize the amount of bytes that were read, excluding the sizePadding
|
||||
* @param[in] sizePadding optional padding to add at the end of the output data (the values are not set)
|
||||
*/
|
||||
static std::unique_ptr<uint8_t[]> readBinaryInternal(const std::string& filepath, size_t& outSize, size_t sizePadding = 0) {
|
||||
FILE* file = fopen(filepath.c_str(), "rb");
|
||||
|
||||
if (file == nullptr) {
|
||||
LOG_E(TAG, "Failed to open %s", filepath.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long content_length = getSize(file);
|
||||
if (content_length == -1) {
|
||||
LOG_E(TAG, "Failed to determine content length for %s", filepath.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto data = std::make_unique<uint8_t[]>(content_length + sizePadding);
|
||||
if (data == nullptr) {
|
||||
LOG_E(TAG, "Insufficient memory. Failed to allocate %ld bytes.", content_length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t buffer_offset = 0;
|
||||
while (buffer_offset < content_length) {
|
||||
size_t bytes_read = fread(&data.get()[buffer_offset], 1, content_length - buffer_offset, file);
|
||||
LOG_D(TAG, "Read %u bytes", (unsigned)bytes_read);
|
||||
if (bytes_read > 0) {
|
||||
buffer_offset += bytes_read;
|
||||
} else { // Something went wrong?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
if (buffer_offset == content_length) {
|
||||
outSize = buffer_offset;
|
||||
return data;
|
||||
} else {
|
||||
outSize = 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> readBinary(const std::string& filepath, size_t& outSize) {
|
||||
return readBinaryInternal(filepath, outSize);
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> readString(const std::string& filepath) {
|
||||
size_t size = 0;
|
||||
auto data = readBinaryInternal(filepath, size, 1);
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data.get()[size] = 0; // Append null terminator
|
||||
return data;
|
||||
}
|
||||
|
||||
bool writeString(const std::string& filepath, const std::string& content) {
|
||||
std::ofstream fileStream(filepath);
|
||||
|
||||
if (!fileStream.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fileStream << content;
|
||||
fileStream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool findOrCreateDirectoryInternal(std::string path, mode_t mode) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
struct stat dir_stat;
|
||||
if (mkdir(path.c_str(), mode) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errno != EEXIST) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat(path.c_str(), &dir_stat) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(dir_stat.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getLastPathSegment(const std::string& path) {
|
||||
auto index = path.find_last_of('/');
|
||||
if (index != std::string::npos) {
|
||||
return path.substr(index + 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getFirstPathSegment(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
auto start_index = path[0] == SEPARATOR ? 1 : 0;
|
||||
auto index = path.find_first_of(SEPARATOR, start_index);
|
||||
if (index != std::string::npos) {
|
||||
return path.substr(0, index);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
bool findOrCreateDirectory(const std::string& path, mode_t mode) {
|
||||
if (path.empty()) {
|
||||
return true;
|
||||
}
|
||||
LOG_D(TAG, "findOrCreate: %s %u", path.c_str(), (unsigned)mode);
|
||||
|
||||
const char separator_to_find[] = {SEPARATOR, 0x00};
|
||||
auto first_index = path[0] == SEPARATOR ? 1 : 0;
|
||||
auto separator_index = path.find(separator_to_find, first_index);
|
||||
|
||||
bool should_break = false;
|
||||
while (!should_break) {
|
||||
bool is_last_segment = (separator_index == std::string::npos);
|
||||
auto to_create = is_last_segment ? path : path.substr(0, separator_index);
|
||||
should_break = is_last_segment;
|
||||
if (!findOrCreateDirectoryInternal(to_create, mode)) {
|
||||
LOG_E(TAG, "Failed to create %s", to_create.c_str());
|
||||
return false;
|
||||
} else {
|
||||
LOG_D(TAG, " - got: %s", to_create.c_str());
|
||||
}
|
||||
|
||||
// Find next file separator index
|
||||
separator_index = path.find(separator_to_find, separator_index + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool findOrCreateParentDirectory(const std::string& path, mode_t mode) {
|
||||
std::string parent;
|
||||
if (!string::getPathParent(path, parent)) {
|
||||
return false;
|
||||
}
|
||||
return findOrCreateDirectory(parent, mode);
|
||||
}
|
||||
|
||||
bool deleteRecursively(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isDirectory(path)) {
|
||||
std::vector<dirent> entries;
|
||||
if (scandir(path, entries) < 0) {
|
||||
LOG_E(TAG, "Failed to scan directory %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
auto child_path = path + "/" + entry.d_name;
|
||||
if (!deleteRecursively(child_path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
LOG_I(TAG, "Deleting %s", path.c_str());
|
||||
return deleteDirectory(path);
|
||||
} else if (isFile(path)) {
|
||||
LOG_I(TAG, "Deleting %s", path.c_str());
|
||||
return deleteFile(path);
|
||||
} else if (path == "/" || path == "." || path == "..") {
|
||||
// No-op
|
||||
return true;
|
||||
} else {
|
||||
LOG_E(TAG, "Failed to delete \"%s\": unknown type", path.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool deleteFile(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return remove(path.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool deleteDirectory(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return rmdir(path.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool isFile(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return access(path.c_str(), F_OK) == 0;
|
||||
}
|
||||
|
||||
bool isDirectory(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
struct stat stat_result;
|
||||
return stat(path.c_str(), &stat_result) == 0 && S_ISDIR(stat_result.st_mode);
|
||||
}
|
||||
|
||||
bool readLines(const std::string& filePath, bool stripNewLine, std::function<void(const char* line)> callback) {
|
||||
auto lock = getLock(filePath)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
auto* file = fopen(filePath.c_str(), "r");
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char line[1024];
|
||||
|
||||
while (fgets(line, sizeof(line), file) != nullptr) {
|
||||
// Strip newline
|
||||
if (stripNewLine) {
|
||||
size_t line_length = strlen(line);
|
||||
if (line_length > 0 && line[line_length - 1] == '\n') {
|
||||
line[line_length - 1] = '\0';
|
||||
}
|
||||
}
|
||||
// Publish
|
||||
callback(line);
|
||||
}
|
||||
|
||||
bool success = feof(file);
|
||||
fclose(file);
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#if defined(ESP_PLATFORM) && defined(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
|
||||
#include "Tactility/kernel/PanicHandler.h"
|
||||
|
||||
#include <esp_debug_helpers.h>
|
||||
#include <esp_attr.h>
|
||||
#include <esp_memory_utils.h>
|
||||
#include <esp_cpu.h>
|
||||
#include <esp_cpu_utils.h>
|
||||
#include <xtensa/xtruntime.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* This static variable survives a crash reboot.
|
||||
* It is reset by the Boot app.
|
||||
*/
|
||||
static RTC_NOINIT_ATTR CrashData crashData;
|
||||
|
||||
void __real_esp_panic_handler(void* info);
|
||||
|
||||
void __wrap_esp_panic_handler(void* info) {
|
||||
|
||||
esp_backtrace_frame_t frame = {
|
||||
.pc = 0,
|
||||
.sp = 0,
|
||||
.next_pc = 0,
|
||||
.exc_frame = nullptr
|
||||
};
|
||||
|
||||
crashData.callstackLength = 0;
|
||||
|
||||
esp_backtrace_get_start(&frame.pc, &frame.sp, &frame.next_pc);
|
||||
crashData.callstack[0].pc = frame.pc;
|
||||
#if CRASH_DATA_INCLUDES_SP
|
||||
crashData.callstack[0].sp = frame.sp;
|
||||
#endif
|
||||
crashData.callstackLength++;
|
||||
|
||||
uint32_t processed_pc = esp_cpu_process_stack_pc(frame.pc);
|
||||
bool pc_is_valid = esp_ptr_executable((void *)processed_pc);
|
||||
|
||||
/* Ignore the first corrupted PC in case of InstrFetchProhibited on Xtensa */
|
||||
if (frame.exc_frame && ((XtExcFrame *)frame.exc_frame)->exccause == EXCCAUSE_INSTR_PROHIBITED) {
|
||||
pc_is_valid = true;
|
||||
}
|
||||
|
||||
crashData.callstackCorrupted = !(esp_stack_ptr_is_sane(frame.sp) && pc_is_valid);
|
||||
|
||||
while (
|
||||
frame.next_pc != 0 &&
|
||||
!crashData.callstackCorrupted
|
||||
&& crashData.callstackLength < CRASH_DATA_CALLSTACK_LIMIT
|
||||
) {
|
||||
if (esp_backtrace_get_next_frame(&frame)) {
|
||||
// Validate the current frame
|
||||
uint32_t processed_frame_pc = esp_cpu_process_stack_pc(frame.pc);
|
||||
bool frame_pc_is_valid = esp_ptr_executable((void *)processed_frame_pc);
|
||||
|
||||
if (!esp_stack_ptr_is_sane(frame.sp) || !frame_pc_is_valid) {
|
||||
crashData.callstackCorrupted = true;
|
||||
break;
|
||||
}
|
||||
crashData.callstack[crashData.callstackLength].pc = frame.pc;
|
||||
#if CRASH_DATA_INCLUDES_SP
|
||||
crashData.callstack[crashData.callstackLength].sp = frame.sp;
|
||||
#endif
|
||||
crashData.callstackLength++;
|
||||
} else {
|
||||
crashData.callstackCorrupted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle corrupted logic
|
||||
|
||||
__real_esp_panic_handler(info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const CrashData& getRtcCrashData() { return crashData; }
|
||||
|
||||
#elif defined(ESP_PLATFORM)
|
||||
|
||||
// Stub implementation for RISC-V and other architectures
|
||||
// TODO: Implement crash data collection for RISC-V using frame pointer or EH frame
|
||||
|
||||
#include <Tactility/kernel/PanicHandler.h>
|
||||
|
||||
static CrashData emptyCrashData = {};
|
||||
|
||||
const CrashData& getRtcCrashData() { return emptyCrashData; }
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "Tactility/kernel/Platform.h"
|
||||
|
||||
namespace tt::kernel {
|
||||
|
||||
Platform getPlatform() {
|
||||
#ifdef ESP_PLATFORM
|
||||
return PlatformEsp;
|
||||
#else
|
||||
return PlatformSimulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <Tactility/kernel/critical/Critical.h>
|
||||
|
||||
#include <Tactility/freertoscompat/Task.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
static portMUX_TYPE critical_mutex;
|
||||
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL(&critical_mutex)
|
||||
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL(&critical_mutex)
|
||||
#else
|
||||
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL()
|
||||
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL()
|
||||
#endif
|
||||
|
||||
namespace tt::kernel::critical {
|
||||
|
||||
CriticalInfo enter() {
|
||||
CriticalInfo info = {
|
||||
.isrm = 0,
|
||||
.fromIsr = (xPortInIsrContext() == pdTRUE),
|
||||
.kernelRunning = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)
|
||||
};
|
||||
|
||||
if (info.fromIsr) {
|
||||
info.isrm = taskENTER_CRITICAL_FROM_ISR();
|
||||
} else if (info.kernelRunning) {
|
||||
TT_ENTER_CRITICAL();
|
||||
} else {
|
||||
portDISABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void exit(CriticalInfo info) {
|
||||
if (info.fromIsr) {
|
||||
taskEXIT_CRITICAL_FROM_ISR(info.isrm);
|
||||
} else if (info.kernelRunning) {
|
||||
TT_EXIT_CRITICAL();
|
||||
} else {
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user