[core] Update OTA API

This commit is contained in:
Kuba Szczodrzyński
2022-05-30 21:59:22 +02:00
parent 3345ce3fb9
commit dee9a98cc3
3 changed files with 129 additions and 15 deletions

View File

@@ -107,3 +107,56 @@ const char *LibreTuya::getDeviceName() {
sprintf(deviceName, "LT-%s-%02x%02x%02x", board, id[0], id[1], id[2]);
return deviceName;
}
static uint8_t otaRunningIndex = 0;
/**
* @brief Get the currently running firmware OTA index.
*/
uint8_t LibreTuya::otaGetRunning() {
if (otaRunningIndex)
return otaRunningIndex;
// otaRunningIndex will be correct even after switchOta()
return otaRunningIndex = otaGetStoredIndex();
}
/**
* @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.
*
* @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;
}

View File

@@ -70,9 +70,13 @@ class LibreTuya {
public: /* Common methods - note: these are documented in LibreTuyaAPI.cpp */
const char *getVersion();
const char *getBoard();
const char *getDeviceName();
ChipFamily getChipFamily();
const char *getChipFamilyName();
const char *getDeviceName();
uint8_t otaGetRunning();
uint8_t otaGetTarget();
bool otaRollback();
bool otaCanRollback();
public: /* Inline methods */
inline uint32_t getFlashChipSize() {
@@ -146,21 +150,30 @@ class LibreTuya {
public: /* OTA-related */
/**
* @brief Get the currently running firmware OTA index.
* @brief Read the currently active OTA index, i.e. the one that will boot upon restart.
*/
uint8_t getOtaRunning();
uint8_t otaGetStoredIndex();
/**
* @brief Get the OTA index for updated firmware.
*
* Note: should return 1 for chips without dual-OTA.
* @brief Check if the chip supports dual-OTA.
*/
uint8_t getOtaTarget();
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.
*
* @return false if writing failed or dual-OTA not supported; true otherwise
* 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 switchOta();
bool otaSwitch(bool force = false);
};
extern LibreTuya LT;

View File

@@ -2,8 +2,11 @@
#include <LibreTuyaAPI.h>
extern "C" {
#include <flash_api.h>
#include <rtl8710b.h>
#include <sys_api.h>
}
void LibreTuya::restart() {
sys_reset();
@@ -71,13 +74,14 @@ uint32_t LibreTuya::getMaxAllocHeap() {
/* OTA-related */
uint8_t LibreTuya::getOtaRunning() {
uint8_t LibreTuya::otaGetStoredIndex() {
uint32_t *otaAddress = (uint32_t *)0x8009000;
if (*otaAddress == 0xFFFFFFFF)
return 1;
uint32_t otaCounter = *((uint32_t *)0x8009004);
// what the-
// "LSB 0 bits number is odd/even"
// 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)
@@ -86,11 +90,55 @@ uint8_t LibreTuya::getOtaRunning() {
return 1 + (count % 2);
}
uint8_t LibreTuya::getOtaTarget() {
return getOtaRunning() ^ 0b11;
bool LibreTuya::otaSupportsDual() {
return true;
}
bool LibreTuya::switchOta() {}
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;
// this function does:
// - read OTA1 firmware magic from 0xB000
// - read OTA2 address from 0x9000
// - read OTA2 firmware magic from that address
// - read current OTA switch value from 0x9004
// - reset OTA switch to 0xFFFFFFFF if it's 0x0
// - check first non-zero bit of OTA switch
// - write OTA switch with first non-zero bit cleared
// sys_clear_ota_signature();
// ok, this function is broken (crashes with HardFault)
if (!otaHasImage1() || !otaHasImage2())
return false;
uint32_t value = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_OFFSET + 4);
if (value == 0) {
// TODO does this work at all?
FLASH_EreaseDwordsXIP(FLASH_SYSTEM_OFFSET + 4, 1);
}
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;
}
/* Global instance */