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);
#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
@@ -123,15 +124,20 @@ static struct mgmt_callback smp_cmd_status_callback = {
/*
* BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둠
* - 명령 하나가 끝날 때까지 다음 명령 drop : 확인 필요
* - 연결 직후 초기화 버스트 등 명령이 몰릴 때 대비해 링버퍼(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
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 명령 길이
static bool ble_cmd_pending; // 처리 대기 명령 있음
static struct k_spinlock ble_cmd_lock; // RX 보호
// RX 명령 링버퍼: 들어온 순서대로 쌓고 워커가 순차 처리 (head=꺼낼 위치, tail=넣을 위치)
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)
{
@@ -139,32 +145,33 @@ static void ble_cmd_work_handler(struct k_work *work)
// work queue 로컬 복사본
uint8_t local_buf[BLE_CMD_MAX_LEN];
uint16_t local_len = 0U;
uint16_t local_len;
// 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_pending)
// 큐가 빌 때까지 head에서 하나씩 꺼내 순차 처리 (dispatch는 락 밖에서)
for (;;)
{
local_len = ble_cmd_len;
memcpy(local_buf, ble_cmd_buf, local_len);
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
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 시작 */
@@ -220,30 +227,27 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
return;
}
// 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관
// 공유 RX 큐 보호: tail 슬롯에 명령 적재 (FIFO)
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);
DBG_ERR("[BLE RX] drop: command busy\r\n");
DBG_ERR("[BLE RX] drop: queue full\r\n");
return;
}
// 명령 버퍼 복사
memcpy(ble_cmd_buf, data, len);
ble_cmd_len = len;
ble_cmd_pending = true;
memcpy(ble_cmd_q_buf[ble_cmd_q_tail], data, len);
ble_cmd_q_len[ble_cmd_q_tail] = len;
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);
// 명령 처리 work 예약
// 명령 처리 work 예약 (워커는 큐가 빌 때까지 처리하므로 중복 예약은 무해)
// 실패해도 항목은 큐에 유효하게 남아 있고, 다음 RX의 submit이 함께 처리하므로 롤백하지 않음
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
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);
}
}
@@ -293,6 +297,7 @@ static void power_control_handler(on_off_cont_t device_power_st, const char *rea
{
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",
reason ? reason : "unknown",
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,
ble_dfu_advertising_is_enabled() ? 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 → 전원 래치 해제 → 전원 차단
}
else
@@ -331,10 +336,12 @@ static void load_default_config(void)
{
serial_len = SERIAL_NO_LENGTH;
}
if (hw_len > HW_NO_LENGTH)
{
hw_len = HW_NO_LENGTH;
}
if (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);
}
/* DFU 업로드가 멈춘 채(청크 무진행) 링크만 살아있는 경우, 연결을 강제로 끊어
* SMP 재광고(10분 타임아웃)로 전환한다. 초록 LED 무한 점등/무한 대기 방지. */
/* DFU 업로드가 멈춘 채(청크 무진행) 링크만 살아있는 경우 강제 disconnect 후 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);
DBG_PRINTF("[DFU] upload stalled (%ds), forcing disconnect\r\n", DFU_STALL_TIMEOUT_SEC);
(void)ble_disconnect_active();
}
}