You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MT2712的嵌入式Linux USB A/B更新自研C++应用开发求助

MT2712平台A/B USB固件更新函数实现方案

针对MT2712平台的A/B分区USB固件更新场景,基于你提供的代码模板,以下是各函数的具体实现思路与代码示例,全部适配嵌入式Linux环境,依赖Yocto构建的系统组件:


1. update_progress_status_set()

用于记录更新阶段状态,可同步到日志或UI(如DBus通知)。

enum class Update_progress_status {
    started, checking_file, getting_partition, backing_up, verifying_image, flashing, completed
};

void update_progress_status_set(Update_progress_status status) {
    // 写入系统日志
    syslog(LOG_INFO, "Update status: %d", static_cast<int>(status));
    // 若有UI交互,可在此添加DBus状态推送逻辑
}

2. update_file_check()

核心功能是识别USB中的合法固件镜像,步骤包括USB挂载检查、文件遍历、镜像签名验证:

#include <filesystem>
#include <fstream>
#include <vector>

// 类成员变量:保存合法镜像路径
std::string m_update_file_path;

bool update_file_check() {
    const std::string usb_mount = "/mnt/usb";
    const std::vector<std::string> valid_exts = {".img", ".bin"};
    // MT2712镜像魔数,需与Yocto生成的镜像实际值匹配
    const uint32_t MT2712_MAGIC = 0x12720000;

    // 检查USB是否挂载
    if (access(usb_mount.c_str(), F_OK) != 0) {
        syslog(LOG_ERR, "USB not mounted at %s", usb_mount.c_str());
        return false;
    }

    // 遍历USB目录查找镜像
    for (const auto& entry : std::filesystem::directory_iterator(usb_mount)) {
        if (!entry.is_regular_file()) continue;

        std::string ext = entry.path().extension().string();
        if (std::find(valid_exts.begin(), valid_exts.end(), ext) == valid_exts.end()) continue;

        // 验证镜像文件头魔数
        std::ifstream img_file(entry.path(), std::ios::binary);
        uint32_t magic;
        img_file.read(reinterpret_cast<char*>(&magic), sizeof(magic));
        if (magic == MT2712_MAGIC) {
            m_update_file_path = entry.path().string();
            syslog(LOG_INFO, "Found valid update file: %s", m_update_file_path.c_str());
            return true;
        }
    }

    syslog(LOG_ERR, "No valid update file on USB");
    return false;
}

注意:Yocto配方中需添加-lstdc++fs编译选项以支持std::filesystem。


3. update_partition_get()

A/B分区机制下,需识别当前运行分区,切换到另一分区作为更新目标:

#include <fstream>
#include <sstream>
#include <vector>

// 类成员变量:保存目标分区路径
std::string m_update_partition;

std::string update_partition_get() {
    std::string current_root;
    // 从/proc/mounts读取当前根分区
    std::ifstream mounts("/proc/mounts");
    std::string line;
    while (std::getline(mounts, line)) {
        std::vector<std::string> parts;
        std::stringstream ss(line);
        std::string part;
        while (ss >> part) parts.push_back(part);
        if (parts.size() >=2 && parts[1] == "/") {
            current_root = parts[0];
            break;
        }
    }

    // MT2712默认A/B分区假设为mmcblk0p2(A)、mmcblk0p3(B)
    if (current_root == "/dev/mmcblk0p2") {
        m_update_partition = "/dev/mmcblk0p3";
        syslog(LOG_INFO, "Current partition: A, target: B");
    } else if (current_root == "/dev/mmcblk0p3") {
        m_update_partition = "/dev/mmcblk0p2";
        syslog(LOG_INFO, "Current partition: B, target: A");
    } else {
        syslog(LOG_ERR, "Unknown root partition: %s", current_root.c_str());
        throw std::runtime_error("Failed to get target partition");
    }
    return m_update_partition;
}

4. update_settings_backup()

将用户配置备份到非A/B分区(如data分区),避免更新后配置丢失:

void update_settings_backup() {
    const std::string data_part = "/dev/mmcblk0p4";
    const std::string backup_path = "/mnt/data/settings_backup.tar.gz";
    const std::string settings_dir = "/etc/my_app_config";

    // 挂载data分区
    system("mount " + data_part + " /mnt/data");
    // 打包配置目录
    std::string cmd = "tar -czf " + backup_path + " " + settings_dir;
    int ret = system(cmd.c_str());
    if (ret != 0) {
        syslog(LOG_WARNING, "Settings backup failed");
    } else {
        syslog(LOG_INFO, "Settings backed up to %s", backup_path.c_str());
    }
    // 卸载分区
    system("umount /mnt/data");
}

5. image_get()

打开USB中的镜像文件,获取文件描述符与大小,为后续校验、写入做准备:

