DFU 모듈 분리

This commit is contained in:
2026-07-21 12:33:54 +09:00
parent a83c9d11fa
commit b8fa7831c7
3 changed files with 262 additions and 227 deletions
+6 -227
View File
@@ -5,13 +5,12 @@
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
#include <hal/nrf_power.h>
#include "main.h"
#include "debug_print.h"
#include "power_control.h"
#include "dfu_manager.h"
#include "led_control.h"
#include "ble_service.h"
#include "battery_adc.h"
@@ -24,11 +23,6 @@ LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
#define BLE_CMD_MAX_LEN 256
#define BLE_CMD_QUEUE_DEPTH 8 // RX 명령 링버퍼 깊이(버스트 흡수)
#define BLE_CMD_WORKQ_STACK_SZ 8192
#define DFU_RESUME_MAGIC 0xD5U
#define DFU_RESUME_GPREGRET_REG 1U
#define DFU_STALL_TIMEOUT_SEC 30 // DFU 업로드 무진행 시 강제 disconnect 임계
#define IMG_MGMT_ID_STATE 0U
#define IMG_MGMT_ID_UPLOAD 1U
/*
* BLE 명령 처리 스택
* - piezo sweep
@@ -52,45 +46,6 @@ static bool resume_without_power_button; // 리셋 후 버튼 없이 복귀
static bool dfu_confirm_pending_boot; // DFU test 이미지 확인 전 자동 복귀
static bool dfu_reset_resume_request; // DFU 완료 리셋 후 자동 복귀
static uint32_t boot_reset_reason; // RESETREAS 원본 값
static bool dfu_led_active; // DFU 업로드 중 초록 LED ON
static struct k_work_delayable dfu_watchdog_work; // DFU 업로드 무진행 감시 work
static void dfu_watchdog_handler(struct k_work *work);
static enum mgmt_cb_return dfu_status_cb(uint32_t event,
enum mgmt_cb_return prev_status,
int32_t *rc,
uint16_t *group,
bool *abort_more,
void *data,
size_t data_size);
static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event,
enum mgmt_cb_return prev_status,
int32_t *rc,
uint16_t *group,
bool *abort_more,
void *data,
size_t data_size);
static struct mgmt_callback dfu_status_callback = {
.callback = dfu_status_cb,
.event_id = MGMT_EVT_OP_IMG_MGMT_DFU_STARTED |
MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK |
MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE |
MGMT_EVT_OP_IMG_MGMT_DFU_PENDING |
MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED,
};
static struct mgmt_callback smp_cmd_status_callback = {
.callback = smp_cmd_status_cb,
.event_id = MGMT_EVT_OP_CMD_DONE,
};
/*
* BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둠
* - 연결 직후 초기화 버스트 등 명령이 몰릴 때 대비해 링버퍼(FIFO)로 흡수
* - 큐가 가득 차면(BLE_CMD_QUEUE_DEPTH) 그 때만 drop
*/
static struct k_work_q ble_cmd_work_q;
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
static struct k_work ble_cmd_work; // BLE 명령 처리 work
@@ -246,188 +201,11 @@ static void load_default_config(void)
static void timers_init(void)
{
// DFU 업로드 무진행 감시 work
k_work_init_delayable(&dfu_watchdog_work, dfu_watchdog_handler);
// BLE 명령 처리 work queue
k_work_init(&ble_cmd_work, ble_cmd_work_handler);
k_work_queue_start(&ble_cmd_work_q, ble_cmd_workq_stack, K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack), BLE_CMD_WORKQ_PRIORITY, NULL);
// DFU 업로드 상태를 LED에 반영
mgmt_callback_register(&dfu_status_callback);
// DFU 명령 실패 시 LED 상태 복구
mgmt_callback_register(&smp_cmd_status_callback);
}
static void dfu_watchdog_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (ble_is_connected())
{
DBG_CORE("[DFU] upload stalled (%ds), forcing disconnect\r\n", DFU_STALL_TIMEOUT_SEC);
(void)ble_disconnect_active();
}
}
static enum mgmt_cb_return dfu_status_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc, uint16_t *group, bool *abort_more, void *data, size_t data_size)
{
ARG_UNUSED(prev_status);
ARG_UNUSED(rc);
ARG_UNUSED(group);
ARG_UNUSED(abort_more);
ARG_UNUSED(data);
ARG_UNUSED(data_size);
switch (event)
{
case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED:
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK:
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE:
dfu_led_active = true;
led_ble_solid(); // DFU 업로드 중에는 초록 LED ON
// 청크 수신/flash write 완료 때마다 무진행 감시 타이머 리셋
k_work_reschedule(&dfu_watchdog_work, K_SECONDS(DFU_STALL_TIMEOUT_SEC));
break;
case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING:
dfu_led_active = true;
led_ble_solid(); // 업로드 완료 후 리셋 전까지 초록 LED ON
k_work_cancel_delayable(&dfu_watchdog_work); // 업로드 완료: 감시 종료
#if NRF_POWER_HAS_GPREGRET
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, DFU_RESUME_MAGIC); // DFU 리셋 후 버튼 없이 전원 ON
#endif
break;
case MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED:
k_work_cancel_delayable(&dfu_watchdog_work); // 중단/실패: 감시 종료
if (dfu_led_active)
{
dfu_led_active = false;
led_set_state(LED_STATE_ADVERTISING); // 실패나 중단 시 일반 광고 LED로 복귀
}
break;
default:
break;
}
return MGMT_CB_OK;
}
static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc, uint16_t *group, bool *abort_more, void *data, size_t data_size)
{
ARG_UNUSED(event);
ARG_UNUSED(prev_status);
ARG_UNUSED(rc);
ARG_UNUSED(group);
ARG_UNUSED(abort_more);
if (data == NULL || data_size != sizeof(struct mgmt_evt_op_cmd_arg))
{
return MGMT_CB_OK;
}
const struct mgmt_evt_op_cmd_arg *cmd = data;
// 이미지 업로드/test 명령 실패 시 DFU LED 고정 해제
if (cmd->group == MGMT_GROUP_ID_IMAGE &&
(cmd->id == IMG_MGMT_ID_UPLOAD || cmd->id == IMG_MGMT_ID_STATE) &&
cmd->err != MGMT_ERR_EOK)
{
DBG_ERR("[DFU] command failed id=%u err=%d\r\n", cmd->id, cmd->err);
dfu_led_active = false;
led_set_state(LED_STATE_ADVERTISING);
// 실패 후 리셋 복구 표식이 남지 않게 정리
#if NRF_POWER_HAS_GPREGRET
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U);
#endif
}
return MGMT_CB_OK;
}
static void confirm_running_image(void)
{
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
// 기본 초기화와 BLE 준비가 끝난 뒤 MCUboot revert 방지
if (!boot_is_img_confirmed())
{
int err = boot_write_img_confirmed();
if (err == 0)
{
DBG_CORE("[DFU] Running image confirmed\r\n");
}
else
{
DBG_ERR("[DFU] Image confirm failed (err %d)\r\n", err);
}
}
else
{
DBG_CORE("[DFU] Running image already confirmed\r\n");
}
#endif
}
static const char *swap_type_to_str(int swap_type)
{
// MCUboot swap 상태 문자열
switch (swap_type)
{
case BOOT_SWAP_TYPE_NONE:
return "none";
case BOOT_SWAP_TYPE_TEST:
return "test";
case BOOT_SWAP_TYPE_PERM:
return "perm";
case BOOT_SWAP_TYPE_REVERT:
return "revert";
case BOOT_SWAP_TYPE_FAIL:
return "fail";
default:
return "unknown";
}
}
static void log_mcuboot_state(void)
{
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
// 현재 부팅 슬롯/교체 상태
struct mcuboot_img_header header;
uint8_t active_slot = boot_fetch_active_slot();
int swap_type = mcuboot_swap_type();
int err = boot_read_bank_header(active_slot, &header, sizeof(header));
DBG_CORE("[DFU] active_slot=%u swap_type=%s(%d) confirmed=%d\r\n",
active_slot,
swap_type_to_str(swap_type),
swap_type,
boot_is_img_confirmed());
if (err == 0)
{
/* 실행 이미지 버전 */
DBG_CORE("[DFU] image version %u.%u.%u+%u size=0x%x\r\n",
header.h.v1.sem_ver.major,
header.h.v1.sem_ver.minor,
header.h.v1.sem_ver.revision,
header.h.v1.sem_ver.build_num,
header.h.v1.image_size);
}
else
{
DBG_ERR("[DFU] image header read failed (slot %u, err %d)\r\n",
active_slot,
err);
}
#endif
}
/* 메인 함수 */
@@ -438,13 +216,13 @@ int main(void)
// 하드웨어 기본 초기화
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
dfu_confirm_pending_boot = !boot_is_img_confirmed();
dfu_confirm_pending_boot = dfu_is_confirm_pending_boot();
#else
dfu_confirm_pending_boot = false;
#endif
power_control_latch_init(resume_without_power_button || dfu_confirm_pending_boot || dfu_reset_resume_request);
power_control_reset_state();
confirm_running_image();
dfu_confirm_running_image();
DBG_CORE("\r\n========================================\r\n");
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
@@ -455,7 +233,8 @@ int main(void)
// 기본 하드웨어/센서 초기화
power_control_init();
timers_init();
log_mcuboot_state();
dfu_manager_init();
dfu_log_mcuboot_state();
load_default_config();
led_init();
battery_adc_init();
@@ -475,7 +254,7 @@ int main(void)
power_control_resume_after_reset(boot_reset_reason);
}
// BLE 준비까지 확인한 뒤 DFU test 이미지를 확정
confirm_running_image();
dfu_confirm_running_image();
}
else
{