瀏覽代碼

feat(gmsp): CRC16-CCITT software module + binary frame protocol

Task 5: CRC16-CCITT (poly 0x1021, init 0xFFFF) software implementation
- 256-entry lookup table (512B const in flash)
- init/update/final incremental + one-shot calc API
- Test vector: '123456789' -> 0x29B1

Task 6: Binary frame protocol parser and response builder
- State machine: IDLE->SYNC1->SYNC2->LEN_HI->LEN_LO->CMD->PAYLOAD->CRC_HI->CRC_LO->DONE
- 17 command codes (SM2/SM3/SM4/cert/info) + 9 status codes
- Zero-copy payload pointer into receive buffer
- Response builder with CRC16 over LEN+STATUS+DATA
kennyh 1 周之前
父節點
當前提交
73e9c73a4d

+ 9 - 0
.sisyphus/boulder.json

@@ -30,6 +30,15 @@
       "agent": "Sisyphus-Junior",
       "category": "deep",
       "updated_at": "2026-06-15T08:47:33.449Z"
+    },
+    "todo:5": {
+      "task_key": "todo:5",
+      "task_label": "5",
+      "task_title": "CRC16 帧校验模块",
+      "session_id": "ses_13585cbf9ffeWXjn3i8PBpY0S4",
+      "agent": "Sisyphus-Junior",
+      "category": "deep",
+      "updated_at": "2026-06-15T09:09:27.719Z"
     }
   }
 }

+ 51 - 12
.sisyphus/notepads/guomi-smart-card/learnings.md

@@ -58,37 +58,37 @@
 - Commit + push after each task completion
 
 
-## GMSP Transport Layer (Task 3+4) ¡ª Implementation Details
+## GMSP Transport Layer (Task 3+4) �� Implementation Details
 
 ### DMA LLI Ping-Pong Pattern
 - Existing `dma_spi_LLIReceive()` creates a SELF-LOOP LLI (next points to itself)
 - For Ping-Pong, need TWO LLI nodes: node[0].next_lli = &node[1], node[1].next_lli = &node[0]
 - DMA_LLI struct has 7 fields but hardware only reads first 5 (src_addr, dst_addr, next_lli, control0, len)
-- Extra fields (hs_sel, per) are software metadata ¡ª NOT loaded by DMA hardware
+- Extra fields (hs_sel, per) are software metadata �� NOT loaded by DMA hardware
 - CFG and CFG_HIGH registers persist across LLI transitions (set once, not per-node)
 - control0 value: `DMA_IE | SNC | DI | LLP_DST_EN | LLP_SRC_EN | P2M_DMA` = 0x18200401
 
 ### Buffer Completion Tracking
 - Cannot reliably determine completed buffer from DMA_DADDR (race with ongoing transfer)
 - Cannot read DMA_LLP register (may not update during LLI operation)
-- SOLUTION: toggle counter `gmsp_currently_filling` ¡ª starts at 0 (buf_A), XOR'd in ISR after each completion
+- SOLUTION: toggle counter `gmsp_currently_filling` �� starts at 0 (buf_A), XOR'd in ISR after each completion
 - `gmsp_completed_buf = gmsp_currently_filling` captures the just-finished buffer before toggling
 
 ### ISR Variable Scope
 - ALL ISR-shared variables MUST be non-static if accessed from a different .c file
-- `gmsp_currently_filling` was initially `static volatile` ¡ú broke extern in gmsp_isr.c
+- `gmsp_currently_filling` was initially `static volatile` �� broke extern in gmsp_isr.c
 - Pattern: define in spi_slave.c, `extern volatile` in gmsp_isr.c
 
 ### Response Path Design
 - Short responses (<=8 bytes = SPI_FIFO_SIZE): SPI_SlaveSendData (CPU register loop)
-- Long responses (>8 bytes): dma_spitran(0, resp, dummy, len, FALSE) ¡ª blocking DMA
-- dma_spitran uses BOTH CH0 (TX) and CH1 (RX) ¡ª must stop LLI RX on CH1 first
+- Long responses (>8 bytes): dma_spitran(0, resp, dummy, len, FALSE) �� blocking DMA
+- dma_spitran uses BOTH CH0 (TX) and CH1 (RX) �� must stop LLI RX on CH1 first
 - After response: caller must call gmsp_transport_rearm() to restart LLI
