/*
 * 模块名称：CH395F SPI Ethernet Protocol Stack Driver
 * 模块功能：CH395F 以太网协议栈芯片 SPI 驱动实现，提供芯片检测、初始化�? *          网络参数配置、PHY 管理、DHCP、Socket 管理及数据收发功�? * 适用平台：STM32F4 系列（SPI2 接口�? * 作者：王建�? * 创建日期�?026-03-14
 * 修改记录�? *     2026-03-14  王建�? 创建初始版本，按代码规范重构
 *     2026-07-18  王建�? 改�?write_send_buf / read_recv_buf 使用 SPI2 DMA 批量传输
 */

/* 头文件包含区 - 先包含系统头文件，再包含本模块头文件 */
#include <string.h>
#include <stdio.h>
#include "ch395f.h"
#include "spi.h"
#include "usart.h"

/*
 * 调试输出配置
 */
#define DBG_TAG "[CH395]"
#include "dbg_log.h"

/*
 * 私有宏定义区
 */
#define CH395F_CHECK_EXIST_TEST_DATA   0x57U  /* 芯片检测测试字�?*/
#define CH395F_RESET_DELAY_MS          20U    /* 复位后等待延时（ms�?*/
#define CH395F_INIT_TIMEOUT_MS         500U   /* 初始化超时时间（ms�?*/
#define CH395F_SPI_BEGIN_DELAY_MS      1U     /* SPI 事务开始延时（ms�?*/
#define CH395F_DMA_BUF_SIZE            1500U  /* DMA 缓冲区最大字节数 */

/*
 * 私有变量�?- DMA 传输相关
 */
static volatile uint8_t s_spi2_dma_done = 0U;   /* DMA 传输完成标志 */

__attribute__((aligned(4)))
static uint8_t s_spi2_dma_tx_buf[CH395F_DMA_BUF_SIZE]; /* DMA 发送缓冲区 */
__attribute__((aligned(4)))
static uint8_t s_spi2_dma_rx_buf[CH395F_DMA_BUF_SIZE]; /* DMA 接收缓冲�?*/

/*
 * 私有函数声明�? */
static void ch395f_spi_begin(void);
static void ch395f_spi_end(void);
static void ch395f_spi_dma_transfer(uint16_t len);

/*
 * 私有函数实现�? */

/*
 * 函数功能：开�?SPI 事务（CS 拉高 -> 拉低�? * 函数说明：手册要�?SPI 模式下字节间无需间隔�? *          CS 拉高再拉低满足最小片选间隔要�? */
static void ch395f_spi_begin(void) {
    /* CS 先拉高，确保空闲状�?*/
    HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
    /* CS 拉低，启动事务（无需额外延时，SPI 时序�?SCK 频率决定�?*/
    HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_RESET);
}

/*
 * 函数功能：结�?SPI 事务（CS 拉高�? */
static void ch395f_spi_end(void) {
    /* CS 拉高，结束事�?*/
    HAL_GPIO_WritePin(CH395_SCS_GPIO_Port, CH395_SCS_Pin, GPIO_PIN_SET);
}

/*
 * 函数功能：DMA 全双工传输并等待完成
 * 入口参数：len - 传输字节�? uint16_t
 * 函数说明：发�?s_spi2_dma_tx_buf，接收到 s_spi2_dma_rx_buf�? *          阻塞等待 DMA 完成回调置位标志
 */
