From c11ce4ec3e2d6f4258f73555af8239370b7544ed Mon Sep 17 00:00:00 2001 From: jhchun Date: Thu, 16 Apr 2026 01:20:52 +0900 Subject: [PATCH] =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=BB=A4=EB=A7=A8?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rxx: (Unknown command) - 수신 TAG가 명령 테이블에 없는 경우 - rxd: (Disabled command) - 수신 TAG가 테이블에 있지만 enabled=false로 비활성화된 경우 - rxn: (NULL handler) - 엔트리는 매칭됐지만 함수 포인터가 NULL인 경우(펌웨어 버그 방어) - rxc: (CRC fail) - CRC16 검증 실패 - rxs: (Too short) - 패킷이 너무 짧은 경우(CRC 활성 시 7바이트 미만, 비활성 시 4바이트 미만) --- .../ble_app_bladder_patch/command/parser.c | 95 +++++++++++++------ 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/project/ble_peripheral/ble_app_bladder_patch/command/parser.c b/project/ble_peripheral/ble_app_bladder_patch/command/parser.c index cda563e..39221e0 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/command/parser.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/parser.c @@ -278,6 +278,20 @@ static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len) return dr_crc16_check(packet, data_len, expected_crc); } +/* ---- 에러 응답 헬퍼 ---- + * 패킷: [err_tag 4B] [cmd_tag 에코 4B] = 8바이트 = 4워드 + * CRC16은 tx_bin 레이어에서 자동 부가 + */ +static void dr_send_error(const char *err_tag, const char *cmd_tag) +{ + if (g_plat.tx_bin) { + uint8_t err_buf[8]; + memcpy(&err_buf[0], err_tag, 4); + memcpy(&err_buf[4], cmd_tag, 4); + g_plat.tx_bin(err_buf, 4); + } +} + /* ---- 수신 버퍼 → ParsedCmd 구조체 변환 ---- */ /* 수신된 원시 바이트 버퍼를 파싱하여 TAG와 데이터를 분리 @@ -289,43 +303,57 @@ static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len) */ static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out) { - uint8_t i; + uint8_t data_len; + /* TAG 4바이트조차 수신되지 않음 → rxs: + "????" (TAG 특정 불가) */ if (length < 4) { - return false; /* TAG 4바이트조차 수신되지 않음 */ + dr_send_error("rxs:", "????"); + if (g_plat.log && g_log_enable) { + g_plat.log("[parser] too short (%u bytes) -> rxs:\r\n", length); + } + return false; } + /* TAG 먼저 추출 (에러 응답 에코에 사용) */ + dr_copy_tag(buffer, out->tag); + /* CRC 검증이 활성화된 경우 패킷 무결성 확인 */ if (g_plat.crc_check) { - if (!dr_crc16_check_packet(buffer, length)) - { + if (length < 7) { + dr_send_error("rxs:", out->tag); if (g_plat.log && g_log_enable) { - g_plat.log("CRC check FAILED!\n"); + g_plat.log("[parser] CRC enabled but too short (%u) -> rxs:\r\n", length); } return false; } - /* CRC 검증 성공 → CRC 2바이트를 제외한 실제 데이터 길이로 조정 */ - length = (uint8_t)(length - 2); + if (!dr_crc16_check_packet(buffer, length)) + { + dr_send_error("rxc:", out->tag); + if (g_plat.log && g_log_enable) { + g_plat.log("[parser] CRC mismatch '%s' -> rxc:\r\n", out->tag); + } + return false; + } + + data_len = (uint8_t)(length - 4 - 2); /* TAG(4) + CRC(2) 제외 */ } - - /* TAG 4바이트 복사 */ - dr_copy_tag(buffer, out->tag); - - /* TAG 이후 데이터 길이 계산 (최대 DR_MAX_DATA까지) */ - out->data_len = (length > 4) ? (uint8_t)(length - 4) : 0; - if (out->data_len > DR_MAX_DATA) + else { - out->data_len = DR_MAX_DATA; + data_len = (uint8_t)(length - 4); /* TAG(4) 제외 */ } - /* TAG 뒤의 데이터 바이트를 ParsedCmd.data[]에 복사 */ - for (i = 0; i < out->data_len; i++) - { - out->data[i] = buffer[4 + i]; + /* 최대 길이 클램프 */ + if (data_len > DR_MAX_DATA) { + data_len = DR_MAX_DATA; } + if (data_len > 0) { + memcpy(out->data, buffer + 4, data_len); + } + out->data_len = data_len; + return true; } @@ -488,10 +516,20 @@ static int dr_cmd_dispatch(const ParsedCmd *cmd) for (i = 0; i < g_cmd_count; i++) { if (dr_tag_eq(tag_lower, g_cmd_table[i].tag)) { - /* 비활성화된 명령이면 실행하지 않음 */ + /* 비활성화된 명령 → rxd: 응답 */ if (!g_cmd_table[i].enabled) { + dr_send_error("rxd:", cmd->tag); if (g_plat.log && g_log_enable) { - g_plat.log("Command '%s' disabled\n", cmd->tag); + g_plat.log("Command '%s' disabled -> rxd:\n", cmd->tag); + } + return 0; + } + + /* NULL 핸들러 가드 → rxn: 응답 */ + if (g_cmd_table[i].handler == NULL) { + dr_send_error("rxn:", cmd->tag); + if (g_plat.log) { + g_plat.log("[parser] NULL handler for '%s' -> rxn:\n", cmd->tag); } return 0; } @@ -501,9 +539,10 @@ static int dr_cmd_dispatch(const ParsedCmd *cmd) } } - /* 테이블에 없는 TAG 수신 시 */ + /* 테이블에 없는 TAG → rxx: 응답 */ + dr_send_error("rxx:", cmd->tag); if (g_plat.log && g_log_enable) { - g_plat.log("Unknown TAG '%s'\n", cmd->tag); + g_plat.log("Unknown TAG '%s' -> rxx:\n", cmd->tag); } return 0; } @@ -523,16 +562,10 @@ int dr_cmd_parser(const uint8_t *buf, uint8_t len) { ParsedCmd cmd; - /* 패킷 파싱 (CRC 검증 포함) */ + /* 패킷 파싱 (CRC 검증 포함) - 에러 응답은 dr_parse_cmd 내부에서 전송 */ if (!dr_parse_cmd(buf, len, &cmd)) { if (g_plat.log) g_plat.log("[PARSER] PARSE FAIL\r\n"); - - /* CRC 실패 시 "crc!" 에러 응답을 BLE로 전송 (65530 = 에러 코드) */ - if (g_plat.crc_check && g_plat.tx_bin) { - single_format_data(ble_bin_buffer, "crc!", 65530); - dr_binary_tx_safe(ble_bin_buffer, 3); - } - return -1; /* CRC/파싱 실패 → 음수 반환으로 레거시 파서에 위임 */ + return -1; } /* 수신 명령어 종류 확인용 로그 */