-- dma_spitran disables DMAC (DMA_CONFIG=0, DMA_CHEN=0) at end ¡ª rearm must re-enable
+- dma_spitran disables DMAC (DMA_CONFIG=0, DMA_CHEN=0) at end �� rearm must re-enable
 
 ### IRQ3/IRQ4 Vector Table Repointing
-- ISR23 (IRQ3): was `PIT32_SPI1_SPI2_ISR` ¡ú now `gmsp_irq3_handler`
-- ISR24 (IRQ4): was `DMA_IRQHandler` (from .a lib) ¡ú now `gmsp_dma_irq_handler`
+- ISR23 (IRQ3): was `PIT32_SPI1_SPI2_ISR` �� now `gmsp_irq3_handler`
+- ISR24 (IRQ4): was `DMA_IRQHandler` (from .a lib) �� now `gmsp_dma_irq_handler`
 - Must add `extern void` declarations in vector_table.h for new handlers
 - Original `PIT32_SPI1_SPI2_ISR` in pit32_drv.c still exists but is no longer called
 - Original `DMA_IRQHandler` in libCUni360S_mcc.a still exists but vector no longer points to it
@@ -102,9 +102,48 @@
 - SPISR is at offset 0x16 in SPI_TypeDef, union with SPISRHW (16-bit) at same offset
 - Read `SPI1->SPISRHW` for full 16-bit status in IRQ3 handler
 - Error bits: SPISR_RXFOVF(0x0400), SPISR_RXFUDF(0x0200), SPISR_TXFOVF(0x4000), SPISR_TXFUDF(0x2000), SPISR_MODF(0x0010), SPISR_FLOST(0x0040)
-- In DMA mode, SPI interrupt enables should be off ¡ª IRQ3 only fires for PIT32
+- In DMA mode, SPI interrupt enables should be off �� IRQ3 only fires for PIT32
 
 ### LSP Diagnostics
-- clangd cannot resolve Eclipse CDT include paths ¡ª ALL source files show "file not found" for type.h, memmap.h etc.
+- clangd cannot resolve Eclipse CDT include paths �� ALL source files show "file not found" for type.h, memmap.h etc.
 - This is project-wide, NOT a code error. Same errors exist on dmac_drv.c, spi_demo.c etc.
-- Verification: compare new file errors against existing file errors ¡ª if same pattern, code is correct
+- Verification: compare new file errors against existing file errors �� if same pattern, code is correct
+
+## GMSP Protocol Layer (Task 5+6) �� Implementation Details
+
+### CRC16-CCITT Software Implementation
+- Hardware CRC engine CANNOT do CRC16-CCITT (poly 0x1021) �� confirmed again
+- 256-entry lookup table (512 bytes const in flash) is pre-computed, stored in .rodata
+- Table generation: for each i in 0..255, crc = i<<8, then 8 rounds of (crc<<1 ^ 0x1021 if MSB set)
+- Algorithm per byte: `idx = (crc >> 8) ^ data[i]; crc = (crc << 8) ^ table[idx];`
+- Test vector verified: ASCII "123456789" �� 0x29B1
+- Incremental API (init/update/final) uses a static context variable g_crc_ctx
+- gmsp_crc16_calc() is a one-shot wrapper: init + update + final
+
+### Frame Protocol Design Decisions
+- Frame format: SYNC(0xAA,0x55) + LEN(big-endian, 2B) + CMD/STATUS(1B) + PAYLOAD(N) + CRC16(big-endian, 2B)
+- LEN = payload length only (NOT including CMD/STATUS or CRC)
+- Total frame size = LEN + 7 (SYNC2 + LEN2 + CMD/STATUS1 + CRC2)
+- CRC coverage: LEN(2) + CMD/STATUS(1) + PAYLOAD/RESPONSE(N) = everything between SYNC and CRC
+- CRC is computed incrementally as bytes arrive through the state machine
+- Both LEN and CRC are big-endian (MSB first on wire), matching SYNC byte order
+
+### State Machine Parser
+- States: IDLE �� SYNC1 �� SYNC2 �� LEN_HI �� LEN_LO �� CMD �� PAYLOAD �� CRC_HI �� CRC_LO �� DONE
+- State semantics: "what was the last significant byte received"
+- LEN_LO state processes CMD byte; CMD state processes first payload byte (or CRC MSB if len==0)
+- Parser uses static variables for multi-buffer resilience (though frames fit in single 256B buffer)
+- g_payload_ptr points directly into raw_buf �� NO memory copy/allocation
+- On CRC mismatch: state resets to IDLE and continues scanning remaining bytes in buffer
+- SYNC1 state handles repeated 0xAA bytes (stays in SYNC1 for re-sync)
+
+### Response Builder
+- Response frame: SYNC + LEN + STATUS + DATA + CRC (same layout, STATUS replaces CMD)
+- cmd parameter is accepted but NOT written to frame (per spec: no CMD echo in response)
+- CRC covers LEN(2) + STATUS(1) + DATA(resp_len) = resp_len + 3 bytes
+- Returns total frame length = resp_len + 7
+
+### Verification Notes
+- LSP diagnostics show ONLY type.h-not-found errors (same as all project files)
+- No structural/syntax errors in any of the 4 new files
+- Round-trip test: build frame �� parse frame �� verify CRC and fields �� PASS