static void ch395f_spi_dma_transfer(uint16_t len) {
    HAL_StatusTypeDef status;
    uint32_t tickstart;
    volatile uint32_t loop_cnt = 0U;

    s_spi2_dma_done = 0U;
    status = HAL_SPI_TransmitReceive_DMA(&hspi2, s_spi2_dma_tx_buf, s_spi2_dma_rx_buf, len);
    if (status != HAL_OK) {
        DBG_ERROR("HAL_SPI_TransmitReceive_DMA FAIL: %d, SPI_State=%d, SPI_Error=0x%lX, DMA_State=%d",
                  (int)status,
                  (int)hspi2.State,
                  hspi2.ErrorCode,
                  (int)hspi2.hdmarx->State);
        return;
    }

    tickstart = HAL_GetTick();
    while (s_spi2_dma_done == 0U) {
        loop_cnt++;
        if ((HAL_GetTick() - tickstart) > 1000U) {
            DBG_ERROR("TIMEOUT after 1s, len=%d, loop=%lu, SPI_State=%d, SPI_Error=0x%lX",
                      (int)len, loop_cnt,
                      (int)hspi2.State,
                      hspi2.ErrorCode);
            DBG_ERROR("  DMA_RX: State=%d, Dir=%lu, CNT=%lu",
                      (int)hspi2.hdmarx->State,
                      hspi2.hdmarx->Init.Direction,
                      __HAL_DMA_GET_COUNTER(hspi2.hdmarx));
            DBG_ERROR("  DMA_TX: State=%d, Dir=%lu, CNT=%lu",
                      (int)hspi2.hdmatx->State,
                      hspi2.hdmatx->Init.Direction,
                      __HAL_DMA_GET_COUNTER(hspi2.hdmatx));
            DBG_ERROR("  SPI2_CR1=0x%lX, CR2=0x%lX, SR=0x%lX",
                      SPI2->CR1, SPI2->CR2, SPI2->SR);
            /* 强制复位 SPI2 DMA 状态，避免死锁 */
            HAL_SPI_Abort(&hspi2);
            return;
        }
    }

    /* 检查传输是否成功：SPI 错误码必须为 0 */
    if (hspi2.ErrorCode != 0U) {
        DBG_INFO("SPI DMA done with errors: ErrorCode=0x%lX, SPI_State=%d",
                 hspi2.ErrorCode, (int)hspi2.State);
        __HAL_SPI_CLEAR_OVRFLAG(&hspi2);
    }
}

/*
 * 函数功能：SPI 收发完成回调
 * 函数说明：由 HAL SPI 驱动�?DMA TX+RX 完成后调用（HAL_SPI_TransmitReceive_DMA 触发此回调）
 */
/* SPI1 DMA 完成标志（定义在 gd5f2gq5ue.c 中） */
extern volatile uint8_t g_spi1_dma_done;

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI2) {
        s_spi2_dma_done = 1U;
    } else if (hspi->Instance == SPI1) {
        g_spi1_dma_done = 1U;
    }
}

/*
 * 函数功能：SPI 错误回调
 * 函数说明：由 HAL SPI 驱动�?DMA 传输出错时调用，释放等待
 */
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI2) {
        s_spi2_dma_done = 1U;
    } else if (hspi->Instance == SPI1) {
        g_spi1_dma_done = 1U;
    }
}

/*
 * 公共函数实现�? */

/*
 * 函数功能：向 CH395 写入命令字节
 * 入口参数：cmd - 命令�? uint8_t  0x00 - 0xFF
 * 返回值：命令码回显�? uint8_t
 * 限定条件：需�?ch395f_spi_begin() �?ch395f_spi_end() 之间调用
 * 函数说明：SPI 模式下命令与数据之间无需间隔
 */
uint8_t ch395f_write_cmd(uint8_t cmd) {
    uint8_t rx_data;

    /* 通过 SPI 发送命令并接收回显 */
    HAL_SPI_TransmitReceive(&hspi2, &cmd, &rx_data, 1, HAL_MAX_DELAY);
    return rx_data;
}

/*
 * 函数功能：向 CH395 写入数据字节
 * 入口参数：data - 数据字节  uint8_t  0x00 - 0xFF
 * 返回值：数据回显�? uint8_t
 * 限定条件：需�?ch395f_spi_begin() �?ch395f_spi_end() 之间调用
 */
uint8_t ch395f_write_data(uint8_t data) {
    uint8_t rx_data;

    /* 通过 SPI 发送数据并接收回显 */
    HAL_SPI_TransmitReceive(&hspi2, &data, &rx_data, 1, HAL_MAX_DELAY);
    return rx_data;
}

/*
 * 函数功能：从 CH395 读取数据字节
 * 返回值：接收到的数据字节  uint8_t
 * 限定条件：需�?ch395f_spi_begin() �?ch395f_spi_end() 之间调用
 */
uint8_t ch395f_read_data(void) {
    uint8_t tx_dummy = 0xFFU;
    uint8_t rx_data;

    /* 发�?0xFF 作为时钟源，读取芯片返回的数�?*/
    HAL_SPI_TransmitReceive(&hspi2, &tx_dummy, &rx_data, 1, HAL_MAX_DELAY);
    return rx_data;
}

/*
 * 函数功能：检�?CH395 芯片是否存在且通信正常
 * 返回值：CH395F_STATUS_OK - 检测到芯片，CH395F_STATUS_NOT_DETECTED - 未检测到
 * 限定条件：SPI2 已正确初始化
 * 函数说明：发�?CMD_CHECK_EXIST (0x06) 及测试字�?0x57�? *          芯片应返回按位取反�?0xA8
 */
