[core] Refactor LT class into C methods
This commit is contained in:
@@ -104,6 +104,7 @@ queue.AppendPublic(
|
||||
("LT_VARIANT_H", r"\"${VARIANT}.h\""),
|
||||
("F_CPU", board.get("build.f_cpu")),
|
||||
("MCU", "${MCU}"),
|
||||
("MCULC", "${MCULC}"),
|
||||
("FAMILY", "F_${FAMILY}"),
|
||||
# Add flash layout defines created in env.AddFlashLayout()
|
||||
*env["FLASH_DEFINES"].items(),
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */
|
||||
|
||||
#include <LT.h>
|
||||
|
||||
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
|
||||
#include <Flash/Flash.h>
|
||||
|
||||
#define REG_FLASH_BASE 0x00803000
|
||||
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
|
||||
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
|
||||
#define FLASH_BUSY_SW (0x01UL << 31)
|
||||
#define FLASH_WP_VALUE (0x01UL << 30)
|
||||
#define FLASH_OP_SW (0x01UL << 29)
|
||||
#define FLASH_OP_TYPE_POS 24
|
||||
#define FLASH_OP_RDID 20
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <include.h>
|
||||
|
||||
#include <flash_pub.h>
|
||||
#include <param_config.h>
|
||||
#include <start_type_pub.h>
|
||||
#include <sys_ctrl.h>
|
||||
#include <sys_rtos.h>
|
||||
#include <wdt_pub.h>
|
||||
#include <wlan_ui_pub.h>
|
||||
|
||||
extern uint8_t system_mac[];
|
||||
extern uint32_t wdt_ctrl(uint32_t cmd, void *param);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
void LibreTuya::restart() {
|
||||
bk_reboot();
|
||||
}
|
||||
|
||||
void LibreTuya::restartDownloadMode() {
|
||||
bk_reboot();
|
||||
}
|
||||
|
||||
/* CPU-related */
|
||||
|
||||
ChipType LibreTuya::getChipType() {
|
||||
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
|
||||
return CHIP_TYPE_ENUM(FAMILY, chipId);
|
||||
}
|
||||
|
||||
const char *LibreTuya::getChipModel() {
|
||||
return STRINGIFY_MACRO(MCU);
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getChipId() {
|
||||
uint8_t mac[6];
|
||||
cfg_load_mac(mac); // force loading MAC from TLV (ignore user-set WiFi MAC)
|
||||
return (mac[3]) | (mac[4] << 8) | (mac[5] << 16);
|
||||
}
|
||||
|
||||
uint8_t LibreTuya::getChipCores() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *LibreTuya::getChipCoreType() {
|
||||
return "ARM968E-S";
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getCpuFreq() {
|
||||
return configCPU_CLOCK_HZ;
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getCycleCount() {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flash memory utilities */
|
||||
|
||||
FlashId LibreTuya::getFlashChipId() {
|
||||
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
|
||||
REG_WRITE(REG_FLASH_OPERATE_SW, data);
|
||||
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
|
||||
FlashId id = {
|
||||
.manufacturerId = REG_RD8(REG_FLASH_RDID, 2),
|
||||
.chipId = REG_RD8(REG_FLASH_RDID, 1),
|
||||
.chipSizeId = REG_RD8(REG_FLASH_RDID, 0),
|
||||
};
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Memory management */
|
||||
|
||||
uint32_t LibreTuya::getRamSize() {
|
||||
return 256 * 1024;
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getHeapSize() {
|
||||
#if configDYNAMIC_HEAP_SIZE
|
||||
extern unsigned char _empty_ram;
|
||||
#if CFG_SOC_NAME == SOC_BK7231N
|
||||
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
|
||||
#else
|
||||
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
|
||||
#endif
|
||||
#else
|
||||
return configTOTAL_HEAP_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getFreeHeap() {
|
||||
return xPortGetFreeHeapSize();
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getMinFreeHeap() {
|
||||
return xPortGetMinimumEverFreeHeapSize();
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getMaxAllocHeap() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OTA-related */
|
||||
|
||||
static int8_t otaImage2Valid = -1;
|
||||
|
||||
uint8_t LibreTuya::otaGetRunning() {
|
||||
// Beken has bootloader-based OTA, running app is always index 1
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t LibreTuya::otaGetStoredIndex() {
|
||||
return otaHasImage2() ? 2 : 1;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaSupportsDual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaHasImage1() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaHasImage2() {
|
||||
if (otaImage2Valid != -1)
|
||||
return otaImage2Valid;
|
||||
// check download RBL
|
||||
// TODO: maybe check header CRC or even binary hashes
|
||||
uint32_t magic;
|
||||
Flash.readBlock(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
|
||||
otaImage2Valid = magic == 0x004C4252; // "RBL\0", little-endian
|
||||
return otaImage2Valid;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaSwitch(bool force) {
|
||||
// no need to check otaGetStoredIndex() as it does the same as otaHasImage2()
|
||||
|
||||
// force checking validity again
|
||||
otaImage2Valid = -1;
|
||||
|
||||
if (otaHasImage2() && force) {
|
||||
// "rollback" - abort bootloader upgrade operation by wiping first sector
|
||||
return Flash.eraseSector(FLASH_DOWNLOAD_OFFSET);
|
||||
}
|
||||
|
||||
return otaHasImage2(); // false if second image is not valid
|
||||
}
|
||||
|
||||
/* Watchdog */
|
||||
|
||||
bool LibreTuya::wdtEnable(uint32_t timeout) {
|
||||
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
|
||||
wdt_ctrl(WCMD_POWER_UP, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibreTuya::wdtDisable() {
|
||||
wdt_ctrl(WCMD_POWER_DOWN, NULL);
|
||||
}
|
||||
|
||||
void LibreTuya::wdtFeed() {
|
||||
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
|
||||
}
|
||||
|
||||
/* Global instance */
|
||||
|
||||
LibreTuya LT;
|
||||
LibreTuya ESP = LT;
|
||||
190
cores/beken-72xx/base/lt_api.c
Normal file
190
cores/beken-72xx/base/lt_api.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
|
||||
|
||||
#include <libretuya.h>
|
||||
#include <sdk_private.h>
|
||||
|
||||
// can't include <flash.h> as it collides with <Flash.h> on Windows -_-
|
||||
#define REG_FLASH_BASE 0x00803000
|
||||
#define REG_FLASH_OPERATE_SW (REG_FLASH_BASE + 0 * 4)
|
||||
#define REG_FLASH_RDID (REG_FLASH_BASE + 4 * 4)
|
||||
#define FLASH_BUSY_SW (0x01UL << 31)
|
||||
#define FLASH_WP_VALUE (0x01UL << 30)
|
||||
#define FLASH_OP_SW (0x01UL << 29)
|
||||
#define FLASH_OP_TYPE_POS 24
|
||||
#define FLASH_OP_RDID 20
|
||||
|
||||
void lt_init_family() {
|
||||
// set default UART output port
|
||||
uart_print_port = LT_UART_DEFAULT_PORT - 1;
|
||||
}
|
||||
|
||||
/* _____ _____ _ _
|
||||
/ ____| __ \| | | |
|
||||
| | | |__) | | | |
|
||||
| | | ___/| | | |
|
||||
| |____| | | |__| |
|
||||
\_____|_| \____*/
|
||||
lt_cpu_model_t lt_get_cpu_model() {
|
||||
uint8_t chipId = *(uint8_t *)(SCTRL_CHIP_ID);
|
||||
return CPU_MODEL_ENUM(FAMILY, chipId);
|
||||
}
|
||||
|
||||
uint32_t lt_get_cpu_unique_id() {
|
||||
return lt_get_cpu_mac_id();
|
||||
}
|
||||
|
||||
uint32_t lt_get_cpu_mac_id() {
|
||||
uint8_t mac[6];
|
||||
cfg_load_mac(mac); // force loading MAC from TLV (ignore user-set WiFi MAC)
|
||||
return (mac[3]) | (mac[4] << 8) | (mac[5] << 16);
|
||||
}
|
||||
|
||||
const char *lt_get_cpu_core_type() {
|
||||
return "ARM968E-S";
|
||||
}
|
||||
|
||||
/*_____ _
|
||||
| __ \ (_)
|
||||
| | | | _____ ___ ___ ___
|
||||
| | | |/ _ \ \ / / |/ __/ _ \
|
||||
| |__| | __/\ V /| | (_| __/
|
||||
|_____/ \___| \_/ |_|\___\__*/
|
||||
void lt_reboot() {
|
||||
bk_reboot();
|
||||
}
|
||||
|
||||
bool lt_reboot_download_mode() {
|
||||
bk_reboot();
|
||||
return true;
|
||||
}
|
||||
|
||||
lt_reboot_reason_t lt_get_reboot_reason() {
|
||||
switch (bk_misc_get_start_type()) {
|
||||
case RESET_SOURCE_POWERON:
|
||||
return REBOOT_REASON_POWER;
|
||||
case RESET_SOURCE_REBOOT:
|
||||
return REBOOT_REASON_SOFTWARE;
|
||||
case RESET_SOURCE_WATCHDOG:
|
||||
return REBOOT_REASON_WATCHDOG;
|
||||
case RESET_SOURCE_CRASH_XAT0:
|
||||
case RESET_SOURCE_CRASH_UNDEFINED:
|
||||
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
|
||||
case RESET_SOURCE_CRASH_DATA_ABORT:
|
||||
case RESET_SOURCE_CRASH_UNUSED:
|
||||
case RESET_SOURCE_CRASH_PER_XAT0:
|
||||
return REBOOT_REASON_CRASH;
|
||||
case RESET_SOURCE_DEEPPS_GPIO:
|
||||
case RESET_SOURCE_DEEPPS_RTC:
|
||||
case RESET_SOURCE_DEEPPS_USB:
|
||||
return REBOOT_REASON_SLEEP;
|
||||
default:
|
||||
return REBOOT_REASON_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/*______ _ _
|
||||
| ____| | | |
|
||||
| |__ | | __ _ ___| |__
|
||||
| __| | |/ _` / __| '_ \
|
||||
| | | | (_| \__ \ | | |
|
||||
|_| |_|\__,_|___/_| |*/
|
||||
lt_flash_id_t lt_flash_get_id() {
|
||||
uint32_t data = (FLASH_OP_RDID << FLASH_OP_TYPE_POS) | FLASH_OP_SW | FLASH_WP_VALUE;
|
||||
REG_WRITE(REG_FLASH_OPERATE_SW, data);
|
||||
while (REG_READ(REG_FLASH_OPERATE_SW) & FLASH_BUSY_SW) {}
|
||||
lt_flash_id_t id = {
|
||||
.manufacturer_id = REG_RD8(REG_FLASH_RDID, 2),
|
||||
.chip_id = REG_RD8(REG_FLASH_RDID, 1),
|
||||
.chip_size_id = REG_RD8(REG_FLASH_RDID, 0),
|
||||
};
|
||||
return id;
|
||||
}
|
||||
|
||||
/*__ __
|
||||
| \/ |
|
||||
| \ / | ___ _ __ ___ ___ _ __ _ _
|
||||
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
|
||||
| | | | __/ | | | | | (_) | | | |_| |
|
||||
|_| |_|\___|_| |_| |_|\___/|_| \__, |
|
||||
__/ |
|
||||
|__*/
|
||||
uint32_t lt_get_ram_size() {
|
||||
return 256 * 1024;
|
||||
}
|
||||
|
||||
uint32_t lt_get_heap_size() {
|
||||
#if configDYNAMIC_HEAP_SIZE
|
||||
extern unsigned char _empty_ram;
|
||||
#if CFG_SOC_NAME == SOC_BK7231N
|
||||
return (0x00400000 + 192 * 1024) - (uint32_t)(&_empty_ram);
|
||||
#else
|
||||
return (0x00400000 + 256 * 1024) - (uint32_t)(&_empty_ram);
|
||||
#endif
|
||||
#else
|
||||
return configTOTAL_HEAP_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ____ _______
|
||||
/ __ \__ __|/\
|
||||
| | | | | | / \
|
||||
| | | | | | / /\ \
|
||||
| |__| | | |/ ____ \
|
||||
\____/ |_/_/ \*/
|
||||
|
||||
lt_ota_type_t lt_ota_get_type() {
|
||||
return OTA_TYPE_SINGLE;
|
||||
}
|
||||
|
||||
bool lt_ota_is_valid(uint8_t index) {
|
||||
if (index != 0)
|
||||
return false;
|
||||
// check download RBL
|
||||
// TODO: maybe check header CRC or even binary hashes
|
||||
uint32_t magic;
|
||||
lt_flash_read(FLASH_DOWNLOAD_OFFSET, (uint8_t *)&magic, 4);
|
||||
return magic == 0x004C4252; // "RBL\0", little-endian
|
||||
}
|
||||
|
||||
uint8_t lt_ota_dual_get_current() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t lt_ota_dual_get_stored() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool lt_ota_switch(bool revert) {
|
||||
if (!lt_ota_is_valid(0))
|
||||
// no valid "download" image
|
||||
// - return false when trying to activate
|
||||
// - return true when trying to revert
|
||||
return revert;
|
||||
if (revert) {
|
||||
// there's a valid "download" image, which has to be removed
|
||||
return lt_flash_erase_block(FLASH_DOWNLOAD_OFFSET);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*_ __ _ _ _
|
||||
\ \ / / | | | | | |
|
||||
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
|
||||
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
|
||||
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
|
||||
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
|
||||
__/ |
|
||||
|___*/
|
||||
bool lt_wdt_enable(uint32_t timeout) {
|
||||
wdt_ctrl(WCMD_SET_PERIOD, &timeout);
|
||||
wdt_ctrl(WCMD_POWER_UP, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
void lt_wdt_disable() {
|
||||
wdt_ctrl(WCMD_POWER_DOWN, NULL);
|
||||
}
|
||||
|
||||
void lt_wdt_feed() {
|
||||
wdt_ctrl(WCMD_RELOAD_PERIOD, NULL);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
|
||||
|
||||
#include "lt_family_api.h"
|
||||
|
||||
#include <start_type_pub.h>
|
||||
|
||||
extern int uart_print_port;
|
||||
|
||||
void lt_init_family() {
|
||||
// set default UART output port
|
||||
uart_print_port = LT_UART_DEFAULT_PORT - 1;
|
||||
}
|
||||
|
||||
ResetReason lt_get_reset_reason() {
|
||||
switch (bk_misc_get_start_type()) {
|
||||
case RESET_SOURCE_POWERON:
|
||||
return RESET_REASON_POWER;
|
||||
case RESET_SOURCE_REBOOT:
|
||||
return RESET_REASON_SOFTWARE;
|
||||
case RESET_SOURCE_WATCHDOG:
|
||||
return RESET_REASON_WATCHDOG;
|
||||
case RESET_SOURCE_CRASH_XAT0:
|
||||
case RESET_SOURCE_CRASH_UNDEFINED:
|
||||
case RESET_SOURCE_CRASH_PREFETCH_ABORT:
|
||||
case RESET_SOURCE_CRASH_DATA_ABORT:
|
||||
case RESET_SOURCE_CRASH_UNUSED:
|
||||
case RESET_SOURCE_CRASH_PER_XAT0:
|
||||
return RESET_REASON_CRASH;
|
||||
case RESET_SOURCE_DEEPPS_GPIO:
|
||||
case RESET_SOURCE_DEEPPS_RTC:
|
||||
case RESET_SOURCE_DEEPPS_USB:
|
||||
return RESET_REASON_SLEEP;
|
||||
default:
|
||||
return RESET_REASON_UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ static int write(long offset, const uint8_t *buf, size_t size) {
|
||||
|
||||
static int erase(long offset, size_t size) {
|
||||
unprotect();
|
||||
offset &= ~(FLASH_ERASE_MIN_SIZE - 1);
|
||||
size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
uint32_t addr = offset + i * FLASH_ERASE_MIN_SIZE;
|
||||
|
||||
@@ -11,22 +11,22 @@ extern "C" {
|
||||
// other includes
|
||||
#include <flash_pub.h>
|
||||
#include <gpio_pub.h>
|
||||
#include <param_config.h>
|
||||
#include <start_type_pub.h>
|
||||
#include <sys_ctrl.h>
|
||||
#include <sys_rtos.h>
|
||||
#include <uart_pub.h>
|
||||
#include <wdt_pub.h>
|
||||
#include <wlan_ui_pub.h>
|
||||
|
||||
extern uint8_t system_mac[];
|
||||
extern uint32_t wdt_ctrl(uint32_t cmd, void *param);
|
||||
extern int uart_print_port;
|
||||
|
||||
// conflict with stl_algobase.h
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
// make non-SDK code call the proper printf()
|
||||
#undef bk_printf
|
||||
#undef os_printf
|
||||
#undef warning_prf
|
||||
#undef fatal_prf
|
||||
#define bk_printf printf
|
||||
#define os_printf printf
|
||||
#define warning_prf printf
|
||||
#define fatal_prf printf
|
||||
|
||||
// from fixups/arch_main.c
|
||||
extern unsigned char __bk_rf_is_init;
|
||||
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
|
||||
|
||||
#include "LT.h"
|
||||
|
||||
/**
|
||||
* @brief Get LibreTuya version string.
|
||||
*/
|
||||
const char *LibreTuya::getVersion() {
|
||||
return LT_VERSION_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get board name.
|
||||
*/
|
||||
const char *LibreTuya::getBoard() {
|
||||
return LT_BOARD_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get CPU family ID.
|
||||
*/
|
||||
ChipFamily LibreTuya::getChipFamily() {
|
||||
return FAMILY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get CPU family name as string.
|
||||
*/
|
||||
const char *LibreTuya::getChipFamilyName() {
|
||||
return STRINGIFY_MACRO(FAMILY) + 2;
|
||||
}
|
||||
|
||||
static char *deviceName = NULL;
|
||||
|
||||
/**
|
||||
* @brief Get device friendly name in format "LT-<board>-<chip id>".
|
||||
* Can be used as hostname.
|
||||
*/
|
||||
const char *LibreTuya::getDeviceName() {
|
||||
if (deviceName)
|
||||
return deviceName;
|
||||
uint32_t chipId = getChipId();
|
||||
uint8_t *id = (uint8_t *)&chipId;
|
||||
|
||||
const char *board = getBoard();
|
||||
uint8_t boardLen = strlen(board);
|
||||
deviceName = (char *)malloc(3 + boardLen + 1 + 6 + 1);
|
||||
|
||||
sprintf(deviceName, "LT-%s-%02x%02x%02x", board, id[0], id[1], id[2]);
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the reason of last chip reset.
|
||||
*/
|
||||
ResetReason LibreTuya::getResetReason() {
|
||||
return lt_get_reset_reason();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a textual representation of a reset reason.
|
||||
*
|
||||
* @param reason value to convert to text, uses getResetReason() by default
|
||||
*/
|
||||
const char *LibreTuya::getResetReasonName(ResetReason reason) {
|
||||
if (reason >= RESET_REASON_MAX)
|
||||
reason = getResetReason();
|
||||
switch (reason) {
|
||||
case RESET_REASON_POWER:
|
||||
return "Power-On";
|
||||
case RESET_REASON_BROWNOUT:
|
||||
return "Brownout";
|
||||
case RESET_REASON_HARDWARE:
|
||||
return "HW Reboot";
|
||||
case RESET_REASON_SOFTWARE:
|
||||
return "SW Reboot";
|
||||
case RESET_REASON_WATCHDOG:
|
||||
return "WDT Reset";
|
||||
case RESET_REASON_CRASH:
|
||||
return "Crash";
|
||||
case RESET_REASON_SLEEP:
|
||||
return "Sleep Wakeup";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get CPU frequency in MHz.
|
||||
*/
|
||||
uint32_t LibreTuya::getCpuFreqMHz() {
|
||||
return getCpuFreq() / 1000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get flash chip total size.
|
||||
* The default implementation uses the least significant
|
||||
* byte of the chip ID to determine the size.
|
||||
*/
|
||||
__attribute__((weak)) uint32_t LibreTuya::getFlashChipSize() {
|
||||
FlashId id = getFlashChipId();
|
||||
if (id.chipSizeId >= 0x14 && id.chipSizeId <= 0x19) {
|
||||
return (1 << id.chipSizeId);
|
||||
}
|
||||
#ifdef FLASH_LENGTH
|
||||
return FLASH_LENGTH;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the OTA index for updated firmware.
|
||||
*
|
||||
* Note: returns 1 for chips without dual-OTA.
|
||||
*/
|
||||
uint8_t LibreTuya::otaGetTarget() {
|
||||
if (!otaSupportsDual())
|
||||
return 1;
|
||||
return otaGetRunning() ^ 0b11;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform OTA rollback: switch to the previous image, or abort current
|
||||
* switched OTA update, if not rebooted yet.
|
||||
*
|
||||
* @return false if no second image to run, writing failed or dual-OTA not supported
|
||||
*/
|
||||
bool LibreTuya::otaRollback() {
|
||||
if (!otaCanRollback())
|
||||
return false;
|
||||
if (otaGetRunning() != otaGetStoredIndex())
|
||||
// force switching back to current image
|
||||
return otaSwitch(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if OTA rollback is supported and available (there is another image to run).
|
||||
* @return false if no second image to run or dual-OTA not supported
|
||||
*/
|
||||
bool LibreTuya::otaCanRollback() {
|
||||
if (!otaSupportsDual())
|
||||
return false;
|
||||
if (otaGetRunning() == otaGetStoredIndex())
|
||||
return true;
|
||||
if (otaGetRunning() == 1 && otaHasImage1())
|
||||
return true;
|
||||
if (otaGetRunning() == 2 && otaHasImage2())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void LibreTuya::gpioRecover() {
|
||||
// nop by default
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* @brief Flash chip ID structure.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t manufacturerId;
|
||||
uint8_t chipId;
|
||||
uint8_t chipSizeId;
|
||||
} FlashId;
|
||||
|
||||
/**
|
||||
* @brief Main LibreTuya API class.
|
||||
*
|
||||
* This class contains all functions common amongst all families.
|
||||
* Implementations of these methods may vary between families.
|
||||
*
|
||||
* The class is accessible using the `LT` global object (defined by the family).
|
||||
*/
|
||||
class LibreTuya {
|
||||
public: /* Common methods - note: these are documented in LibreTuyaAPI.cpp */
|
||||
const char *getVersion();
|
||||
const char *getBoard();
|
||||
ChipFamily getChipFamily();
|
||||
const char *getChipFamilyName();
|
||||
const char *getDeviceName();
|
||||
ResetReason getResetReason();
|
||||
const char *getResetReasonName(ResetReason reason = RESET_REASON_MAX);
|
||||
uint32_t getCpuFreqMHz();
|
||||
uint32_t getFlashChipSize();
|
||||
uint8_t otaGetTarget();
|
||||
bool otaRollback();
|
||||
bool otaCanRollback();
|
||||
|
||||
public: /* Compatibility methods */
|
||||
/**
|
||||
* @brief Alias of getMaxAllocHeap().
|
||||
*/
|
||||
inline uint32_t getMaxFreeBlockSize() {
|
||||
return getMaxAllocHeap();
|
||||
}
|
||||
|
||||
public: /* Family-defined methods */
|
||||
/**
|
||||
* @brief Reboot the CPU.
|
||||
*/
|
||||
void restart();
|
||||
/**
|
||||
* @brief Reboot the CPU and stay in download mode (if possible).
|
||||
*/
|
||||
void restartDownloadMode();
|
||||
/**
|
||||
* @brief Reconfigure GPIO pins used for debugging
|
||||
* (SWD/JTAG), so that they can be used as normal I/O.
|
||||
*/
|
||||
void gpioRecover();
|
||||
|
||||
public: /* CPU-related */
|
||||
/**
|
||||
* @brief Get CPU model ID.
|
||||
*/
|
||||
ChipType getChipType();
|
||||
/**
|
||||
* @brief Get CPU model name as string.
|
||||
*/
|
||||
const char *getChipModel();
|
||||
/**
|
||||
* @brief Get CPU unique ID. This may be based on MAC, eFuse, etc.
|
||||
* Note: the number should be 24-bit (with most significant byte being zero).
|
||||
*/
|
||||
uint32_t getChipId();
|
||||
/**
|
||||
* @brief Get CPU core count.
|
||||
*/
|
||||
uint8_t getChipCores();
|
||||
/**
|
||||
* @brief Get CPU core type name as string.
|
||||
*/
|
||||
const char *getChipCoreType();
|
||||
/**
|
||||
* @brief Get CPU frequency in Hz.
|
||||
*/
|
||||
uint32_t getCpuFreq();
|
||||
/**
|
||||
* @brief Get CPU cycle count.
|
||||
*/
|
||||
uint32_t getCycleCount();
|
||||
|
||||
public: /* Flash memory utilities */
|
||||
/**
|
||||
* @brief Read flash chip ID and return a FlashId struct.
|
||||
*/
|
||||
FlashId getFlashChipId();
|
||||
|
||||
public: /* Memory management */
|
||||
/**
|
||||
* @brief Get total RAM size.
|
||||
*/
|
||||
uint32_t getRamSize();
|
||||
/**
|
||||
* @brief Get total heap size.
|
||||
*/
|
||||
uint32_t getHeapSize();
|
||||
/**
|
||||
* @brief Get free heap size.
|
||||
*/
|
||||
uint32_t getFreeHeap();
|
||||
/**
|
||||
* @brief Get lowest level of free heap memory.
|
||||
*/
|
||||
uint32_t getMinFreeHeap();
|
||||
/**
|
||||
* @brief Get largest block of heap that can be allocated at once.
|
||||
*/
|
||||
uint32_t getMaxAllocHeap();
|
||||
|
||||
public: /* OTA-related */
|
||||
/**
|
||||
* @brief Get the currently running firmware OTA index.
|
||||
*/
|
||||
uint8_t otaGetRunning();
|
||||
/**
|
||||
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
|
||||
*/
|
||||
uint8_t otaGetStoredIndex();
|
||||
/**
|
||||
* @brief Check if the chip supports dual-OTA (i.e. OTA is flashed to a different partition).
|
||||
*
|
||||
* TODO: make this work for actual dual-OTA chips; remove checking this in otaGetTarget() etc.
|
||||
*/
|
||||
bool otaSupportsDual();
|
||||
/**
|
||||
* @brief Check if OTA1 image is valid.
|
||||
*/
|
||||
bool otaHasImage1();
|
||||
/**
|
||||
* @brief Check if OTA2 image is valid.
|
||||
*/
|
||||
bool otaHasImage2();
|
||||
/**
|
||||
* @brief Try to switch OTA index to the other image.
|
||||
*
|
||||
* Note: should return true for chips without dual-OTA. Should return false if one of two images is not valid.
|
||||
*
|
||||
* @param force switch even if other image already marked as active
|
||||
* @return false if writing failed; true otherwise
|
||||
*/
|
||||
bool otaSwitch(bool force = false);
|
||||
|
||||
public: /* Watchdog */
|
||||
/**
|
||||
* @brief Enable the hardware watchdog.
|
||||
*
|
||||
* @param timeout watchdog timeout, milliseconds (defaults to 10s)
|
||||
* @return whether the chip has a hardware watchdog
|
||||
*/
|
||||
bool wdtEnable(uint32_t timeout = 10000);
|
||||
/**
|
||||
* @brief Disable the hardware watchdog.
|
||||
*/
|
||||
void wdtDisable();
|
||||
/**
|
||||
* @brief Feed/reset the hardware watchdog timer.
|
||||
*/
|
||||
void wdtFeed();
|
||||
};
|
||||
|
||||
extern LibreTuya LT;
|
||||
extern LibreTuya ESP;
|
||||
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
|
||||
|
||||
#include "Flash.h"
|
||||
|
||||
extern "C" {
|
||||
#include <fal.h>
|
||||
}
|
||||
|
||||
// Global Flash object.
|
||||
FlashClass Flash;
|
||||
|
||||
FlashId FlashClass::getChipId() {
|
||||
return LT.getFlashChipId();
|
||||
}
|
||||
|
||||
uint32_t FlashClass::getSize() {
|
||||
return LT.getFlashChipSize();
|
||||
}
|
||||
|
||||
bool FlashClass::eraseSector(uint32_t offset) {
|
||||
return fal_partition_erase(fal_root_part, offset, 1) >= 0;
|
||||
}
|
||||
|
||||
bool FlashClass::readBlock(uint32_t offset, uint8_t *data, size_t size) {
|
||||
return fal_partition_read(fal_root_part, offset, data, size) >= 0;
|
||||
}
|
||||
|
||||
bool FlashClass::writeBlock(uint32_t offset, uint8_t *data, size_t size) {
|
||||
return fal_partition_write(fal_root_part, offset, data, size) >= 0;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class FlashClass {
|
||||
public:
|
||||
FlashId getChipId();
|
||||
uint32_t getSize();
|
||||
|
||||
bool eraseSector(uint32_t offset);
|
||||
bool readBlock(uint32_t offset, uint8_t *data, size_t size);
|
||||
bool writeBlock(uint32_t offset, uint8_t *data, size_t size);
|
||||
};
|
||||
|
||||
extern FlashClass Flash;
|
||||
@@ -18,9 +18,9 @@ bool UpdateClass::begin(size_t size, int command, int unused2, uint8_t unused3,
|
||||
return false;
|
||||
cleanup();
|
||||
|
||||
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, trgt: %u", size, LT.otaGetRunning(), LT.otaGetTarget());
|
||||
LT_DM(OTA, "begin(%u, ...) / OTA curr: %u, scheme: %u", size, lt_ota_dual_get_current(), lt_ota_get_uf2_scheme());
|
||||
|
||||
ctx = uf2_ctx_init(LT.otaGetTarget(), FAMILY);
|
||||
ctx = uf2_ctx_init(lt_ota_get_uf2_scheme(), FAMILY);
|
||||
info = uf2_info_init();
|
||||
|
||||
if (!size) {
|
||||
@@ -54,8 +54,8 @@ bool UpdateClass::end(bool evenIfRemaining) {
|
||||
return false;
|
||||
}
|
||||
// TODO what is evenIfRemaining for?
|
||||
if (!LT.otaSwitch()) {
|
||||
// try to activate the second OTA
|
||||
if (!lt_ota_switch(/* revert= */ false)) {
|
||||
cleanup(UPDATE_ERROR_ACTIVATE);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include "Update.h"
|
||||
|
||||
extern "C" {
|
||||
#include <fal.h>
|
||||
}
|
||||
|
||||
static const uint8_t errorMap[] = {
|
||||
UPDATE_ERROR_OK, /* UF2_ERR_OK - no error */
|
||||
UPDATE_ERROR_OK, /* UF2_ERR_IGNORE - block should be ignored */
|
||||
@@ -9,9 +13,9 @@ static const uint8_t errorMap[] = {
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_FAMILY - family ID mismatched */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_NOT_HEADER - block is not a header */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_OTA_VER - unknown/invalid OTA format version */
|
||||
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA index */
|
||||
UPDATE_ERROR_MAGIC_BYTE, /* UF2_ERR_OTA_WRONG - no data for current OTA scheme */
|
||||
UPDATE_ERROR_NO_PARTITION, /* UF2_ERR_PART_404 - no partition with that name */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_ONE - only one partition tag in a block */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_INVALID - invalid partition info tag */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_PART_UNSET - attempted to write without target partition */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_DATA_TOO_LONG - data too long - tags won't fit */
|
||||
UPDATE_ERROR_BAD_ARGUMENT, /* UF2_ERR_SEQ_MISMATCH - sequence number mismatched */
|
||||
@@ -108,10 +112,9 @@ void UpdateClass::printErrorContext1() {
|
||||
if (ctx)
|
||||
LT_EM(
|
||||
OTA,
|
||||
"- ctx: seq=%u, part1=%s, part2=%s",
|
||||
"- ctx: seq=%u, part=%s",
|
||||
ctx->seq - 1, // print last parsed block seq
|
||||
ctx->part1 ? ctx->part1->name : NULL,
|
||||
ctx->part2 ? ctx->part2->name : NULL
|
||||
ctx->part ? ctx->part->name : NULL
|
||||
);
|
||||
|
||||
uf2_block_t *block = (uf2_block_t *)buf;
|
||||
@@ -176,15 +179,17 @@ const char *UpdateClass::getBoardName() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See LT.otaCanRollback() for more info.
|
||||
* @copydoc lt_ota_can_rollback()
|
||||
*/
|
||||
bool UpdateClass::canRollBack() {
|
||||
return LT.otaCanRollback();
|
||||
return lt_ota_can_rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief See LT.otaRollback() for more info.
|
||||
*/
|
||||
bool UpdateClass::rollBack() {
|
||||
return LT.otaRollback();
|
||||
if (!lt_ota_can_rollback())
|
||||
return false;
|
||||
return lt_ota_switch(false);
|
||||
}
|
||||
|
||||
2
cores/common/arduino/libraries/inline/.clang-format
Normal file
2
cores/common/arduino/libraries/inline/.clang-format
Normal file
@@ -0,0 +1,2 @@
|
||||
BasedOnStyle: InheritParentConfig
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
38
cores/common/arduino/libraries/inline/Flash/Flash.h
Normal file
38
cores/common/arduino/libraries/inline/Flash/Flash.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
class FlashClass {
|
||||
public:
|
||||
/** @copydoc lt_flash_get_id() */
|
||||
inline FlashId getChipId() { return lt_flash_get_id(); }
|
||||
|
||||
/** @copydoc lt_flash_get_size() */
|
||||
inline uint32_t getSize() { return lt_flash_get_size(); }
|
||||
|
||||
/** @copydoc lt_flash_erase_block() */
|
||||
inline bool eraseSector(uint32_t offset) {
|
||||
//
|
||||
return lt_flash_erase_block(offset);
|
||||
}
|
||||
|
||||
/** @copydoc lt_flash_read() */
|
||||
inline bool readBlock(uint32_t offset, uint8_t *data, size_t length) {
|
||||
//
|
||||
return lt_flash_read(offset, data, length);
|
||||
}
|
||||
|
||||
/** @copydoc lt_flash_write() */
|
||||
inline bool writeBlock(uint32_t offset, uint8_t *data, size_t length) {
|
||||
//
|
||||
return lt_flash_write(offset, data, length);
|
||||
}
|
||||
};
|
||||
|
||||
extern FlashClass Flash;
|
||||
|
||||
#endif
|
||||
113
cores/common/arduino/libraries/inline/LT/LT.h
Normal file
113
cores/common/arduino/libraries/inline/LT/LT.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <OTA.h>
|
||||
#include <WDT.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define ChipFamily lt_cpu_family_t
|
||||
#define ChipType lt_cpu_model_t
|
||||
#define ResetReason lt_reboot_reason_t
|
||||
#define FlashId lt_flash_id_t
|
||||
|
||||
/**
|
||||
* @brief Main LibreTuya API class.
|
||||
*
|
||||
* Since v1.0.0, this class only consists of inline functions, which
|
||||
* wrap the LibreTuya C API (lt_api.h). Refer to the docs of the C API
|
||||
* for more information.
|
||||
*
|
||||
* The class is accessible using the `LT` global object.
|
||||
*/
|
||||
class LibreTuya {
|
||||
public: /* lt_cpu.h */
|
||||
/** @copydoc lt_get_cpu_family() */
|
||||
inline ChipFamily getChipFamily() { return lt_get_cpu_family(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_family_name() */
|
||||
inline const char *getChipFamilyName() { return lt_get_cpu_family_name(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_model() */
|
||||
inline ChipType getChipType() { return lt_get_cpu_model(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_model_name() */
|
||||
inline const char *getChipModel() { return lt_get_cpu_model_name(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_mac_id() */
|
||||
inline uint32_t getChipId() { return lt_get_cpu_mac_id(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_core_count() */
|
||||
inline uint8_t getChipCores() { return lt_get_cpu_core_count(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_core_type() */
|
||||
inline const char *getChipCoreType() { return lt_get_cpu_core_type(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_freq() */
|
||||
inline uint32_t getCpuFreq() { return lt_get_cpu_freq(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_freq_mhz() */
|
||||
inline uint32_t getCpuFreqMHz() { return lt_get_cpu_freq_mhz(); }
|
||||
|
||||
/** @copydoc lt_get_cpu_cycle_count() */
|
||||
inline uint32_t getCycleCount() { return lt_get_cpu_cycle_count(); }
|
||||
|
||||
public: /* lt_device.h */
|
||||
/** @copydoc lt_get_version() */
|
||||
inline const char *getVersion() { return lt_get_version(); }
|
||||
|
||||
/** @copydoc lt_get_board_code() */
|
||||
inline const char *getBoard() { return lt_get_board_code(); }
|
||||
|
||||
/** @copydoc lt_get_device_name() */
|
||||
inline const char *getDeviceName() { return lt_get_device_name(); }
|
||||
|
||||
/** @copydoc lt_reboot() */
|
||||
inline void restart() { lt_reboot(); }
|
||||
|
||||
/** @copydoc lt_reboot_download_mode() */
|
||||
inline void restartDownloadMode() { lt_reboot_download_mode(); }
|
||||
|
||||
/** @copydoc lt_get_reboot_reason() */
|
||||
inline ResetReason getResetReason() { return lt_get_reboot_reason(); }
|
||||
|
||||
/** @copydoc lt_get_reboot_reason_name() */
|
||||
inline const char *getResetReasonName(ResetReason reason = lt_get_reboot_reason()) {
|
||||
return lt_get_reboot_reason_name(reason);
|
||||
}
|
||||
|
||||
/** @copydoc lt_gpio_recover(); */
|
||||
inline void gpioRecover() { lt_gpio_recover(); }
|
||||
|
||||
public: /* lt_flash.h */
|
||||
/** @copydoc lt_flash_get_id() */
|
||||
inline FlashId getFlashChipId() { return lt_flash_get_id(); }
|
||||
|
||||
/** @copydoc lt_flash_get_size() */
|
||||
inline uint32_t getFlashChipSize() { return lt_flash_get_size(); }
|
||||
|
||||
public: /* lt_mem.h */
|
||||
/** @copydoc lt_get_ram_size() */
|
||||
inline uint32_t getRamSize() { return lt_get_ram_size(); }
|
||||
|
||||
/** @copydoc lt_get_heap_size() */
|
||||
inline uint32_t getHeapSize() { return lt_get_heap_size(); }
|
||||
|
||||
/** @copydoc lt_get_heap_free() */
|
||||
inline uint32_t getFreeHeap() { return lt_get_heap_free(); }
|
||||
|
||||
/** @copydoc lt_get_heap_min_free() */
|
||||
inline uint32_t getMinFreeHeap() { return lt_get_heap_min_free(); }
|
||||
|
||||
/** @copydoc lt_get_heap_max_alloc() */
|
||||
inline uint32_t getMaxAllocHeap() { return lt_get_heap_max_alloc(); }
|
||||
|
||||
/** @copydoc lt_get_heap_max_alloc() */
|
||||
inline uint32_t getMaxFreeBlockSize() { return lt_get_heap_max_alloc(); }
|
||||
};
|
||||
|
||||
extern LibreTuya LT;
|
||||
|
||||
#endif
|
||||
44
cores/common/arduino/libraries/inline/OTA/OTA.h
Normal file
44
cores/common/arduino/libraries/inline/OTA/OTA.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* @brief Over-the-Air updates helper class.
|
||||
*
|
||||
* This class only consists of inline functions, which
|
||||
* wrap the LibreTuya C API (lt_api.h). Refer to the docs of the C API
|
||||
* for more information.
|
||||
*
|
||||
* The class is accessible using the `OTA` global object.
|
||||
*/
|
||||
class LibreTuyaOTA {
|
||||
public: /* lt_ota.h */
|
||||
/** @copydoc lt_ota_get_type() */
|
||||
inline lt_ota_type_t getType() { return lt_ota_get_type(); }
|
||||
|
||||
/** @copydoc lt_ota_is_valid() */
|
||||
inline bool isValid(uint8_t index) { return lt_ota_is_valid(index); }
|
||||
|
||||
/** @copydoc lt_ota_can_rollback() */
|
||||
inline bool canRollback() { return lt_ota_can_rollback(); }
|
||||
|
||||
/** @copydoc lt_ota_dual_get_current() */
|
||||
inline uint8_t getCurrentIndex() { return lt_ota_dual_get_current(); }
|
||||
|
||||
/** @copydoc lt_ota_dual_get_stored() */
|
||||
inline uint8_t getStoredIndex() { return lt_ota_dual_get_stored(); }
|
||||
|
||||
/** @copydoc lt_ota_get_uf2_scheme() */
|
||||
inline uf2_ota_scheme_t getUF2Scheme() { return lt_ota_get_uf2_scheme(); }
|
||||
|
||||
/** @copydoc lt_ota_switch() */
|
||||
inline bool switchImage(bool revert = false) { return lt_ota_switch(revert); }
|
||||
};
|
||||
|
||||
extern LibreTuyaOTA OTA;
|
||||
|
||||
#endif
|
||||
10
cores/common/arduino/libraries/inline/Singletons.cpp
Normal file
10
cores/common/arduino/libraries/inline/Singletons.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */
|
||||
|
||||
#include <LT.h>
|
||||
|
||||
#include <Flash.h>
|
||||
|
||||
LibreTuya LT;
|
||||
LibreTuyaOTA OTA;
|
||||
LibreTuyaWDT WDT;
|
||||
FlashClass Flash;
|
||||
32
cores/common/arduino/libraries/inline/WDT/WDT.h
Normal file
32
cores/common/arduino/libraries/inline/WDT/WDT.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/**
|
||||
* @brief Watchdog control class.
|
||||
*
|
||||
* This class only consists of inline functions, which
|
||||
* wrap the LibreTuya C API (lt_api.h). Refer to the docs of the C API
|
||||
* for more information.
|
||||
*
|
||||
* The class is accessible using the `WDT` global object.
|
||||
*/
|
||||
class LibreTuyaWDT {
|
||||
public: /* lt_wdt.h */
|
||||
/** @copydoc lt_wdt_enable() */
|
||||
inline bool enable(uint32_t timeout = 10000) { return lt_wdt_enable(timeout); }
|
||||
|
||||
/** @copydoc lt_wdt_disable() */
|
||||
inline void disable() { lt_wdt_disable(); }
|
||||
|
||||
/** @copydoc lt_wdt_feed() */
|
||||
inline void feed() { lt_wdt_feed(); }
|
||||
};
|
||||
|
||||
extern LibreTuyaWDT WDT;
|
||||
|
||||
#endif
|
||||
67
cores/common/base/api/lt_cpu.h
Normal file
67
cores/common/base/api/lt_cpu.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
/**
|
||||
* @brief Get CPU family ID (in ChipFamily enumeration).
|
||||
*/
|
||||
lt_cpu_family_t lt_get_cpu_family();
|
||||
|
||||
/**
|
||||
* @brief Get CPU family name as string.
|
||||
*/
|
||||
const char *lt_get_cpu_family_name();
|
||||
|
||||
/**
|
||||
* @brief Get CPU model ID (in ChipType enumeration).
|
||||
*/
|
||||
lt_cpu_model_t lt_get_cpu_model();
|
||||
|
||||
/**
|
||||
* @brief Get CPU model name as string (uppercase).
|
||||
*/
|
||||
const char *lt_get_cpu_model_name();
|
||||
|
||||
/**
|
||||
* @brief Get CPU model name as string (lowercase).
|
||||
*/
|
||||
const char *lt_get_cpu_model_code();
|
||||
|
||||
/**
|
||||
* @brief Get CPU unique ID. This may be based on MAC, eFuse, etc. (family-specific).
|
||||
* Note: the number is 24-bit (with the MSB being zero).
|
||||
*/
|
||||
uint32_t lt_get_cpu_unique_id();
|
||||
|
||||
/**
|
||||
* @brief Get CPU ID based on the last three octets of MAC address.
|
||||
* Note: the number is 24-bit (with the MSB being zero).
|
||||
*/
|
||||
uint32_t lt_get_cpu_mac_id();
|
||||
|
||||
/**
|
||||
* @brief Get CPU core count.
|
||||
*/
|
||||
uint8_t lt_get_cpu_core_count();
|
||||
|
||||
/**
|
||||
* @brief Get CPU core type name as string.
|
||||
*/
|
||||
const char *lt_get_cpu_core_type();
|
||||
|
||||
/**
|
||||
* @brief Get CPU frequency in Hz.
|
||||
*/
|
||||
uint32_t lt_get_cpu_freq();
|
||||
|
||||
/**
|
||||
* @brief Get CPU frequency in MHz.
|
||||
*/
|
||||
uint32_t lt_get_cpu_freq_mhz();
|
||||
|
||||
/**
|
||||
* @brief Get CPU cycle count.
|
||||
*/
|
||||
uint32_t lt_get_cpu_cycle_count();
|
||||
58
cores/common/base/api/lt_device.h
Normal file
58
cores/common/base/api/lt_device.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
/**
|
||||
* @brief Get LibreTuya version string.
|
||||
*/
|
||||
const char *lt_get_version();
|
||||
|
||||
/**
|
||||
* @brief Get board code.
|
||||
*/
|
||||
const char *lt_get_board_code();
|
||||
|
||||
/**
|
||||
* @brief Get device friendly name in format "LT-<family code>-<MAC ID>".
|
||||
* Can be used as hostname.
|
||||
*/
|
||||
const char *lt_get_device_name();
|
||||
|
||||
/**
|
||||
* @brief Reboot the CPU.
|
||||
*/
|
||||
void lt_reboot();
|
||||
|
||||
/**
|
||||
* @brief Reboot the CPU with a watchdog timeout (if possible).
|
||||
*
|
||||
* @return whether WDT reboot is possible
|
||||
*/
|
||||
bool lt_reboot_wdt();
|
||||
|
||||
/**
|
||||
* @brief Reboot the CPU and stay in download mode (if possible).
|
||||
*
|
||||
* @return whether download-mode reboot is possible
|
||||
*/
|
||||
bool lt_reboot_download_mode();
|
||||
|
||||
/**
|
||||
* @brief Get the reason of last chip reboot.
|
||||
*/
|
||||
lt_reboot_reason_t lt_get_reboot_reason();
|
||||
|
||||
/**
|
||||
* @brief Get a textual representation of a reboot reason.
|
||||
*
|
||||
* @param reason value to convert to text, pass 0 to read from lt_reboot_get_reason()
|
||||
*/
|
||||
const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure GPIO pins used for debugging
|
||||
* (SWD/JTAG), so that they can be used as normal I/O.
|
||||
*/
|
||||
void lt_gpio_recover();
|
||||
55
cores/common/base/api/lt_flash.h
Normal file
55
cores/common/base/api/lt_flash.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
/**
|
||||
* @brief Read flash chip ID and return a lt_flash_id_t struct.
|
||||
*/
|
||||
lt_flash_id_t lt_flash_get_id();
|
||||
|
||||
/**
|
||||
* @brief Get flash chip total size.
|
||||
*
|
||||
* The default implementation uses the least significant
|
||||
* byte of the chip ID to determine the size.
|
||||
*/
|
||||
uint32_t lt_flash_get_size();
|
||||
|
||||
/**
|
||||
* @brief Erase flash area. Flash can only be erased in blocks (usually 4 KiB).
|
||||
*
|
||||
* @param offset starting offset to erase (in bytes); must be multiple of the flash chip's block size
|
||||
* @param length length of data to erase (in bytes); will be rounded up to block size
|
||||
* @return whether erasing was successful
|
||||
*/
|
||||
bool lt_flash_erase(uint32_t offset, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Erase a single block of flash (usually 4 KiB).
|
||||
*
|
||||
* @param offset offset of the block (in bytes); must be multiple of the flash chip's block size
|
||||
* @return whether erasing was successful
|
||||
*/
|
||||
bool lt_flash_erase_block(uint32_t offset);
|
||||
|
||||
/**
|
||||
* @brief Read data from the flash.
|
||||
*
|
||||
* @param offset starting offset (in bytes)
|
||||
* @param data pointer to where to store the data
|
||||
* @param length length of data to read
|
||||
* @return whether reading was successful (i.e. all bytes were successfully read)
|
||||
*/
|
||||
bool lt_flash_read(uint32_t offset, uint8_t *data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Write data to the flash.
|
||||
*
|
||||
* @param offset starting offset (in bytes)
|
||||
* @param data pointer to data to write
|
||||
* @param length length of data to write
|
||||
* @return whether writing was successful (i.e. all bytes were successfully written)
|
||||
*/
|
||||
bool lt_flash_write(uint32_t offset, uint8_t *data, size_t length);
|
||||
@@ -4,10 +4,6 @@
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
* @brief Initialize the family core (optional).
|
||||
* This method is family-specific; the family core can do whatever it wants to.
|
||||
@@ -28,12 +24,3 @@ void lt_init_variant() __attribute__((weak));
|
||||
* This method is empty if not implemented, and shouldn't be called manually.
|
||||
*/
|
||||
void lt_init_arduino() __attribute__((weak));
|
||||
|
||||
/**
|
||||
* @brief Get the reason of last chip reset.
|
||||
*/
|
||||
ResetReason lt_get_reset_reason();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
30
cores/common/base/api/lt_mem.h
Normal file
30
cores/common/base/api/lt_mem.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
/**
|
||||
* @brief Get total RAM size.
|
||||
*/
|
||||
uint32_t lt_get_ram_size();
|
||||
|
||||
/**
|
||||
* @brief Get total heap size.
|
||||
*/
|
||||
uint32_t lt_get_heap_size();
|
||||
|
||||
/**
|
||||
* @brief Get free heap size.
|
||||
*/
|
||||
uint32_t lt_get_heap_free();
|
||||
|
||||
/**
|
||||
* @brief Get lowest level of free heap memory.
|
||||
*/
|
||||
uint32_t lt_get_heap_min_free();
|
||||
|
||||
/**
|
||||
* @brief Get largest block of heap that can be allocated at once.
|
||||
*/
|
||||
uint32_t lt_get_heap_max_alloc();
|
||||
60
cores/common/base/api/lt_ota.h
Normal file
60
cores/common/base/api/lt_ota.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libretuya.h>
|
||||
#include <uf2ota/uf2types.h>
|
||||
|
||||
/**
|
||||
* @brief Get OTA type of the device's chip.
|
||||
*/
|
||||
lt_ota_type_t lt_ota_get_type();
|
||||
|
||||
/**
|
||||
* @brief Check if the specified OTA image is valid.
|
||||
*
|
||||
* @param index OTA index to check; 0 for single-OTA chips, 1 or 2 for dual-OTA chips
|
||||
* @return true if index is valid for the chip's OTA type, and there is a valid image; false otherwise
|
||||
*/
|
||||
bool lt_ota_is_valid(uint8_t index);
|
||||
|
||||
/**
|
||||
* @brief Check if OTA rollback is possible (switching the stored index to another partition).
|
||||
*
|
||||
* Note that this is not the same as "switching" OTA with revert=true.
|
||||
*
|
||||
* @return true if 2nd image is valid and the chip is dual-OTA; false otherwise
|
||||
*/
|
||||
bool lt_ota_can_rollback();
|
||||
|
||||
/**
|
||||
* @brief Get the currently running firmware's OTA index.
|
||||
*
|
||||
* @return OTA index if dual-OTA is supported, 0 otherwise
|
||||
*/
|
||||
uint8_t lt_ota_dual_get_current();
|
||||
|
||||
/**
|
||||
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
|
||||
*
|
||||
* @return OTA index if dual-OTA is supported, 0 otherwise
|
||||
*/
|
||||
uint8_t lt_ota_dual_get_stored();
|
||||
|
||||
/**
|
||||
* @brief Check which UF2 OTA scheme should be used for applying firmware updates.
|
||||
*
|
||||
* @return OTA scheme of the target partition
|
||||
*/
|
||||
uf2_ota_scheme_t lt_ota_get_uf2_scheme();
|
||||
|
||||
/**
|
||||
* @brief Try to switch OTA index to the other image. For single-OTA chips, only check if the upgrade image is valid.
|
||||
*
|
||||
* This can be used to "activate" the upgrade after flashing.
|
||||
*
|
||||
* @param revert switch if (and only if) the other image is already marked as active (i.e.
|
||||
* switch back to the running image)
|
||||
* @return false if the second image (or upgrade image) is not valid; false if writing failed; true otherwise
|
||||
*/
|
||||
bool lt_ota_switch(bool revert);
|
||||
@@ -4,10 +4,6 @@
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// https://stackoverflow.com/a/3437484
|
||||
#define MAX(a, b) \
|
||||
({ \
|
||||
@@ -22,14 +18,30 @@ extern "C" {
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
|
||||
/**
|
||||
* @brief Generate random bytes using rand().
|
||||
*
|
||||
* @param buf destination pointer
|
||||
* @param len how many bytes to generate
|
||||
*/
|
||||
void lt_rand_bytes(uint8_t *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Print data pointed to by buf in hexdump-like format (hex+ASCII).
|
||||
*
|
||||
* @param buf source pointer
|
||||
* @param len how many bytes to print
|
||||
* @param offset increment printed offset by this value
|
||||
* @param width how many bytes on a line
|
||||
*/
|
||||
void hexdump(
|
||||
const uint8_t *buf,
|
||||
size_t len,
|
||||
#ifdef __cplusplus
|
||||
void hexdump(const uint8_t *buf, size_t len, uint32_t offset = 0, uint8_t width = 16);
|
||||
uint32_t offset = 0,
|
||||
uint8_t width = 16
|
||||
#else
|
||||
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
uint32_t offset,
|
||||
uint8_t width
|
||||
#endif
|
||||
);
|
||||
23
cores/common/base/api/lt_wdt.h
Normal file
23
cores/common/base/api/lt_wdt.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libretuya.h>
|
||||
|
||||
/**
|
||||
* @brief Enable the hardware watchdog.
|
||||
*
|
||||
* @param timeout watchdog timeout, milliseconds
|
||||
* @return whether the chip has a hardware watchdog
|
||||
*/
|
||||
bool lt_wdt_enable(uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief Disable the hardware watchdog.
|
||||
*/
|
||||
void lt_wdt_disable();
|
||||
|
||||
/**
|
||||
* @brief Feed/reset the hardware watchdog timer.
|
||||
*/
|
||||
void lt_wdt_feed();
|
||||
@@ -42,5 +42,6 @@ extern const struct fal_flash_dev flash0;
|
||||
|
||||
/**
|
||||
* @brief "Root" partition entry, representing the entire flash.
|
||||
* Declared and initialized in lt_main.c.
|
||||
*/
|
||||
extern fal_partition_t fal_root_part;
|
||||
|
||||
@@ -36,16 +36,14 @@
|
||||
)
|
||||
|
||||
// Types & macros
|
||||
#include "lt_chip.h" // ChipType enum
|
||||
#include "lt_config.h" // platform configuration options
|
||||
#include "lt_types.h" // other types & enums
|
||||
#include "lt_types.h" // types & enums
|
||||
// Family-specific macros
|
||||
#include <lt_family.h>
|
||||
// Board variant (pin definitions)
|
||||
#include LT_VARIANT_H
|
||||
// APIs
|
||||
#include "lt_common_api.h" // common APIs
|
||||
#include "lt_family_api.h" // family-specific APIs
|
||||
#include "lt_api.h" // main API function definitions
|
||||
#include "lt_logger.h" // UART logger utility
|
||||
#include "lt_posix_api.h" // POSIX compat functions
|
||||
// printf silencing methods
|
||||
|
||||
264
cores/common/base/lt_api.c
Normal file
264
cores/common/base/lt_api.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
|
||||
|
||||
#include "lt_api.h"
|
||||
|
||||
#include <fal.h>
|
||||
|
||||
#if LT_HAS_FREERTOS
|
||||
#include <FreeRTOS.h>
|
||||
#include <task.h>
|
||||
#endif
|
||||
|
||||
/* _____ _____ _ _
|
||||
/ ____| __ \| | | |
|
||||
| | | |__) | | | |
|
||||
| | | ___/| | | |
|
||||
| |____| | | |__| |
|
||||
\_____|_| \____*/
|
||||
lt_cpu_family_t lt_get_cpu_family() {
|
||||
return FAMILY;
|
||||
}
|
||||
|
||||
const char *lt_get_cpu_family_name() {
|
||||
return STRINGIFY_MACRO(FAMILY) + 2;
|
||||
}
|
||||
|
||||
__attribute__((weak)) lt_cpu_model_t lt_get_cpu_model() {
|
||||
return MCU;
|
||||
}
|
||||
|
||||
const char *lt_get_cpu_model_name() {
|
||||
return STRINGIFY_MACRO(MCU);
|
||||
}
|
||||
|
||||
const char *lt_get_cpu_model_code() {
|
||||
return STRINGIFY_MACRO(MCULC);
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint8_t lt_get_cpu_core_count() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if LT_HAS_FREERTOS
|
||||
__attribute__((weak)) uint32_t lt_get_cpu_freq() {
|
||||
return configCPU_CLOCK_HZ;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t lt_get_cpu_freq_mhz() {
|
||||
return lt_get_cpu_freq() / 1000000;
|
||||
}
|
||||
|
||||
#if LT_HAS_FREERTOS
|
||||
__attribute__((weak)) uint32_t lt_get_cpu_cycle_count() {
|
||||
return xTaskGetTickCount() * (configCPU_CLOCK_HZ / configTICK_RATE_HZ);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*_____ _
|
||||
| __ \ (_)
|
||||
| | | | _____ ___ ___ ___
|
||||
| | | |/ _ \ \ / / |/ __/ _ \
|
||||
| |__| | __/\ V /| | (_| __/
|
||||
|_____/ \___| \_/ |_|\___\__*/
|
||||
static char *device_name = NULL;
|
||||
|
||||
const char *lt_get_version() {
|
||||
return LT_VERSION_STR;
|
||||
}
|
||||
|
||||
const char *lt_get_board_code() {
|
||||
return LT_BOARD_STR;
|
||||
}
|
||||
|
||||
const char *lt_get_device_name() {
|
||||
if (device_name)
|
||||
return device_name;
|
||||
uint32_t chip_id = lt_get_cpu_mac_id();
|
||||
uint8_t *id = (uint8_t *)&chip_id;
|
||||
|
||||
const char *model = lt_get_cpu_model_code();
|
||||
uint8_t model_len = strlen(model);
|
||||
device_name = (char *)malloc(3 + model_len + 1 + 6 + 1);
|
||||
|
||||
sprintf(device_name, "LT-%s-%02x%02x%02x", model, id[0], id[1], id[2]);
|
||||
return device_name;
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool lt_reboot_wdt() {
|
||||
if (!lt_wdt_enable(1L))
|
||||
return false;
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
__attribute__((weak)) bool lt_reboot_download_mode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *lt_get_reboot_reason_name(lt_reboot_reason_t reason) {
|
||||
if (!reason)
|
||||
reason = lt_get_reboot_reason();
|
||||
switch (reason) {
|
||||
case RESET_REASON_POWER:
|
||||
return "Power-On";
|
||||
case RESET_REASON_BROWNOUT:
|
||||
return "Brownout";
|
||||
case RESET_REASON_HARDWARE:
|
||||
return "HW Reboot";
|
||||
case RESET_REASON_SOFTWARE:
|
||||
return "SW Reboot";
|
||||
case RESET_REASON_WATCHDOG:
|
||||
return "WDT Reset";
|
||||
case RESET_REASON_CRASH:
|
||||
return "Crash";
|
||||
case RESET_REASON_SLEEP:
|
||||
return "Sleep Wakeup";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void lt_gpio_recover() {
|
||||
// nop by default
|
||||
}
|
||||
|
||||
/*______ _ _
|
||||
| ____| | | |
|
||||
| |__ | | __ _ ___| |__
|
||||
| __| | |/ _` / __| '_ \
|
||||
| | | | (_| \__ \ | | |
|
||||
|_| |_|\__,_|___/_| |*/
|
||||
__attribute__((weak)) uint32_t lt_flash_get_size() {
|
||||
lt_flash_id_t id = lt_flash_get_id();
|
||||
if (id.chip_size_id >= 0x14 && id.chip_size_id <= 0x19) {
|
||||
return (1 << id.chip_size_id);
|
||||
}
|
||||
#ifdef FLASH_LENGTH
|
||||
return FLASH_LENGTH;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool lt_flash_erase(uint32_t offset, size_t length) {
|
||||
return fal_partition_erase(fal_root_part, offset, length) >= 0;
|
||||
}
|
||||
|
||||
bool lt_flash_erase_block(uint32_t offset) {
|
||||
return fal_partition_erase(fal_root_part, offset, 1) >= 0;
|
||||
}
|
||||
|
||||
bool lt_flash_read(uint32_t offset, uint8_t *data, size_t length) {
|
||||
return fal_partition_write(fal_root_part, offset, data, length) == length;
|
||||
}
|
||||
|
||||
bool lt_flash_write(uint32_t offset, uint8_t *data, size_t length) {
|
||||
return fal_partition_read(fal_root_part, offset, data, length) == length;
|
||||
}
|
||||
|
||||
/*__ __
|
||||
| \/ |
|
||||
| \ / | ___ _ __ ___ ___ _ __ _ _
|
||||
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
|
||||
| | | | __/ | | | | | (_) | | | |_| |
|
||||
|_| |_|\___|_| |_| |_|\___/|_| \__, |
|
||||
__/ |
|
||||
|__*/
|
||||
#if LT_HAS_FREERTOS
|
||||
__attribute__((weak)) uint32_t lt_get_heap_size() {
|
||||
return configTOTAL_HEAP_SIZE;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t lt_get_heap_free() {
|
||||
return xPortGetFreeHeapSize();
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t lt_get_heap_min_free() {
|
||||
return xPortGetMinimumEverFreeHeapSize();
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((weak)) uint32_t lt_get_heap_max_alloc() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ____ _______
|
||||
/ __ \__ __|/\
|
||||
| | | | | | / \
|
||||
| | | | | | / /\ \
|
||||
| |__| | | |/ ____ \
|
||||
\____/ |_/_/ \*/
|
||||
bool lt_ota_can_rollback() {
|
||||
if (lt_ota_get_type() != OTA_TYPE_DUAL)
|
||||
return false;
|
||||
uint8_t current = lt_ota_dual_get_current();
|
||||
return lt_ota_is_valid(current ^ 0b11);
|
||||
}
|
||||
|
||||
uf2_ota_scheme_t lt_ota_get_uf2_scheme() {
|
||||
if (lt_ota_get_type() == OTA_TYPE_SINGLE)
|
||||
return UF2_SCHEME_DEVICE_SINGLE;
|
||||
uint8_t current = lt_ota_dual_get_current();
|
||||
// UF2_SCHEME_DEVICE_DUAL_1 or UF2_SCHEME_DEVICE_DUAL_2
|
||||
return (uf2_ota_scheme_t)(current ^ 0b11);
|
||||
}
|
||||
|
||||
/*_ _ _ _ _
|
||||
| | | | | (_) |
|
||||
| | | | |_ _| |___
|
||||
| | | | __| | / __|
|
||||
| |__| | |_| | \__ \
|
||||
\____/ \__|_|_|__*/
|
||||
void lt_rand_bytes(uint8_t *buf, size_t len) {
|
||||
int *data = (int *)buf;
|
||||
size_t i;
|
||||
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
|
||||
data[i++] = rand();
|
||||
}
|
||||
if (len) {
|
||||
int rem = rand();
|
||||
unsigned char *pRem = (unsigned char *)&rem;
|
||||
memcpy(buf + i * sizeof(int), pRem, len);
|
||||
}
|
||||
}
|
||||
|
||||
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
|
||||
uint16_t pos = 0;
|
||||
while (pos < len) {
|
||||
// print hex offset
|
||||
printf("%06lx ", offset + pos);
|
||||
// calculate current line width
|
||||
uint8_t lineWidth = MIN(width, len - pos);
|
||||
// print hexadecimal representation
|
||||
for (uint8_t i = 0; i < lineWidth; i++) {
|
||||
if (i % 8 == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%02x ", buf[pos + i]);
|
||||
}
|
||||
// print ascii representation
|
||||
printf(" |");
|
||||
for (uint8_t i = 0; i < lineWidth; i++) {
|
||||
char c = buf[pos + i];
|
||||
putchar((c >= 0x20 && c <= 0x7f) ? c : '.');
|
||||
}
|
||||
puts("|\r");
|
||||
pos += lineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/*_ __ _ _ _
|
||||
\ \ / / | | | | | |
|
||||
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
|
||||
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
|
||||
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
|
||||
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
|
||||
__/ |
|
||||
|___*/
|
||||
__attribute__((weak)) bool lt_wdt_enable(uint32_t timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void lt_wdt_disable() {}
|
||||
|
||||
__attribute__((weak)) void lt_wdt_feed() {}
|
||||
24
cores/common/base/lt_api.h
Normal file
24
cores/common/base/lt_api.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */
|
||||
|
||||
#pragma once
|
||||
|
||||
// This file collects all LibreTuya C API includes.
|
||||
// The functions are implemented in lt_api.c, which is located
|
||||
// in the common core, and in the family cores.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#include "api/lt_cpu.h"
|
||||
#include "api/lt_device.h"
|
||||
#include "api/lt_flash.h"
|
||||
#include "api/lt_init.h"
|
||||
#include "api/lt_mem.h"
|
||||
#include "api/lt_ota.h"
|
||||
#include "api/lt_utils.h"
|
||||
#include "api/lt_wdt.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CHIP_TYPE(family, chip_id) (((family >> 24) << 8) | chip_id)
|
||||
#define CHIP_TYPE_ENUM(family, chip_id) (ChipType) CHIP_TYPE(family, chip_id)
|
||||
|
||||
typedef enum {
|
||||
// used in UF2 Family ID
|
||||
F_RTL8710A = 0x9FFFD543, // Realtek Ameba1
|
||||
F_RTL8710B = 0x22E0D6FC, // Realtek AmebaZ (realtek-ambz)
|
||||
F_RTL8720C = 0xE08F7564, // Realtek AmebaZ2
|
||||
F_RTL8720D = 0x3379CFE2, // Realtek AmebaD
|
||||
F_BK7231U = 0x675A40B0, // Beken 7231U/7231T
|
||||
F_BK7231N = 0x7B3EF230, // Beken 7231N
|
||||
F_BK7251 = 0x6A82CC42, // Beken 7251/7252
|
||||
F_BL60X = 0xDE1270B7, // Boufallo 602
|
||||
} ChipFamily;
|
||||
|
||||
typedef enum {
|
||||
// Realtek AmebaZ
|
||||
// IDs copied from rtl8710b_efuse.h
|
||||
RTL8710BL = CHIP_TYPE(F_RTL8710B, 0xE0), // ???
|
||||
RTL8710BN = CHIP_TYPE(F_RTL8710B, 0xFF), // CHIPID_8710BN / QFN32
|
||||
RTL8710BU = CHIP_TYPE(F_RTL8710B, 0xFE), // CHIPID_8710BU / QFN48
|
||||
RTL8710BX = CHIP_TYPE(F_RTL8710B, 0xF6), // found on an actual RTL8710BX
|
||||
RTL8710L0 = CHIP_TYPE(F_RTL8710B, 0xFB), // CHIPID_8710BN_L0 / QFN32
|
||||
RTL8711BN = CHIP_TYPE(F_RTL8710B, 0xFD), // CHIPID_8711BN / QFN48
|
||||
RTL8711BU = CHIP_TYPE(F_RTL8710B, 0xFC), // CHIPID_8711BG / QFN68
|
||||
// Beken 72XX
|
||||
BK7231T = CHIP_TYPE(F_BK7231U, 0x1A), // *SCTRL_CHIP_ID = 0x7231a
|
||||
BK7231N = CHIP_TYPE(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
|
||||
BL2028N = CHIP_TYPE(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
|
||||
BK7252 = CHIP_TYPE(F_BK7251, 0x00), // TODO
|
||||
} ChipType;
|
||||
@@ -1,55 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */
|
||||
|
||||
#include "lt_common_api.h"
|
||||
|
||||
/**
|
||||
* @brief Generate random bytes using rand().
|
||||
*
|
||||
* @param buf destination pointer
|
||||
* @param len how many bytes to generate
|
||||
*/
|
||||
void lt_rand_bytes(uint8_t *buf, size_t len) {
|
||||
int *data = (int *)buf;
|
||||
size_t i;
|
||||
for (i = 0; len >= sizeof(int); len -= sizeof(int)) {
|
||||
data[i++] = rand();
|
||||
}
|
||||
if (len) {
|
||||
int rem = rand();
|
||||
unsigned char *pRem = (unsigned char *)&rem;
|
||||
memcpy(buf + i * sizeof(int), pRem, len);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print data pointed to by buf in hexdump-like format (hex+ASCII).
|
||||
*
|
||||
* @param buf source pointer
|
||||
* @param len how many bytes to print
|
||||
* @param offset increment printed offset by this value
|
||||
* @param width how many bytes on a line
|
||||
*/
|
||||
void hexdump(const uint8_t *buf, size_t len, uint32_t offset, uint8_t width) {
|
||||
uint16_t pos = 0;
|
||||
while (pos < len) {
|
||||
// print hex offset
|
||||
printf("%06lx ", offset + pos);
|
||||
// calculate current line width
|
||||
uint8_t lineWidth = MIN(width, len - pos);
|
||||
// print hexadecimal representation
|
||||
for (uint8_t i = 0; i < lineWidth; i++) {
|
||||
if (i % 8 == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%02x ", buf[pos + i]);
|
||||
}
|
||||
// print ascii representation
|
||||
printf(" |");
|
||||
for (uint8_t i = 0; i < lineWidth; i++) {
|
||||
char c = buf[pos + i];
|
||||
putchar((c >= 0x20 && c <= 0x7f) ? c : '.');
|
||||
}
|
||||
puts("|\r");
|
||||
pos += lineWidth;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ int lt_main(void) {
|
||||
// initialize C library
|
||||
__libc_init_array();
|
||||
// inform about the reset reason
|
||||
LT_I("Reset reason: %u", lt_get_reset_reason());
|
||||
LT_I("Reset reason: %u", lt_get_reboot_reason());
|
||||
// initialize FAL
|
||||
fal_init();
|
||||
// provide root partition
|
||||
|
||||
@@ -1,15 +1,79 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CPU_MODEL(family, chip_id) (((family >> 24) << 8) | chip_id)
|
||||
#define CPU_MODEL_ENUM(family, chip_id) (lt_cpu_model_t) CPU_MODEL(family, chip_id)
|
||||
|
||||
#define RESET_REASON_UNKNOWN REBOOT_REASON_UNKNOWN
|
||||
#define RESET_REASON_POWER REBOOT_REASON_POWER
|
||||
#define RESET_REASON_BROWNOUT REBOOT_REASON_BROWNOUT
|
||||
#define RESET_REASON_HARDWARE REBOOT_REASON_HARDWARE
|
||||
#define RESET_REASON_SOFTWARE REBOOT_REASON_SOFTWARE
|
||||
#define RESET_REASON_WATCHDOG REBOOT_REASON_WATCHDOG
|
||||
#define RESET_REASON_CRASH REBOOT_REASON_CRASH
|
||||
#define RESET_REASON_SLEEP REBOOT_REASON_SLEEP
|
||||
#define RESET_REASON_MAX REBOOT_REASON_MAX
|
||||
|
||||
typedef enum {
|
||||
RESET_REASON_UNKNOWN = 0,
|
||||
RESET_REASON_POWER = 1,
|
||||
RESET_REASON_BROWNOUT = 2,
|
||||
RESET_REASON_HARDWARE = 3,
|
||||
RESET_REASON_SOFTWARE = 4,
|
||||
RESET_REASON_WATCHDOG = 5,
|
||||
RESET_REASON_CRASH = 6,
|
||||
RESET_REASON_SLEEP = 7,
|
||||
RESET_REASON_MAX = 8,
|
||||
} ResetReason;
|
||||
F_RTL8710A = 0x9FFFD543, // Realtek Ameba1
|
||||
F_RTL8710B = 0x22E0D6FC, // Realtek AmebaZ (realtek-ambz)
|
||||
F_RTL8720C = 0xE08F7564, // Realtek AmebaZ2
|
||||
F_RTL8720D = 0x3379CFE2, // Realtek AmebaD
|
||||
F_BK7231U = 0x675A40B0, // Beken 7231U/7231T
|
||||
F_BK7231N = 0x7B3EF230, // Beken 7231N
|
||||
F_BK7251 = 0x6A82CC42, // Beken 7251/7252
|
||||
F_BL60X = 0xDE1270B7, // Boufallo 602
|
||||
} lt_cpu_family_t;
|
||||
|
||||
typedef enum {
|
||||
// Realtek AmebaZ
|
||||
// IDs copied from rtl8710b_efuse.h
|
||||
RTL8710BL = CPU_MODEL(F_RTL8710B, 0xE0), // ???
|
||||
RTL8710BN = CPU_MODEL(F_RTL8710B, 0xFF), // CHIPID_8710BN / QFN32
|
||||
RTL8710BU = CPU_MODEL(F_RTL8710B, 0xFE), // CHIPID_8710BU / QFN48
|
||||
RTL8710BX = CPU_MODEL(F_RTL8710B, 0xF6), // found on an actual RTL8710BX
|
||||
RTL8710L0 = CPU_MODEL(F_RTL8710B, 0xFB), // CHIPID_8710BN_L0 / QFN32
|
||||
RTL8711BN = CPU_MODEL(F_RTL8710B, 0xFD), // CHIPID_8711BN / QFN48
|
||||
RTL8711BU = CPU_MODEL(F_RTL8710B, 0xFC), // CHIPID_8711BG / QFN68
|
||||
// Beken 72XX
|
||||
BK7231T = CPU_MODEL(F_BK7231U, 0x1A), // *SCTRL_CHIP_ID = 0x7231a
|
||||
BK7231N = CPU_MODEL(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
|
||||
BL2028N = CPU_MODEL(F_BK7231N, 0x1C), // *SCTRL_CHIP_ID = 0x7231c
|
||||
BK7252 = CPU_MODEL(F_BK7251, 0x00), // TODO
|
||||
} lt_cpu_model_t;
|
||||
|
||||
/**
|
||||
* @brief Reset reason enumeration.
|
||||
*/
|
||||
typedef enum {
|
||||
REBOOT_REASON_UNKNOWN = 1,
|
||||
REBOOT_REASON_POWER = 2,
|
||||
REBOOT_REASON_BROWNOUT = 3,
|
||||
REBOOT_REASON_HARDWARE = 4,
|
||||
REBOOT_REASON_SOFTWARE = 5,
|
||||
REBOOT_REASON_WATCHDOG = 6,
|
||||
REBOOT_REASON_CRASH = 7,
|
||||
REBOOT_REASON_SLEEP = 8,
|
||||
REBOOT_REASON_MAX = 9,
|
||||
} lt_reboot_reason_t;
|
||||
|
||||
/**
|
||||
* @brief Flash chip ID structure.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t manufacturer_id;
|
||||
uint8_t chip_id;
|
||||
uint8_t chip_size_id;
|
||||
} lt_flash_id_t;
|
||||
|
||||
/**
|
||||
* @brief Chip's OTA type enumeration.
|
||||
*/
|
||||
typedef enum {
|
||||
OTA_TYPE_SINGLE = 0,
|
||||
OTA_TYPE_DUAL = 1,
|
||||
OTA_TYPE_FILE = 2,
|
||||
} lt_ota_type_t;
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */
|
||||
|
||||
#include <LT.h>
|
||||
#include <sdk_private.h>
|
||||
|
||||
#include <Flash.h>
|
||||
|
||||
void LibreTuya::restart() {
|
||||
// The Watchdog Way
|
||||
wdtEnable(1L);
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
void LibreTuya::restartDownloadMode() {
|
||||
// mww 0x40000138 0x8
|
||||
HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08);
|
||||
// reboot it the ugly way
|
||||
sys_reset();
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
void LibreTuya::gpioRecover() {
|
||||
// PA14 and PA15 are apparently unusable with SWD enabled
|
||||
sys_jtag_off();
|
||||
Pinmux_Config(PA_14, PINMUX_FUNCTION_GPIO);
|
||||
Pinmux_Config(PA_15, PINMUX_FUNCTION_GPIO);
|
||||
}
|
||||
|
||||
/* CPU-related */
|
||||
|
||||
ChipType LibreTuya::getChipType() {
|
||||
uint8_t chipId;
|
||||
EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE);
|
||||
return CHIP_TYPE_ENUM(FAMILY, chipId);
|
||||
}
|
||||
|
||||
const char *LibreTuya::getChipModel() {
|
||||
return STRINGIFY_MACRO(MCU);
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getChipId() {
|
||||
uint32_t chipId = 0;
|
||||
uint8_t *id = (uint8_t *)&chipId;
|
||||
// 9902 was extracted from ROM disassembly, probably not needed
|
||||
/* EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE);
|
||||
EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE);
|
||||
EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE); */
|
||||
// new method, based on EFUSE logical map
|
||||
uint8_t *efuse = (uint8_t *)malloc(512);
|
||||
// TODO do what EFUSE_LogicalMapRead() does, and read only the used data
|
||||
EFUSE_LogicalMap_Read(efuse);
|
||||
memcpy(id, efuse + 0x11A + 3, 3);
|
||||
free(efuse);
|
||||
return chipId;
|
||||
}
|
||||
|
||||
uint8_t LibreTuya::getChipCores() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *LibreTuya::getChipCoreType() {
|
||||
return "ARM Cortex-M4F";
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getCpuFreq() {
|
||||
return CPU_ClkGet(false);
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getCycleCount() {
|
||||
return microsecondsToClockCycles(micros());
|
||||
}
|
||||
|
||||
/* Flash memory utilities */
|
||||
|
||||
FlashId LibreTuya::getFlashChipId() {
|
||||
FlashId id;
|
||||
uint8_t idBytes[3];
|
||||
flash_read_id(NULL, idBytes, 3);
|
||||
id.manufacturerId = idBytes[0];
|
||||
id.chipId = idBytes[1];
|
||||
id.chipSizeId = idBytes[2];
|
||||
return id;
|
||||
}
|
||||
|
||||
/* Memory management */
|
||||
|
||||
uint32_t LibreTuya::getRamSize() {
|
||||
return 256 * 1024;
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getHeapSize() {
|
||||
return configTOTAL_HEAP_SIZE;
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getFreeHeap() {
|
||||
return xPortGetFreeHeapSize();
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getMinFreeHeap() {
|
||||
return xPortGetMinimumEverFreeHeapSize();
|
||||
}
|
||||
|
||||
uint32_t LibreTuya::getMaxAllocHeap() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OTA-related */
|
||||
|
||||
uint8_t LibreTuya::otaGetRunning() {
|
||||
// RTL8710B is XIP, so check the code offset in flash
|
||||
uint32_t addr = (uint32_t)lt_log;
|
||||
uint32_t offs = addr - SPI_FLASH_BASE;
|
||||
return offs > FLASH_OTA2_OFFSET ? 2 : 1;
|
||||
}
|
||||
|
||||
uint8_t LibreTuya::otaGetStoredIndex() {
|
||||
uint32_t *otaAddress = (uint32_t *)0x8009000;
|
||||
if (*otaAddress == 0xFFFFFFFF)
|
||||
return 1;
|
||||
uint32_t otaCounter = *((uint32_t *)0x8009004);
|
||||
// even count of zero-bits means OTA1, odd count means OTA2
|
||||
// this allows to switch OTA images by simply clearing next bits,
|
||||
// without needing to erase the flash
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
if ((otaCounter & (1 << i)) == 0)
|
||||
count++;
|
||||
}
|
||||
return 1 + (count % 2);
|
||||
}
|
||||
|
||||
bool LibreTuya::otaSupportsDual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaHasImage1() {
|
||||
uint8_t *ota1Addr = (uint8_t *)(SPI_FLASH_BASE + FLASH_OTA1_OFFSET);
|
||||
return memcmp(ota1Addr, "81958711", 8) == 0;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaHasImage2() {
|
||||
uint8_t *ota2Addr = (uint8_t *)(SPI_FLASH_BASE + FLASH_OTA2_OFFSET);
|
||||
return memcmp(ota2Addr, "81958711", 8) == 0;
|
||||
}
|
||||
|
||||
bool LibreTuya::otaSwitch(bool force) {
|
||||
if (!force && otaGetRunning() != otaGetStoredIndex())
|
||||
// OTA has already been switched
|
||||
return true;
|
||||
// - read current OTA switch value from 0x9004
|
||||
// - reset OTA switch to 0xFFFFFFFE if it's 0x0
|
||||
// - else check first non-zero bit of OTA switch
|
||||
// - write OTA switch with first non-zero bit cleared
|
||||
|
||||
if (!otaHasImage1() || !otaHasImage2())
|
||||
return false;
|
||||
|
||||
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
|
||||
if (value == 0) {
|
||||
uint8_t *system = (uint8_t *)malloc(64);
|
||||
Flash.readBlock(FLASH_SYSTEM_OFFSET, system, 64);
|
||||
// reset OTA switch
|
||||
((uint32_t *)system)[1] = -2;
|
||||
Flash.eraseSector(FLASH_SYSTEM_OFFSET);
|
||||
return Flash.writeBlock(FLASH_SYSTEM_OFFSET, system, 64);
|
||||
}
|
||||
|
||||
uint8_t i;
|
||||
// find first non-zero bit
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (value & (1 << i))
|
||||
break;
|
||||
}
|
||||
// clear the bit
|
||||
value &= ~(1 << i);
|
||||
// write OTA switch to flash
|
||||
flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Watchdog */
|
||||
|
||||
bool LibreTuya::wdtEnable(uint32_t timeout) {
|
||||
watchdog_init(timeout);
|
||||
watchdog_start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibreTuya::wdtDisable() {
|
||||
watchdog_stop();
|
||||
}
|
||||
|
||||
void LibreTuya::wdtFeed() {
|
||||
watchdog_refresh();
|
||||
}
|
||||
|
||||
/* Global instance */
|
||||
|
||||
LibreTuya LT;
|
||||
LibreTuya ESP = LT;
|
||||
8
cores/realtek-amb/arduino/src/lt_api.c
Normal file
8
cores/realtek-amb/arduino/src/lt_api.c
Normal file
@@ -0,0 +1,8 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <sdk_private.h>
|
||||
|
||||
uint32_t lt_get_cpu_cycle_count() {
|
||||
return microsecondsToClockCycles(micros());
|
||||
}
|
||||
219
cores/realtek-amb/base/lt_api.c
Normal file
219
cores/realtek-amb/base/lt_api.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
|
||||
|
||||
#include <libretuya.h>
|
||||
#include <sdk_private.h>
|
||||
|
||||
extern uint32_t GlobalDebugEnable;
|
||||
extern uint16_t GlobalDebugLevel;
|
||||
extern uint8_t GlobalPrivateLog;
|
||||
extern uint8_t lt_uart_port;
|
||||
|
||||
void lt_init_family() {
|
||||
// make the SDK less verbose by default
|
||||
GlobalDebugEnable = 0;
|
||||
GlobalPrivateLog = 0;
|
||||
lt_uart_port = LT_UART_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
/* _____ _____ _ _
|
||||
/ ____| __ \| | | |
|
||||
| | | |__) | | | |
|
||||
| | | ___/| | | |
|
||||
| |____| | | |__| |
|
||||
\_____|_| \____*/
|
||||
lt_cpu_model_t lt_get_cpu_model() {
|
||||
uint8_t chipId;
|
||||
EFUSE_OneByteReadROM(9902, 0xF8, &chipId, L25EOUTVOLTAGE);
|
||||
return CPU_MODEL_ENUM(FAMILY, chipId);
|
||||
}
|
||||
|
||||
uint32_t lt_get_cpu_unique_id() {
|
||||
return lt_get_cpu_mac_id();
|
||||
}
|
||||
|
||||
uint32_t lt_get_cpu_mac_id() {
|
||||
uint32_t chipId = 0;
|
||||
uint8_t *id = (uint8_t *)&chipId;
|
||||
// 9902 was extracted from ROM disassembly, probably not needed
|
||||
/* EFUSE_OneByteReadROM(9902, 0x3B, id + 0, L25EOUTVOLTAGE);
|
||||
EFUSE_OneByteReadROM(9902, 0x3C, id + 1, L25EOUTVOLTAGE);
|
||||
EFUSE_OneByteReadROM(9902, 0x3D, id + 2, L25EOUTVOLTAGE); */
|
||||
// new method, based on EFUSE logical map
|
||||
uint8_t *efuse = (uint8_t *)malloc(512);
|
||||
// TODO do what EFUSE_LogicalMapRead() does, and read only the used data
|
||||
EFUSE_LogicalMap_Read(efuse);
|
||||
memcpy(id, efuse + 0x11A + 3, 3);
|
||||
free(efuse);
|
||||
return chipId;
|
||||
}
|
||||
|
||||
const char *lt_get_cpu_core_type() {
|
||||
return "ARM Cortex-M4F";
|
||||
}
|
||||
|
||||
uint32_t lt_get_cpu_freq() {
|
||||
return CPU_ClkGet(false);
|
||||
}
|
||||
|
||||
/*_____ _
|
||||
| __ \ (_)
|
||||
| | | | _____ ___ ___ ___
|
||||
| | | |/ _ \ \ / / |/ __/ _ \
|
||||
| |__| | __/\ V /| | (_| __/
|
||||
|_____/ \___| \_/ |_|\___\__*/
|
||||
void lt_reboot() {
|
||||
// The Watchdog Way
|
||||
lt_wdt_enable(1L);
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
bool lt_reboot_download_mode() {
|
||||
// mww 0x40000138 0x8
|
||||
HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_NORESET_FF, 0x08);
|
||||
// reboot it the ugly way
|
||||
sys_reset();
|
||||
while (1) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
lt_reboot_reason_t lt_get_reboot_reason() {
|
||||
// TODO
|
||||
return REBOOT_REASON_UNKNOWN;
|
||||
}
|
||||
|
||||
void lt_gpio_recover() {
|
||||
// PA14 and PA15 are apparently unusable with SWD enabled
|
||||
sys_jtag_off();
|
||||
Pinmux_Config(PA_14, PINMUX_FUNCTION_GPIO);
|
||||
Pinmux_Config(PA_15, PINMUX_FUNCTION_GPIO);
|
||||
}
|
||||
|
||||
/*______ _ _
|
||||
| ____| | | |
|
||||
| |__ | | __ _ ___| |__
|
||||
| __| | |/ _` / __| '_ \
|
||||
| | | | (_| \__ \ | | |
|
||||
|_| |_|\__,_|___/_| |*/
|
||||
lt_flash_id_t lt_flash_get_id() {
|
||||
lt_flash_id_t id;
|
||||
uint8_t idBytes[3];
|
||||
flash_read_id(NULL, idBytes, 3);
|
||||
id.manufacturer_id = idBytes[0];
|
||||
id.chip_id = idBytes[1];
|
||||
id.chip_size_id = idBytes[2];
|
||||
return id;
|
||||
}
|
||||
|
||||
/*__ __
|
||||
| \/ |
|
||||
| \ / | ___ _ __ ___ ___ _ __ _ _
|
||||
| |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | |
|
||||
| | | | __/ | | | | | (_) | | | |_| |
|
||||
|_| |_|\___|_| |_| |_|\___/|_| \__, |
|
||||
__/ |
|
||||
|__*/
|
||||
uint32_t lt_get_ram_size() {
|
||||
return 256 * 1024;
|
||||
}
|
||||
|
||||
/* ____ _______
|
||||
/ __ \__ __|/\
|
||||
| | | | | | / \
|
||||
| | | | | | / /\ \
|
||||
| |__| | | |/ ____ \
|
||||
\____/ |_/_/ \*/
|
||||
lt_ota_type_t lt_ota_get_type() {
|
||||
return OTA_TYPE_DUAL;
|
||||
}
|
||||
|
||||
bool lt_ota_is_valid(uint8_t index) {
|
||||
uint32_t offset;
|
||||
switch (index) {
|
||||
case 1:
|
||||
offset = FLASH_OTA1_OFFSET;
|
||||
break;
|
||||
case 2:
|
||||
offset = FLASH_OTA2_OFFSET;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
uint8_t *address = (uint8_t *)(SPI_FLASH_BASE + offset);
|
||||
return memcmp(address, "81958711", 8) == 0;
|
||||
}
|
||||
|
||||
uint8_t lt_ota_dual_get_current() {
|
||||
// RTL8710B is XIP, so check the code offset in flash
|
||||
uint32_t addr = (uint32_t)lt_log;
|
||||
uint32_t offs = addr - SPI_FLASH_BASE;
|
||||
return offs > FLASH_OTA2_OFFSET ? 2 : 1;
|
||||
}
|
||||
|
||||
uint8_t lt_ota_dual_get_stored() {
|
||||
uint32_t *ota_address = (uint32_t *)0x8009000;
|
||||
if (*ota_address == 0xFFFFFFFF)
|
||||
return 1;
|
||||
uint32_t ota_counter = *((uint32_t *)0x8009004);
|
||||
// even count of zero-bits means OTA1, odd count means OTA2
|
||||
// this allows to switch OTA images by simply clearing next bits,
|
||||
// without needing to erase the flash
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < 32; i++) {
|
||||
if ((ota_counter & (1 << i)) == 0)
|
||||
count++;
|
||||
}
|
||||
return 1 + (count % 2);
|
||||
}
|
||||
|
||||
bool lt_ota_switch(bool revert) {
|
||||
uint8_t current = lt_ota_dual_get_current();
|
||||
uint8_t stored = lt_ota_dual_get_stored();
|
||||
if ((current == stored) == revert)
|
||||
return true;
|
||||
|
||||
if (!lt_ota_is_valid(stored ^ 0b11))
|
||||
return false;
|
||||
|
||||
// - read current OTA switch value from 0x9004
|
||||
// - reset OTA switch to 0xFFFFFFFE if it's 0x0
|
||||
// - else check first non-zero bit of OTA switch
|
||||
// - write OTA switch with first non-zero bit cleared
|
||||
|
||||
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
|
||||
if (value == 0) {
|
||||
uint8_t *system = (uint8_t *)malloc(64);
|
||||
lt_flash_read(FLASH_SYSTEM_OFFSET, system, 64);
|
||||
// reset OTA switch
|
||||
((uint32_t *)system)[1] = -2;
|
||||
lt_flash_erase_block(FLASH_SYSTEM_OFFSET);
|
||||
return lt_flash_write(FLASH_SYSTEM_OFFSET, system, 64);
|
||||
}
|
||||
|
||||
// clear first non-zero bit
|
||||
value <<= 1;
|
||||
// write OTA switch to flash
|
||||
flash_write_word(NULL, FLASH_SYSTEM_OFFSET + 4, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*_ __ _ _ _
|
||||
\ \ / / | | | | | |
|
||||
\ \ /\ / /_ _| |_ ___| |__ __| | ___ __ _
|
||||
\ \/ \/ / _` | __/ __| '_ \ / _` |/ _ \ / _` |
|
||||
\ /\ / (_| | || (__| | | | (_| | (_) | (_| |
|
||||
\/ \/ \__,_|\__\___|_| |_|\__,_|\___/ \__, |
|
||||
__/ |
|
||||
|___*/
|
||||
bool lt_wdt_enable(uint32_t timeout) {
|
||||
watchdog_init(timeout);
|
||||
watchdog_start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void lt_wdt_disable() {
|
||||
watchdog_stop();
|
||||
}
|
||||
|
||||
void lt_wdt_feed() {
|
||||
watchdog_refresh();
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */
|
||||
|
||||
#include "lt_family_api.h"
|
||||
|
||||
extern uint32_t GlobalDebugEnable;
|
||||
extern uint16_t GlobalDebugLevel;
|
||||
extern uint8_t GlobalPrivateLog;
|
||||
extern uint8_t lt_uart_port;
|
||||
|
||||
void lt_init_family() {
|
||||
// make the SDK less verbose by default
|
||||
GlobalDebugEnable = 0;
|
||||
GlobalPrivateLog = 0;
|
||||
lt_uart_port = LT_UART_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
ResetReason lt_get_reset_reason() {
|
||||
return RESET_REASON_UNKNOWN;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ static int write(long offset, const uint8_t *buf, size_t size) {
|
||||
}
|
||||
|
||||
static int erase(long offset, size_t size) {
|
||||
offset &= ~(FLASH_ERASE_MIN_SIZE - 1);
|
||||
size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
flash_erase_sector(NULL, offset + i * FLASH_ERASE_MIN_SIZE);
|
||||
|
||||
@@ -26,7 +26,7 @@ def load_chip_type_h() -> str:
|
||||
"cores",
|
||||
"common",
|
||||
"base",
|
||||
"lt_chip.h",
|
||||
"lt_types.h",
|
||||
)
|
||||
)
|
||||
code = re.sub(r"//.+", "", code)
|
||||
@@ -79,11 +79,11 @@ def get_enum_keys(code: str, name: str) -> Set[str]:
|
||||
|
||||
|
||||
def get_enum_mcus(code: str) -> Set[str]:
|
||||
return get_enum_keys(code, "ChipType")
|
||||
return get_enum_keys(code, "lt_cpu_model_t")
|
||||
|
||||
|
||||
def get_enum_families(code: str) -> Set[str]:
|
||||
return set(family[2:] for family in get_enum_keys(code, "ChipFamily"))
|
||||
return set(family[2:] for family in get_enum_keys(code, "lt_cpu_family_t"))
|
||||
|
||||
|
||||
def board_json_sort(tpl):
|
||||
|
||||
Reference in New Issue
Block a user