+ 2 - 2
.sisyphus/plans/guomi-smart-card.md

@@ -419,7 +419,7 @@ Wave FINAL (验证 — 4 tasks, 并行):
   - Files: `include/vector_table.h`, `src/gmsp/transport/gmsp_isr.c`
   - Pre-commit: 编译通过
 
-- [ ] 5. CRC16 帧校验模块
+- [x] 5. CRC16 帧校验模块
 
   **What to do**:
   - 创建 `src/gmsp/protocol/gmsp_crc.c/.h`
@@ -468,7 +468,7 @@ Wave FINAL (验证 — 4 tasks, 并行):
   - Files: `src/gmsp/protocol/gmsp_crc.c`, `src/gmsp/protocol/gmsp_crc.h`
   - Pre-commit: 编译通过
 
-- [ ] 6. 帧协议(解析+封装)
+- [x] 6. 帧协议(解析+封装)
 
   **What to do**:
   - 创建 `src/gmsp/protocol/gmsp_frame.c/.h`

+ 95 - 0
src/gmsp/protocol/gmsp_crc.c

@@ -0,0 +1,95 @@
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// File name    : gmsp_crc.c
+// Description  : CRC16-CCITT (polynomial 0x1021, init 0xFFFF) software
+//                implementation using a 256-entry lookup table.
+//
+// Parameters:  poly    = 0x1021 (CCITT)
+//              init    = 0xFFFF
+//              refin   = false (input not reflected)
+//              refout  = false (output not reflected)
+//              xorout  = 0x0000
+//
+// Test vector: ASCII "123456789" (0x31..0x39) -> 0x29B1
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#include "gmsp_crc.h"
+
+/* ---- Pre-computed lookup table (poly 0x1021, non-reflected) ----------- */
+/* Generated at compile time as a const array — stored in flash/.rodata    */
+static const UINT16 crc16_ccitt_table[256] = {
+    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+    0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+    0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+    0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+    0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+    0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+    0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+    0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+    0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+    0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+    0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+    0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+    0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+    0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+    0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+    0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+    0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+    0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+    0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+    0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+    0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+    0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+    0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+    0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+    0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+    0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+    0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+    0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+    0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+    0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+    0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+    0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+/* ---- Incremental CRC context ------------------------------------------ */
+static UINT16 g_crc_ctx;
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_init
+*******************************************************************************/
+void gmsp_crc16_init(void)
+{
+    g_crc_ctx = GMSP_CRC16_INIT;
+}
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_update
+*******************************************************************************/
+void gmsp_crc16_update(const UINT8 *data, UINT16 len)
+{
+    UINT16 i;
+    UINT8  idx;
+
+    for (i = 0; i < len; i++) {
+        idx = (UINT8)((g_crc_ctx >> 8) ^ data[i]);
+        g_crc_ctx = (UINT16)((g_crc_ctx << 8) ^ crc16_ccitt_table[idx]);
+    }
+}
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_final
+*******************************************************************************/
+UINT16 gmsp_crc16_final(void)
+{
+    return g_crc_ctx;
+}
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_calc
+*******************************************************************************/
+UINT16 gmsp_crc16_calc(const UINT8 *data, UINT16 len)
+{
+    gmsp_crc16_init();
+    gmsp_crc16_update(data, len);
+    return gmsp_crc16_final();
+}

+ 63 - 0
src/gmsp/protocol/gmsp_crc.h

@@ -0,0 +1,63 @@
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// File name    : gmsp_crc.h
+// Description  : CRC16-CCITT (polynomial 0x1021, init 0xFFFF) software
+//                implementation for GMSP protocol frame integrity.
+//                The on-chip CRC hardware engine does NOT support the
+//                CCITT polynomial, so a software lookup-table approach
+//                is used instead.
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#ifndef GMSP_CRC_H_
+#define GMSP_CRC_H_
+
+#include "type.h"
+
+/* ---- CRC16-CCITT parameters ------------------------------------------- */
+#define GMSP_CRC16_POLY     0x1021u   /* CCITT standard polynomial */
+#define GMSP_CRC16_INIT     0xFFFFu   /* Initial CRC value          */
+
+/* ---- Public API ------------------------------------------------------- */
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_init
+* Description    : Reset the incremental CRC context to the initial value.
+*                  Call before starting a new CRC calculation with the
+*                  update/final pattern.
+* Input          : None
+* Output         : None
+* Return         : None
+*******************************************************************************/
+void gmsp_crc16_init(void);
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_update
+* Description    : Feed data bytes into the incremental CRC context.
+* Input          : data - pointer to data buffer
+*                  len  - number of bytes
+* Output         : None
+* Return         : None
+*******************************************************************************/
+void gmsp_crc16_update(const UINT8 *data, UINT16 len);
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_final
+* Description    : Obtain the final CRC16 value after all data has been fed.
+*                  The context is not modified; call gmsp_crc16_init() to
+*                  start a new calculation.
+* Input          : None
+* Output         : None
+* Return         : Computed CRC16 value
+*******************************************************************************/
+UINT16 gmsp_crc16_final(void);
+
+/*******************************************************************************
+* Function Name  : gmsp_crc16_calc
+* Description    : One-shot convenience: compute CRC16 over a data buffer.
+* Input          : data - pointer to data buffer
+*                  len  - number of bytes
+* Output         : None
+* Return         : Computed CRC16 value
+*******************************************************************************/
+UINT16 gmsp_crc16_calc(const UINT8 *data, UINT16 len);
+
+#endif /* GMSP_CRC_H_ */

+ 204 - 0
src/gmsp/protocol/gmsp_frame.c

@@ -0,0 +1,204 @@
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// File name    : gmsp_frame.c
+// Description  : GMSP binary frame protocol implementation.
+//                State-machine parser for command frames and builder
+//                for response frames.
+//
+// CRC coverage: LEN(2) + CMD/STATUS(1) + PAYLOAD/RESPONSE(N)
+//               (everything between SYNC and CRC fields)
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#include "gmsp_frame.h"
+#include "gmsp_crc.h"
+
+/* ---- Parser state machine context (static) ---------------------------- */
+static gmsp_frame_state_t g_state;
+static UINT16 g_len;           /* Payload length from LEN field            */
+static UINT16 g_payload_cnt;   /* Number of payload bytes received so far  */
+static UINT8  g_cmd;           /* Received command code                    */
+static UINT8 *g_payload_ptr;   /* Pointer to payload start in receive buf  */
+static UINT16 g_crc_recv;      /* CRC value received from wire             */
+
+/*******************************************************************************
+* Function Name  : gmsp_frame_reset
+*******************************************************************************/
+void gmsp_frame_reset(void)
+{
+    g_state       = GMSP_ST_IDLE;
+    g_len         = 0;
+    g_payload_cnt = 0;
+    g_cmd         = 0;
+    g_payload_ptr = (UINT8 *)0;
+    g_crc_recv    = 0;
+}
+
+/*******************************************************************************
+* Function Name  : gmsp_frame_parse
+* Description    : Process raw bytes through the state machine.
+*                  On a complete valid frame, fills out_frame and returns 1.
+*                  The payload pointer in out_frame references directly into
+*                  raw_buf — no memory allocation or copy is performed.
+*******************************************************************************/
+UINT8 gmsp_frame_parse(UINT8 *raw_buf, UINT16 raw_len, GMSP_FRAME *out_frame)
+{
+    UINT16 i;
+    UINT8  byte;
+
+    for (i = 0; i < raw_len; i++) {
+        byte = raw_buf[i];
+
+        switch (g_state) {
+
+        /* --- Scanning for first sync byte (0xAA) ---------------------- */
+        case GMSP_ST_IDLE:
+            if (byte == GMSP_SYNC_HI) {
+                g_state = GMSP_ST_SYNC1;
+            }
+            break;
+
+        /* --- Got 0xAA, expecting 0x55 -------------------------------- */
+        case GMSP_ST_SYNC1:
+            if (byte == GMSP_SYNC_LO) {
+                g_state = GMSP_ST_SYNC2;
+            } else if (byte == GMSP_SYNC_HI) {
+                /* Consecutive 0xAA — treat latest as new sync start */
+                /* Stay in SYNC1 */
+            } else {
+                g_state = GMSP_ST_IDLE;
+            }
+            break;
+
+        /* --- Sync complete; this byte is LEN MSB (big-endian) -------- */
+        case GMSP_ST_SYNC2:
+            gmsp_crc16_init();
+            gmsp_crc16_update(&byte, 1);
+            g_len = (UINT16)byte << 8;
+            g_state = GMSP_ST_LEN_HI;
+            break;
+
+        /* --- Got LEN MSB; this byte is LEN LSB ----------------------- */
+        case GMSP_ST_LEN_HI:
+            gmsp_crc16_update(&byte, 1);
+            g_len |= (UINT16)byte;
+            if (g_len > GMSP_MAX_PAYLOAD) {
+                /* Length exceeds maximum — abort and re-sync */
+                g_state = GMSP_ST_IDLE;
+            } else {
+                g_state = GMSP_ST_LEN_LO;
+            }
+            break;
+
+        /* --- Got full LEN; this byte is CMD -------------------------- */
+        case GMSP_ST_LEN_LO:
+            gmsp_crc16_update(&byte, 1);
+            g_cmd = byte;
+            g_payload_cnt = 0;
+            if (g_len > 0u) {
+                g_state = GMSP_ST_CMD;       /* Expect payload[0] next */
+            } else {
+                g_payload_ptr = (UINT8 *)0;
+                g_state = GMSP_ST_CRC_HI;    /* No payload — expect CRC */
+            }
+            break;
+
+        /* --- Got CMD; this byte is first payload byte ---------------- */
+        case GMSP_ST_CMD:
+            gmsp_crc16_update(&byte, 1);
+            g_payload_ptr = &raw_buf[i];     /* Points into receive buffer */
+            g_payload_cnt = 1;
+            if (g_payload_cnt >= g_len) {
+                g_state = GMSP_ST_CRC_HI;    /* All payload received */
+            } else {
+                g_state = GMSP_ST_PAYLOAD;
+            }
+            break;
+
+        /* --- Collecting remaining payload bytes ---------------------- */
+        case GMSP_ST_PAYLOAD:
+            gmsp_crc16_update(&byte, 1);
+            g_payload_cnt++;
+            if (g_payload_cnt >= g_len) {
+                g_state = GMSP_ST_CRC_HI;    /* All payload received */
+            }
+            break;
+
+        /* --- All data received; this byte is CRC MSB (big-endian) ---- */
+        case GMSP_ST_CRC_HI:
+            g_crc_recv = (UINT16)byte << 8;
+            g_state = GMSP_ST_CRC_LO;
+            break;
+
+        /* --- Got CRC MSB; this byte is CRC LSB — validate frame ------ */
+        case GMSP_ST_CRC_LO:
+            g_crc_recv |= (UINT16)byte;
+            {
+                UINT16 crc_calc = gmsp_crc16_final();
+                /* Reset state machine for next frame regardless of result */
+                g_state = GMSP_ST_IDLE;
+                if (crc_calc == g_crc_recv) {
+                    out_frame->cmd         = g_cmd;
+                    out_frame->payload     = g_payload_ptr;
+                    out_frame->payload_len = g_len;
+                    return 1;               /* Valid frame */
+                }
+                /* CRC mismatch — continue scanning remaining bytes */
+            }
+            break;
+
+        /* --- Should not normally reach DONE state here --------------- */
+        case GMSP_ST_DONE:
+            g_state = GMSP_ST_IDLE;
+            break;
+
+        default:
+            g_state = GMSP_ST_IDLE;
+            break;
+        }
+    }
+
+    /* No complete valid frame found in this buffer */
+    return 0;
+}
+
+/*******************************************************************************
+* Function Name  : gmsp_frame_build_response
+* Description    : Assemble a response frame into out_buf.
+*                  Frame: SYNC(2) + LEN(2) + STATUS(1) + DATA(resp_len) + CRC(2)
+*                  Total length returned = resp_len + 7.
+*******************************************************************************/
+UINT16 gmsp_frame_build_response(UINT8 cmd, UINT8 status,
+                                 const UINT8 *resp_data, UINT16 resp_len,
+                                 UINT8 *out_buf)
+{
+    UINT16 crc;
+    UINT16 offset = 0;
+    UINT16 j;
+
+    (void)cmd;  /* Command echo not included in response frame format */
+
+    /* --- SYNC bytes -------------------------------------------------- */
+    out_buf[offset++] = GMSP_SYNC_HI;
+    out_buf[offset++] = GMSP_SYNC_LO;
+
+    /* --- LEN field (big-endian) = response data length --------------- */
+    out_buf[offset++] = (UINT8)(resp_len >> 8);
+    out_buf[offset++] = (UINT8)(resp_len & 0xFF);
+
+    /* --- STATUS byte ------------------------------------------------- */
+    out_buf[offset++] = status;
+
+    /* --- RESPONSE data ----------------------------------------------- */
+    if (resp_data != (const UINT8 *)0 && resp_len > 0u) {
+        for (j = 0; j < resp_len; j++) {
+            out_buf[offset++] = resp_data[j];
+        }
+    }
+
+    /* --- CRC16 (big-endian) over LEN+STATUS+DATA -------------------- */
+    /* CRC data starts at offset 2 (skip SYNC), length = offset - 2     */
+    crc = gmsp_crc16_calc(&out_buf[2], (UINT16)(offset - 2u));
+    out_buf[offset++] = (UINT8)(crc >> 8);
+    out_buf[offset++] = (UINT8)(crc & 0xFF);
+
+    return offset;  /* = resp_len + 7 */
+}

+ 129 - 0
src/gmsp/protocol/gmsp_frame.h

@@ -0,0 +1,129 @@
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// File name    : gmsp_frame.h
+// Description  : GMSP binary frame protocol — constants, data types,
+//                parser and response builder API.
+//
+// Frame layout (host -> device, command frame):
+//   +--------+--------+--------+--------+------+---------+--------+--------+
+//   | SYNC   | SYNC   | LEN    | LEN    | CMD  | PAYLOAD | CRC    | CRC    |
+//   | 0xAA   | 0x55   | HI     | LO     | (1B) | (N B)   | HI     | LO     |
+//   +--------+--------+--------+--------+------+---------+--------+--------+
+//   LEN = payload length N (big-endian, MSB first).
+//   CRC covers LEN_HI..PAYLOAD (everything between SYNC and CRC fields).
+//   Total frame size = N + 7.
+//
+// Frame layout (device -> host, response frame):
+//   +--------+--------+--------+--------+--------+----------+--------+--------+
+//   | SYNC   | SYNC   | LEN    | LEN    | STATUS | RESPONSE | CRC    | CRC    |
+//   | 0xAA   | 0x55   | HI     | LO     | (1B)   | (N B)    | HI     | LO     |
+//   +--------+--------+--------+--------+--------+----------+--------+--------+
+//   LEN = response data length N.
+//   CRC covers LEN_HI..RESPONSE.
+//   Total frame size = N + 7.
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#ifndef GMSP_FRAME_H_
+#define GMSP_FRAME_H_
+
+#include "type.h"
+
+/* ---- Sync bytes ------------------------------------------------------- */
+#define GMSP_SYNC_HI        0xAAu
+#define GMSP_SYNC_LO        0x55u
+
+/* ---- Frame size limits ------------------------------------------------ */
+#define GMSP_MAX_PAYLOAD    240u    /* Leave room for headers in 256B buffer */
+#define GMSP_FRAME_OVERHEAD 7u      /* SYNC(2)+LEN(2)+CMD/STATUS(1)+CRC(2)   */
+
+/* ---- Command codes ---------------------------------------------------- */
+#define CMD_SM2_KEYGEN      0x01u
+#define CMD_SM2_SIGN        0x02u
+#define CMD_SM2_VERIFY      0x03u
+#define CMD_SM2_ENCRYPT     0x04u
+#define CMD_SM2_DECRYPT     0x05u
+#define CMD_SM2_KEYEX       0x06u
+#define CMD_SM3_HASH        0x10u
+#define CMD_SM4_ECB_ENC     0x20u
+#define CMD_SM4_ECB_DEC     0x21u
+#define CMD_SM4_CBC_ENC     0x22u
+#define CMD_SM4_CBC_DEC     0x23u
+#define CMD_SM4_STREAM_INIT 0x24u
+#define CMD_SM4_STREAM_UPD  0x25u
+#define CMD_SM4_STREAM_FIN  0x26u
+#define CMD_EXPORT_PUBKEY   0x30u
+#define CMD_GEN_CERT        0x31u
+#define CMD_GET_INFO        0xF0u
+
+/* ---- Status codes ----------------------------------------------------- */
+#define STAT_SUCCESS          0x00u
+#define STAT_BAD_CRC          0x01u
+#define STAT_BAD_LEN          0x02u
+#define STAT_BAD_PARAM        0x03u
+#define STAT_ERR_KEY          0x04u
+#define STAT_ERR_CRYPTO       0x05u
+#define STAT_ERR_KEY_NOT_INIT 0x06u
+#define STAT_ERR_SESSION      0x07u
+#define STAT_UNKNOWN_CMD      0xFFu
+
+/* ---- Parser state machine states -------------------------------------- */
+typedef enum {
+    GMSP_ST_IDLE = 0,
+    GMSP_ST_SYNC1,      /* Expecting first sync byte (0xAA) */
+    GMSP_ST_SYNC2,      /* Expecting second sync byte (0x55) */
+    GMSP_ST_LEN_HI,     /* First  LEN byte (big-endian: high byte) */
+    GMSP_ST_LEN_LO,     /* Second LEN byte (big-endian: low byte)  */
+    GMSP_ST_CMD,        /* Command byte */
+    GMSP_ST_PAYLOAD,    /* Payload data */
+    GMSP_ST_CRC_HI,     /* First  CRC byte (big-endian: high byte) */
+    GMSP_ST_CRC_LO,     /* Second CRC byte (big-endian: low byte)  */
+    GMSP_ST_DONE        /* Frame fully received */
+} gmsp_frame_state_t;
+
+/* ---- Parsed frame descriptor ------------------------------------------ */
+typedef struct {
+    UINT8   cmd;          /* Command code                   */
+    UINT8  *payload;      /* Pointer into raw buffer (no copy) */
+    UINT16  payload_len;  /* Number of payload bytes         */
+} GMSP_FRAME;
+
+/* ---- Public API ------------------------------------------------------- */
+
+/*******************************************************************************
+* Function Name  : gmsp_frame_reset
+* Description    : Reset the internal parser state machine to IDLE.
+* Input          : None
+* Output         : None
+* Return         : None
+*******************************************************************************/
+void gmsp_frame_reset(void);
+
+/*******************************************************************************
+* Function Name  : gmsp_frame_parse
+* Description    : Parse a raw byte buffer received from the transport layer
+*                  into a validated GMSP_FRAME structure.
+*                  Uses the state machine to scan for sync alignment, validate
+*                  length and CRC16.
+* Input          : raw_buf  - pointer to raw received bytes
+*                  raw_len  - number of raw bytes available
+* Output         : out_frame - filled with cmd, payload pointer, payload_len
+*                             (payload pointer references into raw_buf)
+* Return         : 1 on success (valid frame found)
+*                  0 on failure (no valid frame)
+*******************************************************************************/
+UINT8 gmsp_frame_parse(UINT8 *raw_buf, UINT16 raw_len, GMSP_FRAME *out_frame);
+
+/*******************************************************************************
+* Function Name  : gmsp_frame_build_response
+* Description    : Build a complete response frame into the output buffer.
+* Input          : cmd       - original command code (echoed for context)
+*                  status    - status code (STAT_*)
+*                  resp_data - pointer to response payload data (may be NULL)
+*                  resp_len  - number of response payload bytes
+* Output         : out_buf   - destination buffer (must be >= resp_len + 7)
+* Return         : Total frame length written to out_buf (= resp_len + 7)
+*******************************************************************************/
+UINT16 gmsp_frame_build_response(UINT8 cmd, UINT8 status,
+                                 const UINT8 *resp_data, UINT16 resp_len,
+                                 UINT8 *out_buf);
+
+#endif /* GMSP_FRAME_H_ */