Skip to content

File UpdateUtil.cpp

File List > arduino > libraries > common > Update > UpdateUtil.cpp

Go to the documentation of this file.

/* Copyright (c) Kuba Szczodrzyński 2022-05-30. */

#include "Update.h"

#include <utility>

extern "C" {
#include <fal.h>
}

static const char *errUf2Text[] = {
    nullptr,                     // UF2_ERR_OK (0)
    nullptr,                     // UF2_ERR_IGNORE (1)
    "Bad Magic Number",          // UF2_ERR_MAGIC (2)
    "Bad Family ID",             // UF2_ERR_FAMILY (3)
    "Missing Header",            // UF2_ERR_NOT_HEADER (4)
    "Old OTA Format Found",      // UF2_ERR_OTA_VER (5)
    "Image Not Applicable",      // UF2_ERR_OTA_WRONG (6)
    "Partition Not Found",       // UF2_ERR_PART_404 (7)
    "Partition Info Invalid",    // UF2_ERR_PART_INVALID (8)
    "Partition Info Missing",    // UF2_ERR_PART_UNSET (9)
    "Block Data Too Long",       // UF2_ERR_DATA_TOO_LONG (10)
    "Bad Block Sequence Number", // UF2_ERR_SEQ_MISMATCH (11)
    "Flash Erase Failed",        // UF2_ERR_ERASE_FAILED (12)
    "Flash Write Failed",        // UF2_ERR_WRITE_FAILED (13)
    "Write Failed Length",       // UF2_ERR_WRITE_LENGTH (14)
    "Partition Write-Protected", // UF2_ERR_WRITE_PROTECT (15)
    "Memory Alloc Failed",       // UF2_ERR_ALLOC_FAILED (16)
};

static const char *errArdText[] = {
    nullptr,                           // UPDATE_ERROR_OK (0)
    nullptr,                           // UPDATE_ERROR_WRITE (1)
    nullptr,                           // UPDATE_ERROR_ERASE (2)
    nullptr,                           // UPDATE_ERROR_READ (3)
    nullptr,                           // UPDATE_ERROR_SPACE (4)
    "Bad Size Given",                  // UPDATE_ERROR_SIZE (5)
    "Stream Read Timeout",             // UPDATE_ERROR_STREAM (6)
    "MD5 Check Failed",                // UPDATE_ERROR_MD5 (7)
    nullptr,                           // UPDATE_ERROR_MAGIC_BYTE (8)
    "Could Not Activate The Firmware", // UPDATE_ERROR_ACTIVATE (9)
    nullptr,                           // UPDATE_ERROR_NO_PARTITION (10)
    "Bad Argument",                    // UPDATE_ERROR_BAD_ARGUMENT (11)
    "Aborted",                         // UPDATE_ERROR_ABORT (12)
};

static char errorStr[14];

UpdateClass &UpdateClass::onProgress(THandlerFunction_Progress handler) {
    this->callback = std::move(handler);
    return *this;
}

void UpdateClass::progressHandler(UpdateClass *self) {
    if (self->callback)
        self->callback(self->ctx->bytes_written, self->ctx->bytes_total);
}

bool UpdateClass::canRollBack() {
    return lt_ota_can_rollback();
}

bool UpdateClass::rollBack() {
    if (!lt_ota_can_rollback())
        return false;
    return lt_ota_switch(/* revert= */ false);
}

bool UpdateClass::setMD5(const char *md5) {
    if (strlen(md5) != 32)
        return false;
    if (!this->md5Expected)
        this->md5Expected = static_cast<uint8_t *>(malloc(16));
    if (!this->md5Expected)
        return false;
    lt_xtob(md5, 32, this->md5Expected);
    return true;
}

String UpdateClass::md5String() {
    if (!this->md5Digest)
        return "";
    char out[32 + 1];
    lt_btox(this->md5Digest, 16, out);
    return String(out);
}

void UpdateClass::md5(uint8_t *result) {
    if (!this->md5Digest) {
        memset(result, '\0', 16);
        return;
    }
    memcpy(result, this->md5Digest, 16);
}

uint16_t UpdateClass::getErrorCode() const {
    return (this->getError() << 8) | this->getUF2Error();
}

bool UpdateClass::hasError() const {
    return this->getError() != UPDATE_ERROR_OK || this->getUF2Error() > UF2_ERR_IGNORE;
}

void UpdateClass::clearError() {
    this->errArd = UPDATE_ERROR_OK;
    this->errUf2 = UF2_ERR_OK;
    if (this->ctx)
        this->ctx->error = UF2_ERR_OK;
}

const char *UpdateClass::errorString() const {
    uint8_t err;
    if ((err = this->getUF2Error()) > UF2_ERR_IGNORE)
        return errUf2Text[err];
    if ((err = this->getError()) != UPDATE_ERROR_OK)
        return errArdText[err];
    if (!this->hasError())
        return "";
    sprintf(errorStr, "ard=%u,uf2=%u", this->getError(), this->getUF2Error());
    return errorStr;
}

void UpdateClass::printError(Print &out) const {
    out.println(errorString());
}

void UpdateClass::printErrorContext() {
#if LT_DEBUG_OTA
    if (!this->ctx)
        return;

    LT_EM(OTA, "Error: %s", errorString());
    if (errArd == UPDATE_ERROR_ABORT)
        return;

    LT_EM(OTA, "- written: %u of %u", this->ctx->bytes_written, this->ctx->bytes_total);
    LT_EM(
        OTA,
        "- buf: size=%lld, left=%lld",
        this->ctx->buf_pos - this->ctx->buf,
        this->ctx->buf + UF2_BLOCK_SIZE - this->ctx->buf_pos
    );
    hexdump(this->ctx->buf, this->ctx->buf_pos - this->ctx->buf);

    if (ctx)
        LT_EM(
            OTA,
            "- ctx: seq=%u, part=%s",
            this->ctx->uf2.seq - 1, // print last parsed block seq
            this->ctx->uf2.part ? this->ctx->uf2.part->name : nullptr
        );

    auto *block = (uf2_block_t *)this->ctx->buf;
    LT_EM(OTA, "- buf: seq=%u/%u, addr=%u, len=%u", block->block_seq, block->block_count, block->addr, block->len);
#endif
}