diff --git a/src/ble/ble_service.c b/src/ble/ble_service.c index 2f51d10..41cef7f 100644 --- a/src/ble/ble_service.c +++ b/src/ble/ble_service.c @@ -806,11 +806,23 @@ int ble_dfu_advertising_enable(void) return ble_advertising_start(); } +/* DFU 진입용 disconnect */ bool ble_dfu_advertising_is_enabled(void) { return dfu_advertising_mode; } +/* DFU watchdog */ +int ble_disconnect_active(void) +{ + if (current_conn == NULL) + { + return -ENOTCONN; + } + + return bt_conn_disconnect(current_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); +} + int ble_data_send(const uint8_t *data, uint16_t len) { int err; diff --git a/src/ble/ble_service.h b/src/ble/ble_service.h index bfbf847..18aae4f 100644 --- a/src/ble/ble_service.h +++ b/src/ble/ble_service.h @@ -34,5 +34,6 @@ int ble_data_send(const uint8_t *data, uint16_t len); int ble_dfu_advertising_enable(void); bool ble_dfu_advertising_is_enabled(void); bool ble_is_connected(void); +int ble_disconnect_active(void); #endif /* BLE_SERVICE_H__ */ diff --git a/src/main.c b/src/main.c index 18b3381..9db365f 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,7 @@ LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF); #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 /* @@ -88,6 +89,8 @@ static struct k_work bond_reset_work; // 전원 버튼 15초 롱프레스 static bool dfu_led_active; // DFU 업로드 중 초록 LED ON static const char *power_off_pending_reason = "none"; static bool bond_reset_pending; // 본드 삭제 work 중복 예약 방지 +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, @@ -524,6 +527,9 @@ static void timers_init(void) // 전원 버튼 15초 롱프레스 본드 삭제 work k_work_init(&bond_reset_work, bond_reset_work_handler); + // 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); @@ -544,13 +550,21 @@ static void timers_start(void) k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT); } -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) +/* DFU 업로드가 멈춘 채(청크 무진행) 링크만 살아있는 경우, 연결을 강제로 끊어 + * SMP 재광고(10분 타임아웃)로 전환한다. 초록 LED 무한 점등/무한 대기 방지. */ +static void dfu_watchdog_handler(struct k_work *work) +{ + ARG_UNUSED(work); + + if (ble_is_connected()) + { + DBG_PRINTF("[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); @@ -563,19 +577,19 @@ static enum mgmt_cb_return dfu_status_cb(uint32_t event, { case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED: case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK: - // DFU 업로드 중에는 초록 LED ON dfu_led_active = true; - led_ble_solid(); + led_ble_solid(); // DFU 업로드 중에는 초록 LED ON + // 청크가 올 때마다 무진행 감시 타이머 리셋 + k_work_reschedule(&dfu_watchdog_work, K_SECONDS(DFU_STALL_TIMEOUT_SEC)); break; case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING: - // 업로드 완료 후 리셋 전까지 초록 LED ON dfu_led_active = true; - led_ble_solid(); + led_ble_solid(); // 업로드 완료 후 리셋 전까지 초록 LED ON + k_work_cancel_delayable(&dfu_watchdog_work); // 업로드 완료: 감시 종료 - // DFU 리셋 후 버튼 없이 전원 ON #if NRF_POWER_HAS_GPREGRET - nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, DFU_RESUME_MAGIC); + nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, DFU_RESUME_MAGIC); // DFU 리셋 후 버튼 없이 전원 ON #endif break;