ch395f_status_t ch395f_check_exist(void) {
    uint8_t test_data = CH395F_CHECK_EXIST_TEST_DATA;
    uint8_t reply_data;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送检测命�?*/
    ch395f_write_cmd(CH395F_CMD_CHECK_EXIST);

    /* 发送测试数�?*/
    ch395f_write_data(test_data);

    /* 读取芯片回复 */
    reply_data = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    /* 验证回复是否为测试数据的按位取反 */
    if (reply_data == (uint8_t)(~test_data)) {
        return CH395F_STATUS_OK;
    }

    return CH395F_STATUS_NOT_DETECTED;
}

/*
 * 函数功能：读�?CH395 芯片版本�? * 返回值：版本字节（bit5:0 为版本号）uint8_t
 * 限定条件：芯片已通过 ch395f_check_exist() 检�? */
uint8_t ch395f_get_version(void) {
    uint8_t version;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送版本读取命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_IC_VER);

    /* 读取版本�?*/
    version = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return version;
}

/*
 * 函数功能：获取远�?IP 和端�? * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 *          p_buf - 6 字节输出缓冲区（4字节IP + 2字节端口�? * 限定条件：TCP Server 模式下连接建立后调用
 * 函数说明：IP 低字节在前，端口低字节在�? */
void ch395f_get_remot_ipp(uint8_t sock, uint8_t *p_buf) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_buf == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送获取远�?IP 和端口命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_REMOT_IPP_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 读取 4 字节 IP 地址 */
    for (i = 0; i < 4; i++) {
        p_buf[i] = ch395f_read_data();
    }

    /* 读取 2 字节端口�?*/
    for (i = 4; i < 6; i++) {
        p_buf[i] = ch395f_read_data();
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?CH395 功能参数
 * 入口参数：para - 4 字节功能参数  uint32_t
 * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_fun_para(uint32_t para) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置功能参数命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_FUN_PARA);

    /* 发�?4 字节参数（低字节在前�?*/
    ch395f_write_data((uint8_t)(para));
    ch395f_write_data((uint8_t)(para >> 8));
    ch395f_write_data((uint8_t)(para >> 16));
    ch395f_write_data((uint8_t)(para >> 24));

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?TCP MSS
 * 入口参数：mss - MSS �? uint16_t  60~1460
 * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_tcp_mss(uint16_t mss) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?TCP MSS 命令 */
    ch395f_write_cmd(CH395F_CMD_SET_TCP_MSS);

    /* 发�?2 字节 MSS 值（低字节在前） */
    ch395f_write_data(mss & 0xFFU);
    ch395f_write_data((mss >> 8) & 0xFFU);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?Socket 接收缓冲�? * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 *          start_block - 起始块地址  uint8_t
 *          block_count - 块个�? uint8_t
 * 限定条件：必须在 OPEN_SOCKET_SN 之前调用
 */
void ch395f_set_recv_buf(uint8_t sock, uint8_t start_block, uint8_t block_count) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置接收缓冲区命令 */
    ch395f_write_cmd(CH395F_CMD_SET_RECV_BUF);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 发送起始块地址 */
    ch395f_write_data(start_block);

    /* 发送块个数 */
    ch395f_write_data(block_count);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?Socket 发送缓冲区
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 *          start_block - 起始块地址  uint8_t
 *          block_count - 块个�? uint8_t
 * 限定条件：必须在 OPEN_SOCKET_SN 之前调用
 */
void ch395f_set_send_buf(uint8_t sock, uint8_t start_block, uint8_t block_count) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置发送缓冲区命令 */
    ch395f_write_cmd(CH395F_CMD_SET_SEND_BUF);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 发送起始块地址 */
    ch395f_write_data(start_block);

    /* 发送块个数 */
    ch395f_write_data(block_count);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：清�?Socket 接收缓冲�? * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 限定条件：Socket 已打开
 */
