diff --git a/builder/frameworks/base.py b/builder/frameworks/base.py index cd13865..3ad640d 100644 --- a/builder/frameworks/base.py +++ b/builder/frameworks/base.py @@ -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(), diff --git a/cores/beken-72xx/arduino/libraries/LT/LT.cpp b/cores/beken-72xx/arduino/libraries/LT/LT.cpp deleted file mode 100644 index c95d5be..0000000 --- a/cores/beken-72xx/arduino/libraries/LT/LT.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-06-19. */ - -#include - -// can't include as it collides with on Windows -_- -#include - -#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 -#include -#include -#include -#include -#include -#include - -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; diff --git a/cores/beken-72xx/base/lt_api.c b/cores/beken-72xx/base/lt_api.c new file mode 100644 index 0000000..a798f8b --- /dev/null +++ b/cores/beken-72xx/base/lt_api.c @@ -0,0 +1,190 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */ + +#include +#include + +// can't include as it collides with 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); +} diff --git a/cores/beken-72xx/base/lt_family_api.c b/cores/beken-72xx/base/lt_family_api.c deleted file mode 100644 index 2eff384..0000000 --- a/cores/beken-72xx/base/lt_family_api.c +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */ - -#include "lt_family_api.h" - -#include - -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; - } -} diff --git a/cores/beken-72xx/base/port/fal_flash_bk72xx_port.c b/cores/beken-72xx/base/port/fal_flash_bk72xx_port.c index 78e92b7..6487e67 100644 --- a/cores/beken-72xx/base/port/fal_flash_bk72xx_port.c +++ b/cores/beken-72xx/base/port/fal_flash_bk72xx_port.c @@ -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; diff --git a/cores/beken-72xx/base/sdk_private.h b/cores/beken-72xx/base/sdk_private.h index cfe3247..41d6a56 100644 --- a/cores/beken-72xx/base/sdk_private.h +++ b/cores/beken-72xx/base/sdk_private.h @@ -11,22 +11,22 @@ extern "C" { // other includes #include #include +#include +#include +#include +#include #include +#include +#include + +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; diff --git a/cores/common/arduino/libraries/api/LT/LT.cpp b/cores/common/arduino/libraries/api/LT/LT.cpp deleted file mode 100644 index 022093b..0000000 --- a/cores/common/arduino/libraries/api/LT/LT.cpp +++ /dev/null @@ -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--". - * 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 -} diff --git a/cores/common/arduino/libraries/api/LT/LT.h b/cores/common/arduino/libraries/api/LT/LT.h deleted file mode 100644 index 045ab3c..0000000 --- a/cores/common/arduino/libraries/api/LT/LT.h +++ /dev/null @@ -1,177 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */ - -#pragma once - -#include - -#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 diff --git a/cores/common/arduino/libraries/common/Flash/Flash.cpp b/cores/common/arduino/libraries/common/Flash/Flash.cpp deleted file mode 100644 index f3dc2f9..0000000 --- a/cores/common/arduino/libraries/common/Flash/Flash.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */ - -#include "Flash.h" - -extern "C" { -#include -} - -// 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; -} diff --git a/cores/common/arduino/libraries/common/Flash/Flash.h b/cores/common/arduino/libraries/common/Flash/Flash.h deleted file mode 100644 index 7e23a02..0000000 --- a/cores/common/arduino/libraries/common/Flash/Flash.h +++ /dev/null @@ -1,17 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */ - -#pragma once - -#include - -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; diff --git a/cores/common/arduino/libraries/common/Update/Update.cpp b/cores/common/arduino/libraries/common/Update/Update.cpp index 9f71167..13f83de 100644 --- a/cores/common/arduino/libraries/common/Update/Update.cpp +++ b/cores/common/arduino/libraries/common/Update/Update.cpp @@ -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 + // try to activate the second OTA + if (!lt_ota_switch(/* revert= */ false)) { cleanup(UPDATE_ERROR_ACTIVATE); return false; } diff --git a/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp b/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp index 18ae656..3be655c 100644 --- a/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp +++ b/cores/common/arduino/libraries/common/Update/UpdateUtil.cpp @@ -2,6 +2,10 @@ #include "Update.h" +extern "C" { +#include +} + 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); } diff --git a/cores/common/arduino/libraries/inline/.clang-format b/cores/common/arduino/libraries/inline/.clang-format new file mode 100644 index 0000000..b573581 --- /dev/null +++ b/cores/common/arduino/libraries/inline/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: InheritParentConfig +AllowShortFunctionsOnASingleLine: Inline diff --git a/cores/common/arduino/libraries/inline/Flash/Flash.h b/cores/common/arduino/libraries/inline/Flash/Flash.h new file mode 100644 index 0000000..e9c46ef --- /dev/null +++ b/cores/common/arduino/libraries/inline/Flash/Flash.h @@ -0,0 +1,38 @@ +/* Copyright (c) Kuba Szczodrzyński 2022-04-24. */ + +#pragma once + +#include + +#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 diff --git a/cores/common/arduino/libraries/inline/LT/LT.h b/cores/common/arduino/libraries/inline/LT/LT.h new file mode 100644 index 0000000..8d19949 --- /dev/null +++ b/cores/common/arduino/libraries/inline/LT/LT.h @@ -0,0 +1,113 @@ +/* Copyright (c) Kuba Szczodrzyński 2022-06-06. */ + +#pragma once + +#include +#include +#include + +#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 diff --git a/cores/common/arduino/libraries/inline/OTA/OTA.h b/cores/common/arduino/libraries/inline/OTA/OTA.h new file mode 100644 index 0000000..b646e72 --- /dev/null +++ b/cores/common/arduino/libraries/inline/OTA/OTA.h @@ -0,0 +1,44 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */ + +#pragma once + +#include + +#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 diff --git a/cores/common/arduino/libraries/inline/Singletons.cpp b/cores/common/arduino/libraries/inline/Singletons.cpp new file mode 100644 index 0000000..cfb6106 --- /dev/null +++ b/cores/common/arduino/libraries/inline/Singletons.cpp @@ -0,0 +1,10 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */ + +#include + +#include + +LibreTuya LT; +LibreTuyaOTA OTA; +LibreTuyaWDT WDT; +FlashClass Flash; diff --git a/cores/common/arduino/libraries/inline/WDT/WDT.h b/cores/common/arduino/libraries/inline/WDT/WDT.h new file mode 100644 index 0000000..4875799 --- /dev/null +++ b/cores/common/arduino/libraries/inline/WDT/WDT.h @@ -0,0 +1,32 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */ + +#pragma once + +#include + +#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 diff --git a/cores/common/base/api/lt_cpu.h b/cores/common/base/api/lt_cpu.h new file mode 100644 index 0000000..554f0a4 --- /dev/null +++ b/cores/common/base/api/lt_cpu.h @@ -0,0 +1,67 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */ + +#pragma once + +#include + +/** + * @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(); diff --git a/cores/common/base/api/lt_device.h b/cores/common/base/api/lt_device.h new file mode 100644 index 0000000..73dbd92 --- /dev/null +++ b/cores/common/base/api/lt_device.h @@ -0,0 +1,58 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */ + +#pragma once + +#include + +/** + * @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--". + * 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(); diff --git a/cores/common/base/api/lt_flash.h b/cores/common/base/api/lt_flash.h new file mode 100644 index 0000000..6117f24 --- /dev/null +++ b/cores/common/base/api/lt_flash.h @@ -0,0 +1,55 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */ + +#pragma once + +#include + +/** + * @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); diff --git a/cores/common/base/lt_family_api.h b/cores/common/base/api/lt_init.h similarity index 81% rename from cores/common/base/lt_family_api.h rename to cores/common/base/api/lt_init.h index 4611ddb..0ef8c80 100644 --- a/cores/common/base/lt_family_api.h +++ b/cores/common/base/api/lt_init.h @@ -4,10 +4,6 @@ #include -#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 diff --git a/cores/common/base/api/lt_mem.h b/cores/common/base/api/lt_mem.h new file mode 100644 index 0000000..b1cbe91 --- /dev/null +++ b/cores/common/base/api/lt_mem.h @@ -0,0 +1,30 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */ + +#pragma once + +#include + +/** + * @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(); diff --git a/cores/common/base/api/lt_ota.h b/cores/common/base/api/lt_ota.h new file mode 100644 index 0000000..4f93feb --- /dev/null +++ b/cores/common/base/api/lt_ota.h @@ -0,0 +1,60 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */ + +#pragma once + +#include +#include + +/** + * @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); diff --git a/cores/common/base/lt_common_api.h b/cores/common/base/api/lt_utils.h similarity index 73% rename from cores/common/base/lt_common_api.h rename to cores/common/base/api/lt_utils.h index 9a40cac..26d94d4 100644 --- a/cores/common/base/lt_common_api.h +++ b/cores/common/base/api/lt_utils.h @@ -4,10 +4,6 @@ #include -#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 +); diff --git a/cores/common/base/api/lt_wdt.h b/cores/common/base/api/lt_wdt.h new file mode 100644 index 0000000..dc6970d --- /dev/null +++ b/cores/common/base/api/lt_wdt.h @@ -0,0 +1,23 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-09. */ + +#pragma once + +#include + +/** + * @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(); diff --git a/cores/common/base/config/fal_cfg.h b/cores/common/base/config/fal_cfg.h index fbd073d..43775bc 100644 --- a/cores/common/base/config/fal_cfg.h +++ b/cores/common/base/config/fal_cfg.h @@ -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; diff --git a/cores/common/base/libretuya.h b/cores/common/base/libretuya.h index 746b1dc..6507722 100644 --- a/cores/common/base/libretuya.h +++ b/cores/common/base/libretuya.h @@ -36,17 +36,15 @@ ) // 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 // 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_logger.h" // UART logger utility -#include "lt_posix_api.h" // POSIX compat functions +#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 #include diff --git a/cores/common/base/lt_api.c b/cores/common/base/lt_api.c new file mode 100644 index 0000000..e424f87 --- /dev/null +++ b/cores/common/base/lt_api.c @@ -0,0 +1,264 @@ +/* Copyright (c) Kuba Szczodrzyński 2022-04-29. */ + +#include "lt_api.h" + +#include + +#if LT_HAS_FREERTOS +#include +#include +#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() {} diff --git a/cores/common/base/lt_api.h b/cores/common/base/lt_api.h new file mode 100644 index 0000000..e7a33bf --- /dev/null +++ b/cores/common/base/lt_api.h @@ -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 diff --git a/cores/common/base/lt_chip.h b/cores/common/base/lt_chip.h deleted file mode 100644 index 98a0cf9..0000000 --- a/cores/common/base/lt_chip.h +++ /dev/null @@ -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; diff --git a/cores/common/base/lt_common_api.c b/cores/common/base/lt_common_api.c deleted file mode 100644 index d73ca0c..0000000 --- a/cores/common/base/lt_common_api.c +++ /dev/null @@ -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; - } -} diff --git a/cores/common/base/lt_main.c b/cores/common/base/lt_main.c index df8ce0b..3dd1209 100644 --- a/cores/common/base/lt_main.c +++ b/cores/common/base/lt_main.c @@ -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 diff --git a/cores/common/base/lt_types.h b/cores/common/base/lt_types.h index 97abb7d..6d4e623 100644 --- a/cores/common/base/lt_types.h +++ b/cores/common/base/lt_types.h @@ -1,15 +1,79 @@ -/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */ +/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */ #pragma once +#include + +#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; diff --git a/cores/realtek-amb/arduino/libraries/LT/LT.cpp b/cores/realtek-amb/arduino/libraries/LT/LT.cpp deleted file mode 100644 index 94b0771..0000000 --- a/cores/realtek-amb/arduino/libraries/LT/LT.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* Copyright (c) Kuba Szczodrzyński 2022-05-28. */ - -#include -#include - -#include - -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; diff --git a/cores/realtek-amb/arduino/src/lt_api.c b/cores/realtek-amb/arduino/src/lt_api.c new file mode 100644 index 0000000..f247068 --- /dev/null +++ b/cores/realtek-amb/arduino/src/lt_api.c @@ -0,0 +1,8 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-03-10. */ + +#include +#include + +uint32_t lt_get_cpu_cycle_count() { + return microsecondsToClockCycles(micros()); +} diff --git a/cores/realtek-amb/base/lt_api.c b/cores/realtek-amb/base/lt_api.c new file mode 100644 index 0000000..2c0cae8 --- /dev/null +++ b/cores/realtek-amb/base/lt_api.c @@ -0,0 +1,219 @@ +/* Copyright (c) Kuba Szczodrzyński 2023-02-27. */ + +#include +#include + +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(); +} diff --git a/cores/realtek-amb/base/lt_family_api.c b/cores/realtek-amb/base/lt_family_api.c deleted file mode 100644 index 5c09957..0000000 --- a/cores/realtek-amb/base/lt_family_api.c +++ /dev/null @@ -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; -} diff --git a/cores/realtek-amb/base/port/fal_flash_ambz_port.c b/cores/realtek-amb/base/port/fal_flash_ambz_port.c index 3e5b7f3..42ba53b 100644 --- a/cores/realtek-amb/base/port/fal_flash_ambz_port.c +++ b/cores/realtek-amb/base/port/fal_flash_ambz_port.c @@ -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); diff --git a/docs/update_docs.py b/docs/update_docs.py index 328a219..d1bfd58 100644 --- a/docs/update_docs.py +++ b/docs/update_docs.py @@ -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):