BLE RX 단일 슬롯에서 8-depth 링버퍼로 변경

- 연결 직후 초기화 명령이 몰릴 때, 앞 명령 처리 중 도착한 명령이 drop: command busy로 버려지던 문제 해결- 큐가 가득 찰 때만 drop: queue full로 처리
This commit is contained in:
2026-07-16 17:24:53 +09:00
parent ba64e2bb81
commit 3f2c2e774b
+50 -45
View File
@@ -28,6 +28,7 @@
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF); LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
#define BLE_CMD_MAX_LEN 256 #define BLE_CMD_MAX_LEN 256
#define BLE_CMD_QUEUE_DEPTH 8 // RX 명령 링버퍼 깊이(버스트 흡수)
#define BLE_CMD_WORKQ_STACK_SZ 8192 #define BLE_CMD_WORKQ_STACK_SZ 8192
#define DFU_RESUME_MAGIC 0xD5U #define DFU_RESUME_MAGIC 0xD5U
#define DFU_RESUME_GPREGRET_REG 1U #define DFU_RESUME_GPREGRET_REG 1U
@@ -123,15 +124,20 @@ static struct mgmt_callback smp_cmd_status_callback = {
/* /*
* BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둠 * BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둠
* - 명령 하나가 끝날 때까지 다음 명령 drop : 확인 필요 * - 연결 직후 초기화 버스트 등 명령이 몰릴 때 대비해 링버퍼(FIFO)로 흡수
* - 큐가 가득 차면(BLE_CMD_QUEUE_DEPTH) 그 때만 drop
*/ */
static struct k_work_q ble_cmd_work_q; static struct k_work_q ble_cmd_work_q;
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ); K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
static struct k_work ble_cmd_work; // BLE 명령 처리 work static struct k_work ble_cmd_work; // BLE 명령 처리 work
static struct k_spinlock ble_cmd_lock; // RX 공유 버퍼 보호 static struct k_spinlock ble_cmd_lock; // RX 보호
static uint8_t ble_cmd_buf[BLE_CMD_MAX_LEN];// RX 명령 복사 버퍼
static uint16_t ble_cmd_len; // RX 명령 길이 // RX 명령 링버퍼: 들어온 순서대로 쌓고 워커가 순차 처리 (head=꺼낼 위치, tail=넣을 위치)
static bool ble_cmd_pending; // 처리 대기 명령 있음 static uint8_t ble_cmd_q_buf[BLE_CMD_QUEUE_DEPTH][BLE_CMD_MAX_LEN]; // 명령 데이터
static uint16_t ble_cmd_q_len[BLE_CMD_QUEUE_DEPTH]; // 각 명령 길이
static uint8_t ble_cmd_q_head; // 다음에 꺼낼 슬롯
static uint8_t ble_cmd_q_tail; // 다음에 넣을 슬롯
static uint8_t ble_cmd_q_count; // 큐에 쌓인 명령 수
static void ble_cmd_work_handler(struct k_work *work) static void ble_cmd_work_handler(struct k_work *work)
{ {
@@ -139,32 +145,33 @@ static void ble_cmd_work_handler(struct k_work *work)
// work queue 로컬 복사본 // work queue 로컬 복사본
uint8_t local_buf[BLE_CMD_MAX_LEN]; uint8_t local_buf[BLE_CMD_MAX_LEN];
uint16_t local_len = 0U; uint16_t local_len;
// 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관 // 큐가 빌 때까지 head에서 하나씩 꺼내 순차 처리 (dispatch는 락 밖에서)
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock); for (;;)
if (ble_cmd_pending)
{ {
local_len = ble_cmd_len; k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
memcpy(local_buf, ble_cmd_buf, local_len); if (ble_cmd_q_count == 0U)
{
k_spin_unlock(&ble_cmd_lock, key);
break;
}
local_len = ble_cmd_q_len[ble_cmd_q_head];
memcpy(local_buf, ble_cmd_q_buf[ble_cmd_q_head], local_len);
ble_cmd_q_head = (uint8_t)((ble_cmd_q_head + 1U) % BLE_CMD_QUEUE_DEPTH);
ble_cmd_q_count--;
k_spin_unlock(&ble_cmd_lock, key);
if (local_len == 0U)
{
DBG_ERR("[BLE RX] worker: empty\r\n");
continue;
}
// 일반 스레드 문맥 명령 처리
//DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
ble_cmd_dispatch(local_buf, local_len);
} }
k_spin_unlock(&ble_cmd_lock, key);
if (local_len == 0U)
{
DBG_ERR("[BLE RX] worker: empty\r\n");
return;
}
// 일반 스레드 문맥 명령 처리
//DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
ble_cmd_dispatch(local_buf, local_len);
// 명령 처리 완료 표시
key = k_spin_lock(&ble_cmd_lock);
ble_cmd_pending = false;
ble_cmd_len = 0U;
k_spin_unlock(&ble_cmd_lock, key);
} }
/* 시스템 워크큐에서 BLE advertising 시작 */ /* 시스템 워크큐에서 BLE advertising 시작 */
@@ -220,30 +227,27 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
return; return;
} }
// 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관 // 공유 RX 큐 보호: tail 슬롯에 명령 적재 (FIFO)
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock); k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_pending) if (ble_cmd_q_count >= BLE_CMD_QUEUE_DEPTH)
{ {
k_spin_unlock(&ble_cmd_lock, key); k_spin_unlock(&ble_cmd_lock, key);
DBG_ERR("[BLE RX] drop: command busy\r\n"); DBG_ERR("[BLE RX] drop: queue full\r\n");
return; return;
} }
// 명령 버퍼 복사 // 명령 버퍼 복사
memcpy(ble_cmd_buf, data, len); memcpy(ble_cmd_q_buf[ble_cmd_q_tail], data, len);
ble_cmd_len = len; ble_cmd_q_len[ble_cmd_q_tail] = len;
ble_cmd_pending = true; ble_cmd_q_tail = (uint8_t)((ble_cmd_q_tail + 1U) % BLE_CMD_QUEUE_DEPTH);
ble_cmd_q_count++;
k_spin_unlock(&ble_cmd_lock, key); k_spin_unlock(&ble_cmd_lock, key);
// 명령 처리 work 예약 // 명령 처리 work 예약 (워커는 큐가 빌 때까지 처리하므로 중복 예약은 무해)
// 실패해도 항목은 큐에 유효하게 남아 있고, 다음 RX의 submit이 함께 처리하므로 롤백하지 않음
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work); int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
if (err < 0) if (err < 0)
{ {
// 예약 실패 시 pending 복구
key = k_spin_lock(&ble_cmd_lock);
ble_cmd_pending = false;
ble_cmd_len = 0U;
k_spin_unlock(&ble_cmd_lock, key);
DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err); DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err);
} }
} }
@@ -293,6 +297,7 @@ static void power_control_handler(on_off_cont_t device_power_st, const char *rea
{ {
if (device_power_st == OFF) if (device_power_st == OFF)
{ {
/*
DBG_ERR("[PWR] OFF latch release reason=%s pending=%s device_on=%u btn=%d cnt=%u suspended=%u dfu_adv=%u dfu_confirm=%u dfu_resume=%u\r\n", DBG_ERR("[PWR] OFF latch release reason=%s pending=%s device_on=%u btn=%d cnt=%u suspended=%u dfu_adv=%u dfu_confirm=%u dfu_resume=%u\r\n",
reason ? reason : "unknown", reason ? reason : "unknown",
power_off_pending_reason, power_off_pending_reason,
@@ -302,7 +307,7 @@ static void power_control_handler(on_off_cont_t device_power_st, const char *rea
power_btn_suspended ? 1U : 0U, power_btn_suspended ? 1U : 0U,
ble_dfu_advertising_is_enabled() ? 1U : 0U, ble_dfu_advertising_is_enabled() ? 1U : 0U,
dfu_confirm_pending_boot ? 1U : 0U, dfu_confirm_pending_boot ? 1U : 0U,
dfu_reset_resume_request ? 1U : 0U); dfu_reset_resume_request ? 1U : 0U);*/
gpio_pin_set_dt(&power_hold, 0); // P0.08 LOW → 전원 래치 해제 → 전원 차단 gpio_pin_set_dt(&power_hold, 0); // P0.08 LOW → 전원 래치 해제 → 전원 차단
} }
else else
@@ -331,10 +336,12 @@ static void load_default_config(void)
{ {
serial_len = SERIAL_NO_LENGTH; serial_len = SERIAL_NO_LENGTH;
} }
if (hw_len > HW_NO_LENGTH) if (hw_len > HW_NO_LENGTH)
{ {
hw_len = HW_NO_LENGTH; hw_len = HW_NO_LENGTH;
} }
if (passkey_len > PASSKEY_LENGTH) if (passkey_len > PASSKEY_LENGTH)
{ {
passkey_len = PASSKEY_LENGTH; passkey_len = PASSKEY_LENGTH;
@@ -539,16 +546,14 @@ static void timers_start(void)
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT); k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
} }
/* DFU 업로드가 멈춘 채(청크 무진행) 링크만 살아있는 경우, 연결을 강제로 끊어 /* DFU 업로드가 멈춘 채(청크 무진행) 링크만 살아있는 경우 강제 disconnect 후 SMP 재광고(10분 타임아웃) 전환 - 초록 LED 무한 점등/무한 대기 방지 */
* SMP 재광고(10분 타임아웃)로 전환한다. 초록 LED 무한 점등/무한 대기 방지. */
static void dfu_watchdog_handler(struct k_work *work) static void dfu_watchdog_handler(struct k_work *work)
{ {
ARG_UNUSED(work); ARG_UNUSED(work);
if (ble_is_connected()) if (ble_is_connected())
{ {
DBG_PRINTF("[DFU] upload stalled (%ds), forcing disconnect\r\n", DBG_PRINTF("[DFU] upload stalled (%ds), forcing disconnect\r\n", DFU_STALL_TIMEOUT_SEC);
DFU_STALL_TIMEOUT_SEC);
(void)ble_disconnect_active(); (void)ble_disconnect_active();
} }
} }