From eab562d52eb57d44bcd9eb1dc9ca496cbd5fdd3e Mon Sep 17 00:00:00 2001 From: jhchun Date: Thu, 30 Jul 2026 11:51:06 +0900 Subject: [PATCH] =?UTF-8?q?=EC=83=9D=EC=82=B0=EC=9A=A9=20BLE=20write=20?= =?UTF-8?q?=EB=AA=85=EB=A0=B9=20=EC=9E=A0=EA=B8=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - serial number / HW version / passkey write 명령 해당 - production_done NVS 플래그 추가 - 기존 passkey_changed 플래그 제거 - production_done=0 상태에서만 Serial/HW/Passkey Write 허용 - production_done=1 상태에서는 생산용 Write 명령을 rxd 응답으로 차단 - msr 본딩 삭제 성공 시 production_done=1 저장 --- src/command/handlers/cmd_device.c | 38 +++++++++++++++++---------- src/command/handlers/cmd_info.c | 43 ++++++++++++++++++++----------- src/main.c | 4 +-- src/main.h | 2 +- src/nvs/app_nvs.c | 28 ++++++++++---------- src/nvs/app_nvs.h | 2 +- 6 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/command/handlers/cmd_device.c b/src/command/handlers/cmd_device.c index 5999937..8193e57 100644 --- a/src/command/handlers/cmd_device.c +++ b/src/command/handlers/cmd_device.c @@ -12,6 +12,7 @@ #include "cmd_common.h" #include "main.h" #include "debug_print.h" +#include "app_nvs.h" #include "ble_service.h" #include "cmd_device.h" #include "led_control.h" @@ -64,15 +65,6 @@ int cmd_msr(const uint8_t *data, uint8_t data_len) ARG_UNUSED(data); ARG_UNUSED(data_len); - int tx_err = cmd_send_response_u16("rsr:", status); - if (tx_err) - { - DBG_ERR("[CMD] msr tx failed err=%d\r\n", tx_err); - return 1; - } - - k_msleep(100); - #if IS_ENABLED(CONFIG_BT_SMP) int err = bt_unpair(BT_ID_DEFAULT, NULL); if (err) @@ -82,10 +74,9 @@ int cmd_msr(const uint8_t *data, uint8_t data_len) } else { - led_set_state(LED_STATE_BOND_DELETE); // 삭제 완료 확인 주황 LED ON + led_set_state(LED_STATE_BOND_DELETE); bond_data_delete = true; DBG_PRINTF("[CMD] msr bond data deleted\r\n"); - k_msleep(1500); } #else bond_data_delete = true; @@ -94,13 +85,34 @@ int cmd_msr(const uint8_t *data, uint8_t data_len) if (status == 0x0000) { - DBG_PRINTF("[CMD] msr complete -> reboot\r\n"); + int nvs_err = app_nvs_save_production_done(1U); + if (nvs_err) + { + status = (uint16_t)(-nvs_err); + DBG_ERR("[CMD] msr production_done save failed err=%d\r\n", nvs_err); + } + else + { + m_production_done = 1U; + } + } + + int tx_err = cmd_send_response_u16("rsr:", status); + if (tx_err) + { + DBG_ERR("[CMD] msr tx failed err=%d\r\n", tx_err); + return 1; + } + + if (status == 0x0000) + { + DBG_PRINTF("[CMD] msr complete -> production done, reboot\r\n"); + k_msleep(1500); } cmd_reboot_after_response(); return 1; } - /* * mdf: DFU mode enter */ diff --git a/src/command/handlers/cmd_info.c b/src/command/handlers/cmd_info.c index aadd23a..b6bbeef 100644 --- a/src/command/handlers/cmd_info.c +++ b/src/command/handlers/cmd_info.c @@ -11,6 +11,19 @@ #include "app_nvs.h" #include "cmd_info.h" +/* Production Mode - Serial Number, HW Version, Passkey write command lock */ +static bool cmd_info_write_locked(const char *cmd_tag) +{ + if (m_production_done == 0U) + { + return false; + } + + cmd_send_response_tag_echo("rxd:", cmd_tag); + DBG_PRINTF("[CMD] production write disabled: %s\r\n", cmd_tag); + return true; +} + /* * mfv: firmware version read */ @@ -54,6 +67,11 @@ int cmd_mid(const uint8_t *data, uint8_t data_len) */ int cmd_mwh(const uint8_t *data, uint8_t data_len) { + if (cmd_info_write_locked("mwh?")) + { + return 1; + } + if (data_len < HW_NO_LENGTH) { cmd_send_response_u16("rwh:", 0xFFFF); @@ -96,6 +114,11 @@ int cmd_mrh(const uint8_t *data, uint8_t data_len) */ int cmd_mws(const uint8_t *data, uint8_t data_len) { + if (cmd_info_write_locked("mws?")) + { + return 1; + } + if (data_len < SERIAL_NO_LENGTH) { cmd_send_response_u16("rws:", 0xFFFF); @@ -138,19 +161,17 @@ int cmd_mrs(const uint8_t *data, uint8_t data_len) */ int cmd_mpz(const uint8_t *data, uint8_t data_len) { + if (cmd_info_write_locked("mpz?")) + { + return 1; + } + if (data_len < PASSKEY_LENGTH) { cmd_send_response_u16("rpz:", 0xFFFF); return 1; } - if (m_passkey_changed != 0U) - { - // 패스키가 변경된 경우 다시 바꿀 수 없음(FFFF 반환) - cmd_send_response_u16("rpz:", 0xFFFF); - DBG_PRINTF("[CMD] mpz: passkey already changed\r\n"); - return 1; - } // NVS 저장이 모두 성공한 뒤 RAM 값 확정 char new_passkey[PASSKEY_BUF_SIZE]; @@ -164,17 +185,9 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len) return 1; } - // 변경 완료 플래그 세트 : 다음 mpz? 명령 차단 - err = app_nvs_save_passkey_changed(1U); - if (err) - { - cmd_send_response_u16("rpz:", 0xFFFD); - return 1; - } memset(m_static_passkey, 0, sizeof(m_static_passkey)); memcpy(m_static_passkey, new_passkey, PASSKEY_LENGTH); - m_passkey_changed = 1U; cmd_send_response_ascii("rpz:", m_static_passkey, PASSKEY_LENGTH); return 1; diff --git a/src/main.c b/src/main.c index 21194c0..a081e83 100644 --- a/src/main.c +++ b/src/main.c @@ -34,7 +34,7 @@ uint8_t m_reset_status = 1; // 리셋 상태 코드 char SERIAL_NO[SERIAL_NO_BUF_SIZE]; // 시리얼 번호 char HW_NO[HW_NO_BUF_SIZE]; // 하드웨어 번호 char m_static_passkey[PASSKEY_BUF_SIZE]; // BLE 정적 패스키 -uint8_t m_passkey_changed; // 패스키 변경은 출고 후 1회만 허용 +uint8_t m_production_done; // 생산용 기기 정보 변경 제한(0: production writes allowed, 1: operation mode locked) static bool resume_without_power_button; // 리셋 후 버튼 없이 복귀 static bool dfu_confirm_pending_boot; // DFU test 이미지 확인 전 자동 복귀 @@ -108,7 +108,7 @@ static void load_default_config(void) memset(m_static_passkey, 0, sizeof(m_static_passkey)); memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len); - m_passkey_changed = 0U; // 패스키 변경 여부 초기화 + m_production_done = 0U; // 생산용 기기 정보 변경 여부 초기화 m_reset_status = 1; // 기본 reset 상태 코드 설정 bond_data_delete = true; // 기본 상태는 bond 삭제 완료 삭제로 초기화 diff --git a/src/main.h b/src/main.h index 8d257b0..77e326c 100644 --- a/src/main.h +++ b/src/main.h @@ -75,7 +75,7 @@ extern volatile bool processing; extern char SERIAL_NO[SERIAL_NO_BUF_SIZE]; extern char HW_NO[HW_NO_BUF_SIZE]; extern char m_static_passkey[PASSKEY_BUF_SIZE]; -extern uint8_t m_passkey_changed; +extern uint8_t m_production_done; extern bool bond_data_delete; extern uint32_t m_life_cycle; extern uint8_t m_reset_status; diff --git a/src/nvs/app_nvs.c b/src/nvs/app_nvs.c index 7643a50..633ddad 100644 --- a/src/nvs/app_nvs.c +++ b/src/nvs/app_nvs.c @@ -22,14 +22,14 @@ #define APP_NVS_KEY_HW_NO "hw" #define APP_NVS_KEY_SERIAL_NO "serial" #define APP_NVS_KEY_PASSKEY "passkey" -#define APP_NVS_KEY_PASSKEY_CHANGED "passkey_changed" // 패스키 1회 변경 완료 여부 +#define APP_NVS_KEY_PRODUCTION_DONE "production_done" /* settings_save_one에 넘기는 전체 키 */ #define APP_NVS_FULL_KEY_PIEZO "vesiscan/piezo" #define APP_NVS_FULL_KEY_HW_NO "vesiscan/hw" #define APP_NVS_FULL_KEY_SERIAL_NO "vesiscan/serial" #define APP_NVS_FULL_KEY_PASSKEY "vesiscan/passkey" -#define APP_NVS_FULL_KEY_PASSKEY_CHANGED "vesiscan/passkey_changed" // 0은 변경 가능, 1은 변경 차단 +#define APP_NVS_FULL_KEY_PRODUCTION_DONE "vesiscan/production_done" /* parser.c가 가진 피에조 설정을 NVS 로드 시 갱신하기 위한 포인터 */ static piezo_config_t *m_piezo_cfg; @@ -119,7 +119,7 @@ static int app_nvs_settings_set(const char *name, size_t len, settings_read_cb r return read_exact(read_cb, cb_arg, SERIAL_NO, SERIAL_NO_LENGTH, APP_NVS_KEY_SERIAL_NO); } - // 패스키는 mpz? 명령으로 최초 1회만 저장 + // 패스키는 생산 완료 전 mpz? 명령으로 저장 if (settings_name_steq(name, APP_NVS_KEY_PASSKEY, &next) && (next == NULL)) { if (len != PASSKEY_LENGTH) @@ -131,17 +131,15 @@ static int app_nvs_settings_set(const char *name, size_t len, settings_read_cb r memset(m_static_passkey, 0, sizeof(m_static_passkey)); return read_exact(read_cb, cb_arg, m_static_passkey, PASSKEY_LENGTH, APP_NVS_KEY_PASSKEY); } - - if (settings_name_steq(name, APP_NVS_KEY_PASSKEY_CHANGED, &next) && (next == NULL)) + if (settings_name_steq(name, APP_NVS_KEY_PRODUCTION_DONE, &next) && (next == NULL)) { - /* 패스키 재변경 차단 플래그 복원 */ - if (len != sizeof(m_passkey_changed)) + if (len != sizeof(m_production_done)) { - DBG_ERR("[NVS] passkey_changed size mismatch (%u)\r\n", (uint32_t)len); + DBG_ERR("[NVS] production_done size mismatch (%u)\r\n", (uint32_t)len); return -EINVAL; } - return read_exact(read_cb, cb_arg, &m_passkey_changed, sizeof(m_passkey_changed), APP_NVS_KEY_PASSKEY_CHANGED); + return read_exact(read_cb, cb_arg, &m_production_done, sizeof(m_production_done), APP_NVS_KEY_PRODUCTION_DONE); } return -ENOENT; @@ -229,7 +227,7 @@ int app_nvs_save_serial_no(const char *serial_no) int app_nvs_save_passkey(const char *passkey) { #if IS_ENABLED(CONFIG_SETTINGS) - // mpz? 명령이 허용된 뒤 고정 길이 패스키 저장 + // 고정 길이 패스키 저장 int err = settings_save_one(APP_NVS_FULL_KEY_PASSKEY, passkey, PASSKEY_LENGTH); if (err) { @@ -242,18 +240,18 @@ int app_nvs_save_passkey(const char *passkey) #endif } -int app_nvs_save_passkey_changed(uint8_t changed) +/* Production Mode Done */ +int app_nvs_save_production_done(uint8_t done) { #if IS_ENABLED(CONFIG_SETTINGS) - // 재부팅 후에도 mpz?(패스키 변경) 재실행을 막기 위한 플래그 저장(NVS) - int err = settings_save_one(APP_NVS_FULL_KEY_PASSKEY_CHANGED, &changed, sizeof(changed)); + int err = settings_save_one(APP_NVS_FULL_KEY_PRODUCTION_DONE, &done, sizeof(done)); if (err) { - DBG_ERR("[NVS] passkey_changed save failed err=%d\r\n", err); + DBG_ERR("[NVS] production_done save failed err=%d\r\n", err); } return err; #else - ARG_UNUSED(changed); + ARG_UNUSED(done); return 0; #endif } diff --git a/src/nvs/app_nvs.h b/src/nvs/app_nvs.h index 14b269d..8695946 100644 --- a/src/nvs/app_nvs.h +++ b/src/nvs/app_nvs.h @@ -20,6 +20,6 @@ int app_nvs_save_piezo(const piezo_config_t *cfg); int app_nvs_save_hw_no(const char *hw_no); int app_nvs_save_serial_no(const char *serial_no); int app_nvs_save_passkey(const char *passkey); -int app_nvs_save_passkey_changed(uint8_t changed); +int app_nvs_save_production_done(uint8_t done); #endif /* APP_NVS_H__ */