#include <sys/stat.h>
#include <fcntl.h>

// 类成员变量
int m_image_fd;
off_t m_image_size;

bool image_get() {
    m_image_fd = open(m_update_file_path.c_str(), O_RDONLY);
    if (m_image_fd == -1) {
        syslog(LOG_ERR, "Open update file failed: %s", strerror(errno));
        return false;
    }

    struct stat img_stat;
    if (fstat(m_image_fd, &img_stat) != 0) {
        syslog(LOG_ERR, "Get file stat failed: %s", strerror(errno));
        close(m_image_fd);
        return false;
    }
    m_image_size = img_stat.st_size;
    syslog(LOG_INFO, "Update file size: %ld bytes", m_image_size);
    return true;
}

6. image_verify()

通过SHA256哈希验证镜像完整性,需搭配Yocto生成的镜像校验文件(如xxx.img.sha256):

#include <openssl/sha.h>
#include <fstream>
#include <cstring>

bool image_verify() {
    std::string sha_file_path = m_update_file_path + ".sha256";
    if (access(sha_file_path.c_str(), F_OK) != 0) {
        syslog(LOG_ERR, "SHA256 file not found: %s", sha_file_path.c_str());
        return false;
    }

    // 读取预期哈希值
    std::ifstream sha_file(sha_file_path);
    std::string expected_sha;
    sha_file >> expected_sha;

    // 计算镜像文件的SHA256
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha_ctx;
    SHA256_Init(&sha_ctx);

    char buffer[4096];
    ssize_t bytes_read;
    while ((bytes_read = read(m_image_fd, buffer, sizeof(buffer))) > 0) {
        SHA256_Update(&sha_ctx, buffer, bytes_read);
    }
    SHA256_Final(hash, &sha_ctx);

    // 转换为十六进制字符串
    char computed_sha[SHA256_DIGEST_LENGTH * 2 + 1];
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        sprintf(computed_sha + i*2, "%02x", hash[i]);
    }

    // 对比哈希值
    if (strcmp(computed_sha, expected_sha.c_str()) == 0) {
        syslog(LOG_INFO, "Image verification passed");
        lseek(m_image_fd, 0, SEEK_SET); // 重置文件指针到开头
        return true;
    } else {
        syslog(LOG_ERR, "Verify failed: expected %s, computed %s", expected_sha.c_str(), computed_sha);
        close(m_image_fd);
        return false;
    }
}

注意:Yocto配方需添加DEPENDS += "openssl"以链接OpenSSL库。


7. memory_flash()

将镜像写入目标分区,直接操作设备文件保证效率,需以root权限运行:

#include <fcntl.h>
#include <unistd.h>

bool memory_flash() {
    int target_fd = open(m_update_partition.c_str(), O_WRONLY | O_SYNC);
    if (target_fd == -1) {
        syslog(LOG_ERR, "Open target partition failed: %s", strerror(errno));
        close(m_image_fd);
        return false;
    }

    char buffer[4096];
    ssize_t bytes_read, bytes_written;
    uint64_t total_written = 0;

    while ((bytes_read = read(m_image_fd, buffer, sizeof(buffer))) > 0) {
        bytes_written = write(target_fd, buffer, bytes_read);
        if (bytes_written != bytes_read) {
            syslog(LOG_ERR, "Write partition failed: %s", strerror(errno));
            close(m_image_fd);
            close(target_fd);
            return false;
        }
        total_written += bytes_written;
        update_progress_status_set(Update_progress_status::flashing);
        syslog(LOG_INFO, "Flashed %ld/%ld bytes", total_written, m_image_size);
    }

    // 同步磁盘缓存
    fsync(target_fd);
    close(m_image_fd);
    close(target_fd);
    syslog(LOG_INFO, "Flash completed");
    return true;
}

8. update_reboot()

更新完成后设置U-Boot环境变量,重启系统切换到新分区:

void update_reboot() {
    // 通过fw_setenv设置启动分区(Yocto需添加u-boot-fw-utils组件)
    std::string boot_part = (m_update_partition == "/dev/mmcblk0p3") ? "B" : "A";
    std::string cmd = "fw_setenv boot_part " + boot_part;
    system(cmd.c_str());

    syslog(LOG_INFO, "Rebooting to partition %s...", boot_part.c_str());
    system("reboot");
}

9. update_failure()

处理更新失败场景,记录日志并可添加配置恢复逻辑:

enum class Update_failure_status {
    update_file_failure, flash_memory_failure
};

// 类成员变量
Update_failure_status m_update_failure_status;

void update_failure() {
    syslog(LOG_ERR, "Update failed: status=%d", static_cast<int>(m_update_failure_status));
    // 可选:恢复备份的配置文件
    // restore_settings();
}

内容的提问来源于stack exchange,提问作者frozen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:12:23