void ch395f_clear_recv_buf(uint8_t sock) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送清空接收缓冲区命令 */
    ch395f_write_cmd(CH395F_CMD_CLEAR_RECV_BUF_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?TCP KeepAlive 空闲时间
 * 入口参数：time - 空闲时间（秒�? uint32_t
 * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_keepalive_idle(uint32_t time) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?KeepAlive 空闲时间命令 */
    ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_IDLE);

    /* 发�?4 字节时间值（低字节在前） */
    ch395f_write_data((uint8_t)(time));
    ch395f_write_data((uint8_t)(time >> 8));
    ch395f_write_data((uint8_t)(time >> 16));
    ch395f_write_data((uint8_t)(time >> 24));

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?TCP KeepAlive 探测间隔
 * 入口参数：time - 间隔时间（秒�? uint32_t
 * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_keepalive_intvl(uint32_t time) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?KeepAlive 探测间隔命令 */
    ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_INTVL);

    /* 发�?4 字节时间值（低字节在前） */
    ch395f_write_data((uint8_t)(time));
    ch395f_write_data((uint8_t)(time >> 8));
    ch395f_write_data((uint8_t)(time >> 16));
    ch395f_write_data((uint8_t)(time >> 24));

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?TCP KeepAlive 探测次数
 * 入口参数：cnt - 探测次数  uint8_t
 * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_keepalive_cnt(uint8_t cnt) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?KeepAlive 探测次数命令 */
    ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_CNT);

    /* 发送探测次�?*/
    ch395f_write_data(cnt);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：启�?禁用指定 Socket �?KeepAlive
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 *          enable - 1 启用�? 禁用  uint8_t
 * 限定条件：Socket 已打开
 */
void ch395f_set_keepalive_enable(uint8_t sock, uint8_t enable) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?KeepAlive 使能命令 */
    ch395f_write_cmd(CH395F_CMD_SET_KEEPALIVE_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 发送使能标�?*/
    ch395f_write_data(enable);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?TCP 重传次数
 * 入口参数：cnt - 重传次数  uint8_t�?=使用默认�?�? * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_retrans_count(uint8_t cnt) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置重传次数命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_RETRAN_COUNT);

    /* 发送重传次�?*/
    ch395f_write_data(cnt);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?TCP 重传周期
 * 入口参数：period - 重传周期（毫秒）  uint16_t
 * 限定条件：必须在 ch395f_init() 之前调用
 */
void ch395f_set_retrans_period(uint16_t period) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置重传周期命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_RETRAN_PERIOD);

    /* 发�?2 字节周期值（低字节在前） */
    ch395f_write_data(period & 0xFFU);
    ch395f_write_data((period >> 8) & 0xFFU);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：软件复�?CH395
 * 返回值：CH395F_STATUS_OK
 * 限定条件：SPI2 已正确初始化
 * 函数说明：复位耗时�?15ms（TE0），函数内部已包含延�? */
ch395f_status_t ch395f_reset(void) {
    DBG_INFO("ch395f_reset: spi_begin");
    ch395f_spi_begin();

    DBG_INFO("ch395f_reset: write_cmd RESET_ALL");
    ch395f_write_cmd(CH395F_CMD_RESET_ALL);

    DBG_INFO("ch395f_reset: spi_end");
    ch395f_spi_end();

    DBG_INFO("ch395f_reset: HAL_Delay(%d)", CH395F_RESET_DELAY_MS);
    DBG_INFO("ch395f_reset: tick=%lu basepri=0x%02X", HAL_GetTick(), __get_BASEPRI());
    HAL_Delay(CH395F_RESET_DELAY_MS);

    DBG_INFO("ch395f_reset: done");
    return CH395F_STATUS_OK;
}

/*
 * 函数功能：查询命令执行状�? * 返回值：状态字节（CH395F_ERR_SUCCESS / CH395F_ERR_BUSY / ...）uint8_t
 * 限定条件：仅在长执行命令（如初始化）后调�? */
uint8_t ch395f_get_cmd_status(void) {
    uint8_t status;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送状态查询命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_CMD_STATUS);

    /* 读取状态字�?*/
    status = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return status;
}

/*
 * 函数功能：轮询等待命令执行完�? * 入口参数：timeout_ms - 超时时间（毫秒）  uint32_t
 * 返回值：命令执行状态码（CH395F_ERR_SUCCESS �?CH395F_CMD_RET_ABORT�? * 限定条件：在发送长执行命令后调�? * 函数说明：每 20ms 查询一次，直到命令完成或超�? */
uint8_t ch395f_poll_cmd_status(uint32_t timeout_ms) {
    uint8_t status;
    uint32_t retry_count = 0U;
    uint32_t max_retries = (timeout_ms + 20U - 1U) / 20U;

    while (1) {
        HAL_Delay(20U);
        status = ch395f_get_cmd_status();

        if (status != CH395F_ERR_BUSY) {
            return status;
        }

        if (++retry_count >= max_retries) {
            return CH395F_ERR_BUSY;  /* 超时 */
        }
    }
}

