fix(ota): 修复OtaCoordinator互斥锁死锁和SPI appPing首字节丢失

- OtaCoordinator lock/unlock: 移除共享 m_locked 标志,改为传参传递 take
  结果,消除多任务并发 lock() 超时覆盖持锁状态导致 mutex 永久死锁的问题
- SPI appPing: 发送帧填充至 16 字节防止首字节丢失,增大轮询间隔至 10ms,
  增大接收缓冲区避免字节偏移解析失败
- SPI init: 先创建 mutex 再使用 LockGuard,消除竞态窗口
- 压制 WiFi 框架 INFO 日志 (pp/net80211/wifi/wifi_init),减少启动打印
- 降级 7 条调试日志为 DBG 级别(UART0 初始化、冷启动缓存清理、互斥锁/重连
  任务/回调/netif 创建)
- GD32 版本号读取增加 5 次重试上限和 OTA 繁忙时跳过逻辑
- 移除 forwardFirmware 中已过时注释
This commit is contained in:
小龙虾
2026-06-17 11:52:26 +08:00
parent 98b5cc1ba1
commit 2586582e57
8 changed files with 214 additions and 178 deletions
+129 -119
View File
@@ -23,28 +23,26 @@ OtaCoordinator& OtaCoordinator::instance()
// ============================================================
// 线程安全辅助
// ============================================================
void OtaCoordinator::lock()
bool OtaCoordinator::lock()
{
// ★ 修复:FreeRTOS mutex (xSemaphoreCreateMutex) 只能由 take 的 task give。
// 超时后必须在 unlock() 中跳过 give,避免 xTaskPriorityDisinherit assert。
// 此处假设 OtaCoordinator 内部不嵌套 lock(全局已确认:无嵌套调用)。
m_locked = false;
if (m_mutex) {
m_locked = (xSemaphoreTake(m_mutex, kLockTimeout) == pdTRUE);
if (!m_locked) {
DEBUG_WARN(TAG, "lock() 超时 (%u ms),本次状态变更被跳过",
(unsigned)pdTICKS_TO_MS(kLockTimeout));
}
// ★ 修复: 返回 take 结果,由调用方传回 unlock()。
// 旧实现用成员 bool m_locked 记录 take 结果,多任务并发 lock() 时
// 超时方会把 m_locked 置 false,覆盖持锁方的 true,导致 unlock() 跳过 give,
// mutex 永久死锁 → 后续 take 超时触发 vTaskPriorityDisinherit assert → PANIC。
if (!m_mutex) return false;
bool ok = (xSemaphoreTake(m_mutex, kLockTimeout) == pdTRUE);
if (!ok) {
DEBUG_WARN(TAG, "lock() 超时 (%u ms),本次状态变更被跳过",
(unsigned)pdTICKS_TO_MS(kLockTimeout));
}
return ok;
}
void OtaCoordinator::unlock()
void OtaCoordinator::unlock(bool handle)
{
// ★ 修复:仅在 lock() 成功 take 时才 give。
// 错误的 task 给同一 mutex give 会触发 xTaskPriorityDisinherit assert → PANIC。
if (m_locked && m_mutex) {
// ★ 修复: 仅在对应的 lock() 成功 take 时才 give。
if (handle && m_mutex) {
xSemaphoreGive(m_mutex);
m_locked = false;
}
}
@@ -119,51 +117,51 @@ void OtaCoordinator::init()
// ============================================================
esp_err_t OtaCoordinator::beginGd32Storage(uint32_t total_size)
{
lock();
bool h = lock();
// ★ 先做卡死自愈检查:若之前忙状态卡死,自动转 ERROR 继续后续流程
watchdogCheckLocked();
if (h) watchdogCheckLocked();
// 系统忙检查:仅拒绝真正"在传输"的活跃状态
// STORED / COMPLETE 是"待中继/已中继"的非活跃状态 → 允许新存储覆盖
// ★ 修复:之前把所有非 IDLE 都当忙,导致"Flash 缓存里有固件但没在传输"时无法上传新固件
if (m_sysState == OTA_SYS_ESP32_RECEIVING ||
if (h && (m_sysState == OTA_SYS_ESP32_RECEIVING ||
m_sysState == OTA_SYS_ESP32_FINALIZING ||
m_sysState == OTA_SYS_ESP32_REBOOTING ||
m_sysState == OTA_SYS_GD32_STORING ||
m_sysState == OTA_SYS_GD32_RELAYING) {
m_sysState == OTA_SYS_GD32_RELAYING)) {
DEBUG_WARN(TAG, "系统忙 (state=%d),拒绝 GD32 存储请求", (int)m_sysState);
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
// 如果之前已有存储但未中继(STORED)或刚中继完(COMPLETE),先清理再开始新存储
if (m_sysState == OTA_SYS_GD32_STORED || m_sysState == OTA_SYS_GD32_COMPLETE) {
if (h && (m_sysState == OTA_SYS_GD32_STORED || m_sysState == OTA_SYS_GD32_COMPLETE)) {
DEBUG_INFO(TAG, "清理旧 GD32 存储状态,开始新存储 (size=%" PRIu32 ")", total_size);
OtaRelay::metaClear();
}
m_sysState = OTA_SYS_GD32_STORING;
m_gd32StorageDone = false;
m_busySinceTick = xTaskGetTickCount(); // ★ 记录忙状态起始时刻
DEBUG_INFO(TAG, "GD32 固件存储开始: %" PRIu32 " 字节, 系统状态 → GD32_STORING", total_size);
unlock();
return ESP_OK;
if (h) {
m_sysState = OTA_SYS_GD32_STORING;
m_gd32StorageDone = false;
m_busySinceTick = xTaskGetTickCount(); // ★ 记录忙状态起始时刻
DEBUG_INFO(TAG, "GD32 固件存储开始: %" PRIu32 " 字节, 系统状态 → GD32_STORING", total_size);
}
unlock(h);
return h ? ESP_OK : ESP_ERR_INVALID_STATE;
}
void OtaCoordinator::completeGd32Storage()
{
lock();
if (m_sysState == OTA_SYS_GD32_STORING) {
bool h = lock();
if (h && m_sysState == OTA_SYS_GD32_STORING) {
m_sysState = OTA_SYS_GD32_STORED;
m_gd32StorageDone = true;
m_busySinceTick = 0; // ★ STORED 是非忙状态
DEBUG_INFO(TAG, "GD32 固件存储完成, 系统状态 → GD32_STORED");
} else {
} else if (h) {
DEBUG_WARN(TAG, "completeGd32Storage 在非预期状态: %d", (int)m_sysState);
}
unlock();
unlock(h);
}
// ============================================================
@@ -171,59 +169,60 @@ void OtaCoordinator::completeGd32Storage()
// ============================================================
esp_err_t OtaCoordinator::triggerGd32Relay()
{
lock();
bool h = lock();
// ★ 卡死自愈检查
watchdogCheckLocked();
if (h) watchdogCheckLocked();
// 校验:必须处于"已存储"或"已中继完成"状态
// ★ 修复:COMPLETE(旧固件已成功推给 GD32,NVS 元数据已被 doFinishAndVerify 清掉,
// OtaRelay 状态为 DONE)也能触发——但需要先回滚到 STORED 以保证 m_busySinceTick 一致。
// 通常用户应先上传新固件再触发中继,COMPLETE 时元数据已被清,metaLoad 会返回 stored=0
// 并在 188 行被拒绝,自然不会走到这里。
if (m_sysState != OTA_SYS_GD32_STORED && m_sysState != OTA_SYS_IDLE) {
if (h && m_sysState != OTA_SYS_GD32_STORED && m_sysState != OTA_SYS_IDLE) {
if (m_sysState == OTA_SYS_GD32_STORING) {
DEBUG_WARN(TAG, "GD32 正在存储中,拒绝触发中继");
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
if (m_sysState == OTA_SYS_GD32_RELAYING) {
DEBUG_WARN(TAG, "GD32 中继已在进行中");
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
if (m_sysState == OTA_SYS_ESP32_RECEIVING ||
m_sysState == OTA_SYS_ESP32_FINALIZING ||
m_sysState == OTA_SYS_ESP32_REBOOTING) {
DEBUG_WARN(TAG, "ESP32 OTA 进行中,拒绝中继触发");
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
}
// 验证固件是否确实已存储(通过 NVS 元数据)
ota_meta_t meta;
if (OtaRelay::metaLoad(&meta) != ESP_OK ||
if (h && (OtaRelay::metaLoad(&meta) != ESP_OK ||
meta.total_size == 0 ||
meta.stored_size < meta.total_size) {
meta.stored_size < meta.total_size)) {
DEBUG_WARN(TAG, "GD32 固件未完整存储,拒绝触发中继");
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
// 检查 OtaRelay 是否空闲
if (OtaRelay::getState() != RELAY_IDLE &&
if (h && OtaRelay::getState() != RELAY_IDLE &&
OtaRelay::getState() != RELAY_DONE &&
OtaRelay::getState() != RELAY_ERROR) {
DEBUG_WARN(TAG, "OtaRelay 正忙 (state=%d),拒绝触发", (int)OtaRelay::getState());
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
// 向 OtaRelay task 发送启动消息
if (!OtaRelay::dispatchQueue) {
if (h && !OtaRelay::dispatchQueue) {
DEBUG_ERR(TAG, "OtaRelay dispatchQueue 未就绪");
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
if (!h) {
unlock(h);
return ESP_ERR_INVALID_STATE;
}
@@ -237,39 +236,41 @@ esp_err_t OtaCoordinator::triggerGd32Relay()
DEBUG_ERR(TAG, "向 OtaRelay 发送启动消息失败");
m_sysState = OTA_SYS_GD32_STORED;
m_busySinceTick = 0; // 回退到 STORED(非忙)
unlock();
unlock(h);
return ESP_FAIL;
}
unlock();
unlock(h);
return ESP_OK;
}
void OtaCoordinator::notifyRelayStarted()
{
lock();
if (m_sysState != OTA_SYS_GD32_RELAYING) {
bool h = lock();
if (h && m_sysState != OTA_SYS_GD32_RELAYING) {
m_sysState = OTA_SYS_GD32_RELAYING;
m_busySinceTick = xTaskGetTickCount(); // ★ 记录起始时刻
DEBUG_INFO(TAG, "中继已开始, 系统状态 → GD32_RELAYING");
}
unlock();
unlock(h);
}
void OtaCoordinator::notifyRelayComplete(bool success)
{
lock();
if (success) {
m_sysState = OTA_SYS_GD32_COMPLETE;
m_gd32StorageDone = false;
m_busySinceTick = 0; // ★ COMPLETE 非忙
DEBUG_INFO(TAG, "GD32 中继完成, 系统状态 → GD32_COMPLETE");
} else {
m_sysState = OTA_SYS_ERROR;
m_busySinceTick = 0; // ★ ERROR 非忙(可被后续请求恢复)
DEBUG_ERR(TAG, "GD32 中继失败, 系统状态 → ERROR");
bool h = lock();
if (h) {
if (success) {
m_sysState = OTA_SYS_GD32_COMPLETE;
m_gd32StorageDone = false;
m_busySinceTick = 0; // ★ COMPLETE 非忙
DEBUG_INFO(TAG, "GD32 中继完成, 系统状态 → GD32_COMPLETE");
} else {
m_sysState = OTA_SYS_ERROR;
m_busySinceTick = 0; // ★ ERROR 非忙(可被后续请求恢复)
DEBUG_ERR(TAG, "GD32 中继失败, 系统状态 → ERROR");
}
}
unlock();
unlock(h);
}
// ============================================================
@@ -277,60 +278,65 @@ void OtaCoordinator::notifyRelayComplete(bool success)
// ============================================================
esp_err_t OtaCoordinator::beginEsp32Ota(uint32_t total_size)
{
lock();
bool h = lock();
// ★ 卡死自愈检查
watchdogCheckLocked();
if (h) watchdogCheckLocked();
// 系统忙检查
// ★ 修复:STORING/RELAYING 都拒绝(防止中继中改 gd32_ota 分区被 ESP32 OTA 误触发)。
// STORED/COMPLETE 允许——它们是非活跃状态,ESP32 OTA 与 gd32_ota 分区无冲突。
if (m_sysState == OTA_SYS_GD32_STORING ||
m_sysState == OTA_SYS_GD32_RELAYING) {
if (h && (m_sysState == OTA_SYS_GD32_STORING ||
m_sysState == OTA_SYS_GD32_RELAYING)) {
DEBUG_WARN(TAG, "GD32 正在 %s,拒绝 ESP32 OTA",
m_sysState == OTA_SYS_GD32_STORING ? "存储" : "中继");
unlock();
unlock(h);
return ESP_ERR_INVALID_STATE;
}
// 委托 Esp32OtaCore 处理分区/互斥逻辑
esp_err_t err = Esp32OtaCore::instance().begin(total_size);
if (err == ESP_OK) {
m_sysState = OTA_SYS_ESP32_RECEIVING;
m_busySinceTick = xTaskGetTickCount(); // ★ 记录起始时刻
DEBUG_INFO(TAG, "ESP32 OTA 开始: %" PRIu32 " 字节, 系统状态 → ESP32_RECEIVING", total_size);
} else {
DEBUG_WARN(TAG, "ESP32 OTA begin 失败: %s", esp_err_to_name(err));
esp_err_t err = ESP_ERR_INVALID_STATE;
if (h) {
// 委托 Esp32OtaCore 处理分区/互斥逻辑
err = Esp32OtaCore::instance().begin(total_size);
if (err == ESP_OK) {
m_sysState = OTA_SYS_ESP32_RECEIVING;
m_busySinceTick = xTaskGetTickCount(); // ★ 记录起始时刻
DEBUG_INFO(TAG, "ESP32 OTA 开始: %" PRIu32 " 字节, 系统状态 → ESP32_RECEIVING", total_size);
} else {
DEBUG_WARN(TAG, "ESP32 OTA begin 失败: %s", esp_err_to_name(err));
}
}
unlock();
unlock(h);
return err;
}
void OtaCoordinator::notifyEsp32ReceiveComplete()
{
lock();
if (m_sysState == OTA_SYS_ESP32_RECEIVING) {
bool h = lock();
if (h && m_sysState == OTA_SYS_ESP32_RECEIVING) {
m_sysState = OTA_SYS_ESP32_FINALIZING;
DEBUG_INFO(TAG, "ESP32 固件接收完毕, 系统状态 → ESP32_FINALIZING");
}
unlock();
unlock(h);
}
void OtaCoordinator::notifyEsp32FinalizeComplete()
{
lock();
m_sysState = OTA_SYS_ESP32_REBOOTING;
DEBUG_INFO(TAG, "ESP32 OTA 收尾完成, 系统状态 → ESP32_REBOOTING");
unlock();
bool h = lock();
if (h) {
m_sysState = OTA_SYS_ESP32_REBOOTING;
DEBUG_INFO(TAG, "ESP32 OTA 收尾完成, 系统状态 → ESP32_REBOOTING");
}
unlock(h);
}
void OtaCoordinator::notifyEsp32Rebooting()
{
lock();
m_sysState = OTA_SYS_ESP32_REBOOTING;
DEBUG_INFO(TAG, "ESP32 即将重启, 系统状态 → ESP32_REBOOTING");
unlock();
bool h = lock();
if (h) {
m_sysState = OTA_SYS_ESP32_REBOOTING;
DEBUG_INFO(TAG, "ESP32 即将重启, 系统状态 → ESP32_REBOOTING");
}
unlock(h);
}
// ============================================================
@@ -338,23 +344,25 @@ void OtaCoordinator::notifyEsp32Rebooting()
// ============================================================
void OtaCoordinator::abortCurrent()
{
lock();
DEBUG_INFO(TAG, "中止当前 OTA 操作 (state=%d)", (int)m_sysState);
bool h = lock();
if (h) {
DEBUG_INFO(TAG, "中止当前 OTA 操作 (state=%d)", (int)m_sysState);
if (m_sysState == OTA_SYS_GD32_STORING) {
OtaRelay::metaClear();
} else if (m_sysState == OTA_SYS_GD32_RELAYING) {
OtaRelay::instance().reset();
} else if (m_sysState == OTA_SYS_ESP32_RECEIVING ||
m_sysState == OTA_SYS_ESP32_FINALIZING) {
Esp32OtaCore::instance().abort();
if (m_sysState == OTA_SYS_GD32_STORING) {
OtaRelay::metaClear();
} else if (m_sysState == OTA_SYS_GD32_RELAYING) {
OtaRelay::instance().reset();
} else if (m_sysState == OTA_SYS_ESP32_RECEIVING ||
m_sysState == OTA_SYS_ESP32_FINALIZING) {
Esp32OtaCore::instance().abort();
}
m_sysState = OTA_SYS_IDLE;
m_gd32StorageDone = false;
m_busySinceTick = 0; // ★ 离开忙状态
DEBUG_INFO(TAG, "已中止, 系统状态 → IDLE");
}
m_sysState = OTA_SYS_IDLE;
m_gd32StorageDone = false;
m_busySinceTick = 0; // ★ 离开忙状态
DEBUG_INFO(TAG, "已中止, 系统状态 → IDLE");
unlock();
unlock(h);
}
// ============================================================
@@ -364,9 +372,9 @@ ota_system_state_t OtaCoordinator::getSystemState() const
{
// 对于纯读取,短暂加锁保证一致性
OtaCoordinator* self = const_cast<OtaCoordinator*>(this);
self->lock();
bool h = self->lock();
ota_system_state_t s = m_sysState;
self->unlock();
self->unlock(h);
return s;
}
@@ -416,9 +424,9 @@ bool OtaCoordinator::getGd32AppAlive() const
bool OtaCoordinator::isGd32StorageDone() const
{
OtaCoordinator* self = const_cast<OtaCoordinator*>(this);
self->lock();
bool h = self->lock();
bool done = m_gd32StorageDone;
self->unlock();
self->unlock(h);
return done;
}
@@ -427,10 +435,12 @@ bool OtaCoordinator::isGd32StorageDone() const
// ============================================================
void OtaCoordinator::reset()
{
lock();
m_sysState = OTA_SYS_IDLE;
m_gd32StorageDone = false;
m_busySinceTick = 0; // ★ 复位
DEBUG_INFO(TAG, "协调器已完全复位");
unlock();
bool h = lock();
if (h) {
m_sysState = OTA_SYS_IDLE;
m_gd32StorageDone = false;
m_busySinceTick = 0; // ★ 复位
DEBUG_INFO(TAG, "协调器已完全复位");
}
unlock(h);
}