基于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
相关产品推荐
相关产品推荐