/*
 * 函数功能：初始化 CH395（包�?MAC、PHY、TCP/IP 协议栈）
 * 返回值：CH395F_STATUS_OK - 成功，CH395F_STATUS_TIMEOUT - 超时失败
 * 限定条件：芯片已通过 ch395f_reset() 复位
 * 函数说明：阻塞等待初始化完成或超时（芯片需要约 200ms，这里等最�?4s�? */
ch395f_status_t ch395f_init(void) {
    uint8_t status;

    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_INIT_CH395);
    ch395f_spi_end();

    status = ch395f_poll_cmd_status(4000U);

    if (status == CH395F_ERR_SUCCESS) {
        return CH395F_STATUS_OK;
    }

    return (status == CH395F_ERR_BUSY) ? CH395F_STATUS_TIMEOUT : CH395F_STATUS_ERROR;
}

/*
 * 函数功能：设�?CH395 IP 地址（低字节在前�? * 入口参数：p_ip - 4 字节 IP 地址指针  uint8_t*
 * 限定条件：指针非空，芯片已初始化
 */
void ch395f_set_ip_addr(uint8_t *p_ip) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_ip == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?IP 命令 */
    ch395f_write_cmd(CH395F_CMD_SET_IP_ADDR);

    /* 逐字节发�?IP 地址 */
    for (i = 0; i < 4; i++) {
        ch395f_write_data(p_ip[i]);
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设置网�?IP 地址（低字节在前�? * 入口参数：p_gwip - 4 字节网关 IP 指针  uint8_t*
 * 限定条件：指针非空，芯片已初始化
 */
void ch395f_set_gwip_addr(uint8_t *p_gwip) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_gwip == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置网关命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_GWIP_ADDR);

    /* 逐字节发送网�?IP */
    for (i = 0; i < 4; i++) {
        ch395f_write_data(p_gwip[i]);
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设置子网掩码（低字节在前）
 * 入口参数：p_mask - 4 字节子网掩码指针  uint8_t*
 * 限定条件：指针非空，芯片已初始化
 */
void ch395f_set_mask_addr(uint8_t *p_mask) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_mask == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置掩码命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_MASK_ADDR);

    /* 逐字节发送子网掩�?*/
    for (i = 0; i < 4; i++) {
        ch395f_write_data(p_mask[i]);
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?MAC 地址（低字节在前，存储于 EEPROM�? * 入口参数：p_mac - 6 字节 MAC 地址指针  uint8_t*
 * 限定条件：指针非空，芯片已初始化
 * 函数说明：执行耗时�?30us（TE5�? */
void ch395f_set_mac_addr(uint8_t *p_mac) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_mac == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?MAC 命令 */
    ch395f_write_cmd(CH395F_CMD_SET_MAC_ADDR);

    /* 逐字节发�?MAC 地址 */
    for (i = 0; i < 6; i++) {
        ch395f_write_data(p_mac[i]);
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：从 CH395 读取 MAC 地址
 * 出口参数：p_mac - 6 字节 MAC 地址缓冲区指�? uint8_t*
 * 限定条件：指针非�? */
void ch395f_get_mac_addr(uint8_t *p_mac) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_mac == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送读�?MAC 命令 */
    ch395f_write_cmd(CH395F_CMD_GET_MAC_ADDR);

    /* 逐字节读�?MAC 地址 */
    for (i = 0; i < 6; i++) {
        p_mac[i] = ch395f_read_data();
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：读�?IP 信息（IP + 掩码 + DNS，共 20 字节�? * 出口参数：p_buf - 20 字节缓冲区指�? uint8_t*
 * 限定条件：指针非�? */
void ch395f_get_ip_inf(uint8_t *p_buf) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_buf == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送读�?IP 信息命令 */
    ch395f_write_cmd(CH395F_CMD_GET_IP_INF);

    /* 逐字节读�?IP 信息（共 20 字节�?*/
    for (i = 0; i < 20; i++) {
        p_buf[i] = ch395f_read_data();
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：获�?PHY 连接状�? * 返回值：PHY 状态码（CH395F_PHY_DISCONN、CH395F_PHY_10M_FULL 等）uint8_t
 * 限定条件：芯片已初始�? */
uint8_t ch395f_get_phy_status(void) {
    uint8_t status;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送读�?PHY 状态命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_PHY_STATUS);

    /* 读取 PHY 状�?*/
    status = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return status;
}

/*
 * 函数功能：设�?PHY 连接模式
 * 入口参数：phystat - 连接模式�? uint8_t  0x20 = 自动协商
 * 限定条件：芯片已初始�? */
void ch395f_set_phy(uint8_t phystat) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设�?PHY 命令 */
    ch395f_write_cmd(CH395F_CMD_SET_PHY);

    /* 发送连接模式参�?*/
    ch395f_write_data(phystat);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：使�?关闭 CH395 响应 PING
 * 入口参数：enable - 1 使能 PING 响应�? 关闭
 * 限定条件：芯片已初始�? * 函数说明：手�?5.37 CMD_PING_ENABLE，默认关�? */
void ch395f_ping_enable(uint8_t enable) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发�?PING 使能命令 */
    ch395f_write_cmd(CH395F_CMD_PING_ENABLE);

    /* 发送使能标�?*/
    ch395f_write_data(enable);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：启�?禁用 DHCP
 * 入口参数：enable - 1 启用�? 禁用  uint8_t
 * 限定条件：芯片已初始�? */
void ch395f_set_dhcp(uint8_t enable) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发�?DHCP 使能命令 */
    ch395f_write_cmd(CH395F_CMD_DHCP_ENABLE);

    /* 发送启�?禁用参数 */
    ch395f_write_data(enable);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：获�?DHCP 状�? * 返回值：0 = 成功，非�?= 错误/超时  uint8_t
 * 限定条件：芯片已初始化且 DHCP 已启�? */
uint8_t ch395f_get_dhcp_status(void) {
    uint8_t status;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送读�?DHCP 状态命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_DHCP_STATUS);

    /* 读取 DHCP 状�?*/
    status = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return status;
}

/*
 * 函数功能：设�?Socket 协议类型
 * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 *          proto - 协议类型  uint8_t  CH395F_PROTO_TYPE_xxx
 * 限定条件：Socket 未打开
 */
void ch395f_set_proto_type(uint8_t sock, uint8_t proto) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置协议类型命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_PROTO_TYPE_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 发送协议类�?*/
    ch395f_write_data(proto);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?Socket 目标 IP 地址
 * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 *          p_ip - 4 字节目标 IP 指针  uint8_t*
 * 限定条件：指针非�? */
void ch395f_set_des_ip(uint8_t sock, uint8_t *p_ip) {
    uint8_t i;

    /* 检查输入参数合法�?*/
    if (p_ip == NULL) {
        return;
    }

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置目�?IP 命令 */
    ch395f_write_cmd(CH395F_CMD_SET_IP_ADDR_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 逐字节发送目�?IP */
    for (i = 0; i < 4; i++) {
        ch395f_write_data(p_ip[i]);
    }

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?Socket 目标端口（小端序�? * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 *          port - 目标端口  uint16_t
 * 限定条件：无
 */
void ch395f_set_des_port(uint8_t sock, uint16_t port) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置目标端口命�?*/
    ch395f_write_cmd(CH395F_CMD_SET_DES_PORT_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 发送端口低字节 */
    ch395f_write_data(port & 0xFFU);

    /* 发送端口高字节 */
    ch395f_write_data((port >> 8) & 0xFFU);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：设�?Socket 源端口（小端序）
 * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 *          port - 源端�? uint16_t
 * 限定条件：无
 */
void ch395f_set_sour_port(uint8_t sock, uint16_t port) {
    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送设置源端口命令 */
    ch395f_write_cmd(CH395F_CMD_SET_SOUR_PORT_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 发送端口低字节 */
    ch395f_write_data(port & 0xFFU);

    /* 发送端口高字节 */
    ch395f_write_data((port >> 8) & 0xFFU);

    /* 结束 SPI 事务 */
    ch395f_spi_end();
}

/*
 * 函数功能：打开 Socket
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 返回值：命令执行状态码（CH395F_ERR_SUCCESS 表示成功�? uint8_t
 * 限定条件：协议类型、目�?IP、端口已设置
 */
uint8_t ch395f_open_socket(uint8_t sock) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_OPEN_SOCKET_SN);
    ch395f_write_data(sock);
    ch395f_spi_end();

    return ch395f_poll_cmd_status(4000U);
}

/*
 * 函数功能：关�?Socket
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 返回值：命令执行状态码（CH395F_ERR_SUCCESS 表示成功�? uint8_t
 * 限定条件：Socket 已打开
 */
uint8_t ch395f_close_socket(uint8_t sock) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_CLOSE_SOCKET_SN);
    ch395f_write_data(sock);
    ch395f_spi_end();

    return ch395f_poll_cmd_status(4000U);
}

/*
 * 函数功能：启�?TCP 监听模式
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 返回值：命令执行状态码（CH395F_ERR_SUCCESS 表示成功�? uint8_t
 * 限定条件：Socket 已打开且协议类型为 TCP
 */
uint8_t ch395f_tcp_listen(uint8_t sock) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_TCP_LISTEN_SN);
    ch395f_write_data(sock);
    ch395f_spi_end();

    return ch395f_poll_cmd_status(4000U);
}

/*
 * 函数功能：启�?TCP 连接
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 返回值：命令执行状态码（CH395F_ERR_SUCCESS 表示成功�? uint8_t
 * 限定条件：Socket 已打开且协议类型为 TCP
 */
uint8_t ch395f_tcp_connect(uint8_t sock) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_TCP_CONNECT_SN);
    ch395f_write_data(sock);
    ch395f_spi_end();

    return ch395f_poll_cmd_status(4000U);
}

/*
 * 函数功能：断开 TCP 连接
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 返回值：命令执行状态码（CH395F_ERR_SUCCESS 表示成功�? uint8_t
 * 限定条件：TCP 已建立连�? */
uint8_t ch395f_tcp_disconnect(uint8_t sock) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_TCP_DISCONNECT_SN);
    ch395f_write_data(sock);
    ch395f_spi_end();

    return ch395f_poll_cmd_status(4000U);
}

/*
 * 函数功能：向 Socket 发送缓冲区写入数据（DMA 批量传输�? * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 *          p_data - 数据指针  uint8_t*
 *          len - 数据长度  uint16_t
 * 限定条件：指针非空，长度大于 0，TCP 已连接或 UDP 已打开
 * 函数说明：将命令头和数据打包�?DMA 缓冲区，一�?SPI DMA 传输完成
 */
void ch395f_write_send_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
    uint16_t total;

    /* 检查输入参数合法�?*/
    if (p_data == NULL) {
        return;
    }

    /* 计算总长度：1(cmd) + 1(sock) + 2(len) + data */
    total = 4U + len;
    if (total > CH395F_DMA_BUF_SIZE) {
        return;
    }

    /* 打包命令头到 DMA 发送缓冲区 */
    s_spi2_dma_tx_buf[0] = CH395F_CMD_WRITE_SEND_BUF_SN;
    s_spi2_dma_tx_buf[1] = sock;
    s_spi2_dma_tx_buf[2] = len & 0xFFU;
    s_spi2_dma_tx_buf[3] = (len >> 8) & 0xFFU;

    /* 拷贝用户数据�?DMA 发送缓冲区 */
    memcpy(&s_spi2_dma_tx_buf[4], p_data, len);

    /* 数据同步屏障：确�?memcpy 写入�?DMA 读取前对总线可见 */
    __DSB();

    /* CS 拉低 -> DMA 批量传输 -> CS 拉高 */
    ch395f_spi_begin();
    ch395f_spi_dma_transfer(total);
    ch395f_spi_end();
}

/*
 * 函数功能：从 Socket 接收缓冲区读取数据（DMA 批量传输�? * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 *          p_data - 输出缓冲区指�? uint8_t*
 *          len - 待读取数据长�? uint16_t
 * 限定条件：指针非空，长度大于 0，接收缓冲区有数�? * 函数说明：命令头打包到发送缓冲区（数据部分填 0xFF 作时钟）�? *          一�?SPI DMA 全双工传输，有效数据从接收缓冲区偏移 4 字节处拷�? */
void ch395f_read_recv_buf(uint8_t sock, uint8_t *p_data, uint16_t len) {
    uint16_t total;

    /* 检查输入参数合法�?*/
    if (p_data == NULL) {
        return;
    }

    /* 计算总长度：1(cmd) + 1(sock) + 2(len) + data */
    total = 4U + len;
    if (total > CH395F_DMA_BUF_SIZE) {
        return;
    }

    /* 命令头打包到 DMA 发送缓冲区 */
    s_spi2_dma_tx_buf[0] = CH395F_CMD_READ_RECV_BUF_SN;
    s_spi2_dma_tx_buf[1] = sock;
    s_spi2_dma_tx_buf[2] = len & 0xFFU;
    s_spi2_dma_tx_buf[3] = (len >> 8) & 0xFFU;

    /* 数据部分填充 0xFF（SPI 时钟驱动用） */
    memset(&s_spi2_dma_tx_buf[4], 0xFFU, len);

    /* CS 拉低 -> DMA 全双工传输（发命令头 + 收数据） -> CS 拉高 */
    ch395f_spi_begin();
    ch395f_spi_dma_transfer(total);
    ch395f_spi_end();

    /* �?DMA 接收缓冲区偏�?4 字节处拷贝有效数据到用户缓冲�?*/
    memcpy(p_data, &s_spi2_dma_rx_buf[4], len);
}

/*
 * 函数功能：获�?Socket 接收缓冲区数据长�? * 入口参数：sock - Socket 索引  uint8_t  0 - 3
 * 返回值：接收数据长度  uint16_t
 * 限定条件：Socket 已打开
 */
uint16_t ch395f_get_recv_len(uint8_t sock) {
    uint16_t recv_len;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送获取接收长度命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_RECV_LEN_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 读取长度低字�?*/
    recv_len = ch395f_read_data();

    /* 读取长度高字节并组合 */
    recv_len |= (uint16_t)ch395f_read_data() << 8;

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return recv_len;
}

/*
 * 函数功能：获取全局中断状�? * 返回值：中断状态字�? uint8_t
 * 限定条件：芯片已初始�? */
uint8_t ch395f_get_glob_int_status(void) {
    uint8_t status;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送读取全局中断状态命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_GLOB_INT_STATUS);

    /* 读取中断状�?*/
    status = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return status;
}

/*
 * 函数功能：获取全局中断状态（2 字节版，支持 Socket 0~7�? * 返回值：中断状�?uint16_t（低字节=Socket 0~3 + PHY/DHCP，高字节=Socket 4~7 + DHCPv6�? * 限定条件：芯片已初始�? */
uint16_t ch395f_get_glob_int_status_all(void) {
    uint16_t status;

    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_GET_GLOB_INT_STATUS_ALL);
    status = (uint16_t)ch395f_read_data();
    status |= ((uint16_t)ch395f_read_data() << 8U);
    ch395f_spi_end();

    return status;
}

/*
 * 函数功能：获�?Socket 中断状�? * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 * 返回值：Socket 中断状态字�? uint8_t
 * 限定条件：芯片已初始�? */
uint8_t ch395f_get_sock_int_status(uint8_t sock) {
    uint8_t status;

    /* 开�?SPI 事务 */
    ch395f_spi_begin();

    /* 发送读�?Socket 中断状态命�?*/
    ch395f_write_cmd(CH395F_CMD_GET_INT_STATUS_SN);

    /* 发�?Socket 索引 */
    ch395f_write_data(sock);

    /* 读取中断状�?*/
    status = ch395f_read_data();

    /* 结束 SPI 事务 */
    ch395f_spi_end();

    return status;
}

/*
 * 函数功能：获�?Socket 状态（2 字节输出�? * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 *          status - 2 字节输出缓冲�?[0]=Socket状�? [1]=TCP状�? * 限定条件：芯片已初始�? */
void ch395f_get_socket_status(uint8_t sock, uint8_t *p_status) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_GET_SOCKET_STATUS_SN);
    ch395f_write_data(sock);
    p_status[0] = ch395f_read_data();
    p_status[1] = ch395f_read_data();
    ch395f_spi_end();
}

/*
 * 函数功能：设�?ARP 重传参数
 * 入口参数：period_100ms - ARP 重传周期（单�?100ms�?=默认�?10�?秒）  uint8_t
 *          cnt - ARP 重传次数�?=默认�?3�? uint8_t
 * 限定条件：应�?ch395f_init() 之后调用
 * 函数说明：周期最大值约 25.5 秒，255 表示无限重传
 */
void ch395f_set_arp(uint8_t period_100ms, uint8_t cnt) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_SET_ARP);
    ch395f_write_data(period_100ms);
    ch395f_write_data(cnt);
    ch395f_spi_end();
}

/*
 * 函数功能：设�?Socket TTL
 * 入口参数：sock - Socket 索引  uint8_t  0 - 7
 *          ttl - TTL �? uint8_t  最�?128
 * 限定条件：Socket 已打开，TCP Server 模式下不生效
 */
void ch395f_set_ttl(uint8_t sock, uint8_t ttl) {
    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_SET_TTL);
    ch395f_write_data(sock);
    ch395f_write_data(ttl);
    ch395f_spi_end();
}

/*
 * 函数功能：获取不可达信息（用于调试连接失败原因）
 * 出口参数：p_buf - 8 字节输出缓冲区（类型+协议�?端口+IP�? * 限定条件：收�?GINT_STAT_UNREACH 中断后调�? */
void ch395f_get_unreach_info(uint8_t *p_buf) {
    uint8_t i = 0;

    ch395f_spi_begin();
    ch395f_write_cmd(CH395F_CMD_GET_UNREACH_IPPT);
    for (i = 0; i < 8; i++) {
        p_buf[i] = ch395f_read_data();
    }
    ch395f_spi_end();
}


