/*
 * 模块名称：GD5F2GQ5UE SPI NAND Flash 驱动
 * 模块功能：提供 GD5F2GQ5UE SPI NAND Flash 的初始化、读、写、擦除接口
 * 适用平台：STM32F407ZGT6 + SPI1 硬件 SPI
 * 作者：王建锋
 * 创建日期：2026-07-16
 * 修改记录：
 *     2026-07-16  王建锋  创建初始版本
 *     2026-07-17  王建锋  切换硬件 SPI，修正擦除地址，参考 NuttX 驱动
 *     2026-08-27  补充函数注释（代码规范 V1.0）；修正工厂坏块扫描为首页；增加 BBT 持久化
 */

/* 头文件包含区 */
#include <string.h>
#include "gd5f2gq5ue.h"

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

/* 私有宏定义区 */
#define GD5F_SPI_TIMEOUT          100
#define GD5F_BBT_SIZE             (GD5F_TOTAL_BLOCKS / 8)

/* BBT 静态数组（1 bit 表示一个块，0=好块 1=坏块） */
static uint8_t s_bbt[GD5F_BBT_SIZE];

/* BBT 持久化：保留块池（从 NAND 顶部向下挑选出厂好块，存储 BBT 副本） */
#define GD5F_BBT_HDR_SIZE          16
static uint32_t s_bbt_pool_blocks[GD5F_BBT_POOL_COUNT];  /* BBT 保留池块号列表（出厂好块） */
static uint32_t s_bbt_pool_count = 0;            /* 实际可用池块数（<= GD5F_BBT_POOL_COUNT） */
static uint32_t s_usable_blocks = GD5F_TOTAL_BLOCKS;  /* dhara 可见块数（= 最低池块号） */
static uint32_t s_bbt_write_slot = 0;            /* 下一写入槽（轮转指针） */
static uint32_t s_bbt_version = 0;               /* 当前持久化 BBT 版本号 */
static uint8_t  s_bbt_slot_dead = 0;             /* 池块损坏位掩码 */
static uint8_t  s_bbt_page_buf[GD5F_PAGE_SIZE];  /* BBT 读写页缓冲（静态，避免占栈） */
static uint8_t  s_bbt_best[GD5F_BBT_SIZE];       /* 最高版本位图缓存 */
static const uint8_t s_bbt_magic[4] = { 'G', 'B', 'B', 'T' };  /* BBT 存储魔数 "GBBT" */

/* 外部 SPI 句柄声明 */
extern SPI_HandleTypeDef hspi1;

/* DMA 完成标志（ch395f.c 中的 HAL_SPI_TxRxCpltCallback 会设置此标志） */
volatile uint8_t g_spi1_dma_done;

/* DMA 阈值（字节数超过此值使用 DMA 传输） */
#define GD5F_DMA_THRESHOLD       32

/*
 * 函数功能：SPI1 TX DMA 完成回调（PROGRAM_LOAD 数据发送用）
 * 入口参数：hspi - 触发回调的 SPI 句柄  SPI_HandleTypeDef*
 * 返回值：无
 * 限定条件：由 HAL DMA 中断调用
 * 函数说明：仅当 SPI1 传输完成时置位 g_spi1_dma_done 标志
 */
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI1) {
        g_spi1_dma_done = 1U;
    }
}

/*
 * 函数功能：SPI1 RX DMA 完成回调（READ_FROM_CACHE 数据接收用）
 * 函数说明：HAL_SPI_Receive_DMA() 在 2 线 master 模式下
 *           内部走 HAL_SPI_TransmitReceive_DMA 但 state=BUSY_RX，
 *           完成回调是 HAL_SPI_RxCpltCallback 而非 TxRxCpltCallback
 */
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
    if (hspi->Instance == SPI1) {
        g_spi1_dma_done = 1U;
    }
}

/* ======================== 私有函数声明 ======================== */

static int gd5f_page_program(uint32_t page_addr, uint16_t column,
                              const uint8_t *p_buf, size_t size);
static int gd5f_set_feature(uint8_t addr, uint8_t data);

/* BBT 持久化内部函数 */
static int gd5f_private_bbt_locate_pool(void);
static uint32_t gd5f_private_crc32(const uint8_t *p_buf, uint32_t len);
static int gd5f_private_bbt_find_best(void);
static int gd5f_bbt_load(void);
static int gd5f_bbt_save(void);

/* ======================== SPI 原语定义 ======================== */

/*
 * 函数功能：等待芯片操作完成（轮询 OIP 位）
 * 入口参数：timeout_ms - 超时时间  uint32_t  > 0
 * 返回值：0 - 操作完成，-2 - 超时
 * 限定条件：SPI 已初始化
 * 函数说明：循环读取状态寄存器直到 OIP 位清零或超时
 */
int gd5f_wait_busy(uint32_t timeout_ms) {
    uint8_t cmd = GD5F_CMD_GET_FEATURE;
    uint8_t addr = GD5F_REG_STATUS;
    uint8_t status = 0;
    uint32_t tick_start = HAL_GetTick();

    while (1) {
        GD5F_CS_LOW();
        HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
        HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
        HAL_SPI_Receive(&hspi1, &status, 1, GD5F_SPI_TIMEOUT);
        GD5F_CS_HIGH();

        if ((status & GD5F_STATUS_OIP) == 0) {
            return GD5F_OK;
        }
        if ((HAL_GetTick() - tick_start) >= timeout_ms) {
            return GD5F_BUSY_TIMEOUT;
        }
    }
}

/*
 * 函数功能：发送写使能命令
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：CS# 拉低后发 06h 命令再拉高 */
int gd5f_write_enable(void) {
    uint8_t cmd = GD5F_CMD_WRITE_ENABLE;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：读取状态寄存器
 * 出口参数：p_status - 状态值输出指针  uint8_t*  不为 NULL
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发 0Fh + C0h 地址后读 1 字节状态 */
int gd5f_read_status(uint8_t *p_status) {
    uint8_t cmd = GD5F_CMD_GET_FEATURE;
    uint8_t addr = GD5F_REG_STATUS;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    HAL_SPI_Transmit(&hspi1, &addr, 1, GD5F_SPI_TIMEOUT);
    HAL_SPI_Receive(&hspi1, p_status, 1, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：页读取（将数据从存储阵列加载到内部缓存）
 * 入口参数：page_addr - 页地址  uint32_t  0 ~ GD5F_TOTAL_BLOCKS*64-1
 * 返回值：0 - 成功，其它 - 错误
 * 限定条件：SPI 已初始化
 * 函数说明：发 13h + 3字节行地址，等待 OIP 清零
 */
int gd5f_page_read(uint32_t page_addr) {
    uint8_t cmd[4] = {0};

    cmd[0] = GD5F_CMD_PAGE_READ;
    cmd[1] = (page_addr >> 16) & 0xFF;
    cmd[2] = (page_addr >> 8) & 0xFF;
    cmd[3] = page_addr & 0xFF;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    return gd5f_wait_busy(100);
}

/*
 * 函数功能：从内部缓存读取数据
 * 入口参数：column - 列地址（页内偏移）  uint16_t  0 - 2047
 *           size   - 读取字节数          size_t    > 0
 * 出口参数：p_buf  - 数据输出缓冲区      uint8_t*  不为 NULL
 * 返回值：0 - 成功
 * 限定条件：必须先调用 gd5f_page_read 完成数据加载
 * 函数说明：发 0Bh + 2字节列地址 +1字节 dummy 后读取数据 */
int gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
                                 size_t size) {
    uint8_t cmd[4] = {0};

    cmd[0] = GD5F_CMD_READ_FROM_CACHE;
    cmd[1] = (column >> 8) & 0xFF;
    cmd[2] = column & 0xFF;
    cmd[3] = 0x00;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);

    if (size > GD5F_DMA_THRESHOLD) {
        uint32_t tick_start = HAL_GetTick();
        g_spi1_dma_done = 0U;
        if (HAL_SPI_Receive_DMA(&hspi1, p_buf, (uint16_t)size) != HAL_OK) {
            GD5F_CS_HIGH();
            return GD5F_ERROR;
        }
        while (g_spi1_dma_done == 0U) {
            if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
                HAL_SPI_DMAStop(&hspi1);
                GD5F_CS_HIGH();
                return GD5F_BUSY_TIMEOUT;
            }
        }
    } else {
        HAL_SPI_Receive(&hspi1, p_buf, size, GD5F_SPI_TIMEOUT);
    }
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：页编程（将数据写入指定页）
 * 入口参数：page_addr - 页地址   uint32_t
 *           column    - 列地址   uint16_t  0 - 2047
 *           p_buf     - 数据缓冲区 const uint8_t*
 *           size      - 写入字节数 size_t    > 0
 * 返回值：0 - 成功，其它 - 错误 * 限定条件：目标区域已擦除
 * 函数说明：1. 写使能 - 02h 加载数据 - 10h 执行编程 - 等待完成
 *           2. 编程完成后检查 P_FAIL 位 */
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
                              const uint8_t *p_buf, size_t size) {
    int ret;
    gd5f_write_enable();
    ret = gd5f_program_load(column, p_buf, size);
    if (ret != GD5F_OK) return ret;
    return gd5f_program_exec(page_addr);
}

/*
 * 函数功能：将数据加载到内部缓存（PROGRAM LOAD）
 * 入口参数：column - 列地址（页内偏移）  uint16_t       0 - 2047
 *           p_buf  - 待写入数据缓冲区    const uint8_t* 不为 NULL
 *           size   - 写入字节数          size_t         > 0
 * 返回值：0 - 成功，其它 - 错误
 * 限定条件：SPI 已初始化，目标区域已擦除
 * 函数说明：发 02h + 2字节列地址后发送数据；超过 DMA 阈值走 DMA
 */
int gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
    uint8_t cmd[3];
    cmd[0] = GD5F_CMD_PROGRAM_LOAD;
    cmd[1] = (uint8_t)(column >> 8);
    cmd[2] = (uint8_t)(column);

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);

    if (size > GD5F_DMA_THRESHOLD) {
        uint32_t tick_start = HAL_GetTick();
        g_spi1_dma_done = 0U;
        if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
                != HAL_OK) {
            GD5F_CS_HIGH();
            return GD5F_ERROR;
        }
        while (g_spi1_dma_done == 0U) {
            if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
                HAL_SPI_DMAStop(&hspi1);
                GD5F_CS_HIGH();
                return GD5F_BUSY_TIMEOUT;
            }
        }
    } else {
        HAL_SPI_Transmit(&hspi1, (uint8_t *)p_buf, size, GD5F_SPI_TIMEOUT);
    }
    GD5F_CS_HIGH();

    return GD5F_OK;
}

/*
 * 函数功能：执行页编程（PROGRAM EXECUTE）
 * 入口参数：page_addr - 目标页地址  uint32_t  0 ~ GD5F_TOTAL_BLOCKS*64-1
 * 返回值：0 - 成功，其它 - 错误（GD5F_PROGRAM_FAIL 等）
 * 限定条件：已先调用 gd5f_program_load 加载数据
 * 函数说明：发 10h + 3字节页地址触发编程，等待完成后检查 P_FAIL 位
 */
int gd5f_program_exec(uint32_t page_addr) {
    int ret;
    uint8_t cmd[4];
    uint8_t status;

    cmd[0] = GD5F_CMD_PROGRAM_EXEC;
    cmd[1] = (page_addr >> 16) & 0xFF;
    cmd[2] = (page_addr >> 8) & 0xFF;
    cmd[3] = page_addr & 0xFF;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    ret = gd5f_wait_busy(1000);
    if (ret != GD5F_OK) return ret;

    gd5f_read_status(&status);
    if (status & GD5F_STATUS_P_FAIL) return GD5F_PROGRAM_FAIL;

    return GD5F_OK;
}

/*
 * 函数功能：检查上次读操作的 ECC 状态
 * 入口参数：无
 * 返回值：0 - 无 ECC 错误，GD5F_ECC_ERROR - 存在不可纠正 ECC 错误
 * 限定条件：SPI 已初始化，ECC 已使能
 * 函数说明：读取状态寄存器 ECCS[5:4] 位，0x02 表示不可纠正错误
 */
int gd5f_check_ecc(void) {
    uint8_t status;
    gd5f_read_status(&status);
    uint8_t ecc = (status >> 4) & 0x03;
    if (ecc == 0x02) return GD5F_ECC_ERROR;
    return GD5F_OK;
}

/*
 * 函数功能：设置 Feature 寄存器
 * 入口参数：addr - 寄存器地址  uint8_t
 *           data - 写入数据    uint8_t
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：先写使能，发 1Fh + 地址 + 数据，等待操作完成 */
static int gd5f_set_feature(uint8_t addr, uint8_t data) {
    uint8_t cmd[3];

    gd5f_write_enable();

    cmd[0] = GD5F_CMD_SET_FEATURE;
    cmd[1] = addr;
    cmd[2] = data;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 3, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    gd5f_wait_busy(100);

    return GD5F_OK;
}

/*
 * 函数功能：块擦除（擦除128KB块）
 * 入口参数：block_addr - 块编号  uint32_t  0 - 2047
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已调用
 * 函数说明：1. 写使能 - D8h + 3字节页地址 - 等待完成
 *           2. 擦除完成后检查 E_FAIL */
int gd5f_block_erase(uint32_t block_addr) {
    int ret = GD5F_OK;
    uint8_t cmd[4];
    uint8_t status = 0;
    uint32_t page_addr = block_addr * GD5F_PAGES_PER_BLOCK;

    gd5f_write_enable();

    cmd[0] = GD5F_CMD_BLOCK_ERASE;
    cmd[1] = (page_addr >> 16) & 0xFF;
    cmd[2] = (page_addr >> 8) & 0xFF;
    cmd[3] = page_addr & 0xFF;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    ret = gd5f_wait_busy(5000);
    if (ret != GD5F_OK) {
        return ret;
    }

    gd5f_read_status(&status);
    if (status & GD5F_STATUS_E_FAIL) {
        return GD5F_ERASE_FAIL;
    }

    return GD5F_OK;
}

/*
 * 函数功能：扫描所有块构建 BBT（在 ECC 使能前调用）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化，ECC 尚未使能
 * 函数说明：读取每个块首页（page 0）的 spare byte 0，
 *           若不为 0xFF 则为出厂坏块（符合 datasheet §12.4）
 */
static int gd5f_bbt_scan(void) {
    uint32_t block;
    uint8_t spare[8];
    uint32_t bad_count = 0;

    memset(s_bbt, 0, GD5F_BBT_SIZE);

    for (block = 0; block < GD5F_TOTAL_BLOCKS; block++) {
        uint32_t page = block << 6;

        gd5f_page_read(page);
        gd5f_read_from_cache(GD5F_PAGE_SIZE, spare, 1);

        if (spare[0] != 0xFF) {
            s_bbt[block >> 3] |= (1 << (block & 7));
            bad_count++;
        }
    }

    DBG_INFO("BBT scan: %lu bad blocks found", bad_count);
    return GD5F_OK;
}

/*
 * 函数功能：清空 RAM 中的 BBT
 * 入口参数：无
 * 返回值：无
 * 限定条件：无
 * 函数说明：仅清空内存位图，不影响闪存中的持久化副本
 */
void gd5f2gq5ue_bbt_clear(void) {
    memset(s_bbt, 0, GD5F_BBT_SIZE);
}

/*
 * 函数功能：重建 BBT（重新扫描出厂坏块并持久化，覆盖运行时注入的坏块）
 * 入口参数：无
 * 返回值：GD5F_OK - 成功，其他 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：临时关闭 ECC 重新扫描每块 page0 spare[0] 的出厂坏块标记，覆盖 RAM BBT，
 *           再写回持久化 BBT 池（版本 +1，掉电安全），从而清除 TC-STO-03 等运行时
 *           标记的坏块。调用后建议再执行 nand_ftl_format() 清空 dhara map。
 */
int gd5f2gq5ue_bbt_rebuild(void) {
    DBG_INFO("Rebuilding BBT from factory scan...");

    /* 1. 关闭 ECC 以读取原始出厂坏块标记（与 gd5f2gq5ue_init 的扫描前提一致） */
    gd5f_set_feature(0xB0, 0x00);
    gd5f_wait_busy(100);

    /* 2. 重新扫描出厂坏块（覆盖 RAM BBT，剔除运行时注入的坏块） */
    gd5f_bbt_scan();

    /* 3. 重新使能 ECC */
    gd5f_set_feature(0xB0, 0x10);
    gd5f_wait_busy(100);

    /* 4. 持久化重建后的 BBT（覆盖持久化区中的脏副本） */
    if (gd5f_bbt_save() != GD5F_OK) {
        DBG_ERROR("BBT rebuild: persist failed");
        return GD5F_ERROR;
    }

    DBG_INFO("BBT rebuilt and persisted");
    return GD5F_OK;
}

/*
 * 函数功能：通过调试串口打印当前 BBT 统计与坏块列表
 * 入口参数：无
 * 返回值：无
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：最多打印前 32 个坏块块号与偏移
 */
void gd5f2gq5ue_print_bbt(void) {
    uint32_t bad_count = 0;
    for (uint32_t b = 0; b < GD5F_TOTAL_BLOCKS; b++) {
        if ((s_bbt[b >> 3] >> (b & 7)) & 1) bad_count++;
    }
    DBG_INFO("BBT: %lu/%lu blocks bad", bad_count, GD5F_TOTAL_BLOCKS);
    if (bad_count > 0) {
        uint32_t shown = 0;
        for (uint32_t b = 0; b < GD5F_TOTAL_BLOCKS && shown < 32; b++) {
            if ((s_bbt[b >> 3] >> (b & 7)) & 1) {
                DBG_INFO("  block %lu (offset 0x%08lX)", b, b * GD5F_BLOCK_SIZE);
                shown++;
            }
        }
        if (bad_count > 32) DBG_INFO("  ... and %lu more", bad_count - 32);
    }
}

/* ======================== 公共函数定义 ======================== */

/*
 * 函数功能：初始化 GD5F2GQ5UE
 * 入口参数：无
 * 返回值：0 - 成功，其他 - 错误码
 * 限定条件：SPI1 和相关 GPIO 已由 CubeMX 初始化完成
 * 函数说明：1. 发送复位命令并等待完成
 *           2. 读取芯片 ID 并校验
 *           3. 扫描出厂坏块构建 BBT（ECC 使能前，读每块首页 spare[0]）
 *           4. 使能内部 ECC (B0h bit4)
 *           5. 解除所有块保护 (A0h = 0x00)
 *           6. 定位保留块池并加载持久化 BBT（ECC 使能后）
 */
int gd5f2gq5ue_init(void) {
    int ret = GD5F_OK;
    uint8_t mid = 0;
    uint8_t did = 0;

    GD5F_CS_HIGH();
    GD5F_WP_HIGH();
    GD5F_HOLD_HIGH();

    HAL_Delay(10);

    DBG_INFO("Resetting NAND...");
    ret = gd5f2gq5ue_reset();
    if (ret != GD5F_OK) {
        DBG_ERROR("Reset failed: %d", ret);
        return ret;
    }

    HAL_Delay(5);

    DBG_INFO("Reading NAND ID...");
    ret = gd5f2gq5ue_read_id(&mid, &did);
    if (ret != GD5F_OK) {
        DBG_ERROR("Read ID failed: %d", ret);
        return ret;
    }

    DBG_INFO("NAND ID: MID=0x%02X, DID=0x%02X", mid, did);

    if (mid != GD5F_MANUFACTURER_ID || did != GD5F_DEVICE_ID) {
        DBG_ERROR("ID mismatch: expected MID=0x%02X DID=0x%02X, got MID=0x%02X DID=0x%02X",
                  GD5F_MANUFACTURER_ID, GD5F_DEVICE_ID, mid, did);
        return GD5F_ID_MISMATCH;
    }

    DBG_INFO("Scanning bad blocks...");
    gd5f_bbt_scan();

    DBG_INFO("Enabling ECC...");
    gd5f_set_feature(0xB0, 0x10);
    gd5f_wait_busy(100);

    DBG_INFO("Unlocking block protection...");
    gd5f_set_feature(GD5F_REG_PROTECT, 0x00);
    gd5f_wait_busy(100);

    DBG_INFO("Locating BBT pool...");
    gd5f_private_bbt_locate_pool();

    DBG_INFO("Loading persisted BBT...");
    gd5f_bbt_load();

    DBG_INFO("NAND init OK");
    return GD5F_OK;
}

/*
 * 函数功能：读取芯片 ID（MID + DID）
 * 出口参数：p_mid - 制造商 ID 输出指针  uint8_t*  不为 NULL
 *           p_did - 设备 ID 输出指针    uint8_t*  不为 NULL
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发 9Fh 命令后接 1 字节 dummy + MID + DID
 */
int gd5f2gq5ue_read_id(uint8_t *p_mid, uint8_t *p_did) {
    uint8_t cmd = GD5F_CMD_READ_ID;
    uint8_t id_buf[3] = {0};

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    HAL_SPI_Receive(&hspi1, id_buf, 3, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    *p_mid = id_buf[1];
    *p_did = id_buf[2];

    return GD5F_OK;
}

/*
 * 函数功能：从 NAND 读取数据（支持跨页）
 * 入口参数：offset - 起始字节偏移  long      0 ~ 总容量-1
 *           size   - 读取字节数    size_t    > 0
 * 出口参数：p_buf  - 数据缓冲区    uint8_t*  不为 NULL
 * 返回值：0 - 成功，其它 - 错误
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：自动处理跨页读取；offset+size 越界返回错误
 */
int gd5f2gq5ue_read(long offset, uint8_t *p_buf, size_t size) {
    int ret = GD5F_OK;

    if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
        return GD5F_ERROR;
    }

    while (size > 0) {
        uint32_t page_addr = offset / GD5F_PAGE_SIZE;
        uint16_t column = offset % GD5F_PAGE_SIZE;
        size_t bytes = GD5F_PAGE_SIZE - column;

        if (bytes > size) {
            bytes = size;
        }

        ret = gd5f_page_read(page_addr);
        if (ret != GD5F_OK) {
            return ret;
        }

        ret = gd5f_read_from_cache(column, p_buf, bytes);
        if (ret != GD5F_OK) {
            return ret;
        }

        offset += bytes;
        p_buf += bytes;
        size -= bytes;
    }

    return GD5F_OK;
}

/*
 * 函数功能：向 NAND 写入数据（支持跨页）
 * 入口参数：offset - 起始字节偏移  long           0 ~ 总容量-1
 *           p_buf  - 数据缓冲区    const uint8_t* 不为 NULL
 *           size   - 写入字节数    size_t         > 0
 * 返回值：0 - 成功，其它 - 错误
 * 限定条件：gd5f2gq5ue_init() 已成功调用，目标区域已擦除
 * 函数说明：自动处理跨页写入；offset+size 越界返回错误
 */
int gd5f2gq5ue_write(long offset, const uint8_t *p_buf, size_t size) {
    int ret = GD5F_OK;

    if (offset < 0 || (uint32_t)offset + (uint32_t)size > GD5F_TOTAL_SIZE) {
        return GD5F_ERROR;
    }

    while (size > 0) {
        uint32_t page_addr = offset / GD5F_PAGE_SIZE;
        uint16_t column = offset % GD5F_PAGE_SIZE;
        size_t bytes = GD5F_PAGE_SIZE - column;

        if (bytes > size) {
            bytes = size;
        }

        ret = gd5f_page_program(page_addr, column, p_buf, bytes);
        if (ret != GD5F_OK) {
            return ret;
        }

        offset += bytes;
        p_buf += bytes;
        size -= bytes;
    }

    return GD5F_OK;
}

/*
 * 函数功能：擦除块（按块擦除，最小单位 128KB）
 * 入口参数：offset - 起始字节偏移  long     必须 GD5F_BLOCK_SIZE 对齐
 *           size   - 擦除字节数    size_t   必须 GD5F_BLOCK_SIZE 整数倍
 * 返回值：0 - 成功，其它 - 错误码
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：循环擦除 [offset, offset+size) 覆盖的每个块，偏移/长度不对齐返回错误
 */
int gd5f2gq5ue_erase(long offset, size_t size) {
    int ret = GD5F_OK;

    if (offset % GD5F_BLOCK_SIZE != 0) {
        return GD5F_ERROR;
    }
    if (size % GD5F_BLOCK_SIZE != 0) {
        return GD5F_ERROR;
    }

    while (size > 0) {
        uint32_t block_addr = offset / GD5F_BLOCK_SIZE;

        ret = gd5f_block_erase(block_addr);
        if (ret != GD5F_OK) {
            return ret;
        }

        offset += GD5F_BLOCK_SIZE;
        size -= GD5F_BLOCK_SIZE;
    }

    return GD5F_OK;
}

/*
 * 函数功能：复位芯片
 * 返回值：0 - 成功
 * 限定条件：SPI 已初始化
 * 函数说明：发 FFh 复位命令后等待 5ms
 */
int gd5f2gq5ue_reset(void) {
    uint8_t cmd = GD5F_CMD_RESET;

    GD5F_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, GD5F_SPI_TIMEOUT);
    GD5F_CS_HIGH();

    HAL_Delay(5);

    return GD5F_OK;
}

/*
 * 函数功能：查询块是否坏块
 * 入口参数：block - 块编号  uint32_t  0 ~ GD5F_TOTAL_BLOCKS-1
 * 返回值：0 - 好块，1 - 坏块
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：通过 BBT 查询
 */
int gd5f2gq5ue_is_block_bad(uint32_t block) {
    if (block >= GD5F_TOTAL_BLOCKS) {
        return 1;
    }
    return (s_bbt[block >> 3] >> (block & 7)) & 1;
}

/*
 * 函数功能：标记块为坏块
 * 入口参数：block - 块编号  uint32_t  0 ~ GD5F_TOTAL_BLOCKS-1
 * 返回值：无
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：更新 BBT
 */
void gd5f2gq5ue_mark_block_bad(uint32_t block) {
    if (block >= GD5F_TOTAL_BLOCKS) {
        return;
    }
    uint32_t byte = block >> 3;
    uint8_t  bit  = (uint8_t)(1U << (block & 7));
    if (s_bbt[byte] & bit) {
        return;  /* 已是坏块，幂等，无需重复持久化 */
    }
    s_bbt[byte] |= bit;
    gd5f_bbt_save();  /* 持久化到保留块池（带版本号 + CRC） */
}

/* ======================== BBT 持久化实现 ======================== */

/*
 * 函数功能：从 NAND 顶部向下挑选出厂好块作为 BBT 保留池
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f_bbt_scan() 已完成（s_bbt 已就绪）
 * 函数说明：保留池不交给 dhara 管理；最低池块号以上的块全部排除，
 *           并将可用块数 s_usable_blocks 设为最低池块号
 */
static int gd5f_private_bbt_locate_pool(void) {
    uint32_t cnt = 0;
    uint32_t b = GD5F_TOTAL_BLOCKS;

    while (b-- > 0 && cnt < GD5F_BBT_POOL_COUNT) {
        if (((s_bbt[b >> 3] >> (b & 7)) & 1) == 0) {
            s_bbt_pool_blocks[cnt++] = b;
        }
    }
    s_bbt_pool_count = cnt;
    if (cnt > 0) {
        s_usable_blocks = s_bbt_pool_blocks[cnt - 1];  /* 最低池块 = dhara 可见上限 */
    }
    return GD5F_OK;
}

/*
 * 函数功能：CRC32 计算（IEEE 802.3，多项式 0xEDB88320）
 * 入口参数：p_buf - 数据缓冲区    const uint8_t*  不为 NULL
 *           len   - 数据长度      uint32_t        > 0
 * 返回值：32 位 CRC 校验值
 * 限定条件：无
 * 函数说明：采用位级（bit-by-bit）实现，初始值 0xFFFFFFFF
 */
static uint32_t gd5f_private_crc32(const uint8_t *p_buf, uint32_t len) {
    uint32_t crc = 0xFFFFFFFFU;
    for (uint32_t i = 0; i < len; i++) {
        crc ^= p_buf[i];
        for (int j = 0; j < 8; j++) {
            uint32_t mask = (crc & 1U) ? 0xEDB88320U : 0U;
            crc = (crc >> 1) ^ mask;
        }
    }
    return ~crc;
}

/*
 * 函数功能：在保留池中找出版本号最高的有效 BBT 副本
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f_private_bbt_locate_pool() 已成功执行
 * 函数说明：校验 magic / len / CRC；结果缓存到 s_bbt_best / s_bbt_version，
 *           并设置下一轮转写入槽 s_bbt_write_slot
 */
static int gd5f_private_bbt_find_best(void) {
    uint32_t best_ver = 0;
    int      best_slot = -1;

    s_bbt_slot_dead = 0;
    for (uint32_t i = 0; i < s_bbt_pool_count; i++) {
        uint32_t block = s_bbt_pool_blocks[i];
        uint32_t page  = block * GD5F_PAGES_PER_BLOCK;
        uint32_t ver, len, crc;

        gd5f_page_read(page);
        gd5f_read_from_cache(0, s_bbt_page_buf, GD5F_BBT_HDR_SIZE + GD5F_BBT_SIZE);
        if (memcmp(s_bbt_page_buf, s_bbt_magic, 4) != 0) {
            continue;
        }
        memcpy(&ver, s_bbt_page_buf + 4, 4);
        memcpy(&len, s_bbt_page_buf + 8, 4);
        memcpy(&crc, s_bbt_page_buf + 12, 4);
        if (len != GD5F_BBT_SIZE) {
            continue;
        }
        if (gd5f_private_crc32(s_bbt_page_buf + GD5F_BBT_HDR_SIZE, GD5F_BBT_SIZE) != crc) {
            continue;
        }
        if (best_slot < 0 || (int32_t)ver > (int32_t)best_ver) {
            best_ver = ver;
            best_slot = (int)i;
            memcpy(s_bbt_best, s_bbt_page_buf + GD5F_BBT_HDR_SIZE, GD5F_BBT_SIZE);
        }
    }
    if (best_slot >= 0) {
        s_bbt_version = best_ver;
        s_bbt_write_slot = (uint32_t)(best_slot + 1) % s_bbt_pool_count;
    } else {
        s_bbt_write_slot = 0;
    }
    return GD5F_OK;
}

/*
 * 函数功能：加载持久化 BBT（OR 进 RAM BBT）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f_private_bbt_locate_pool() 已成功执行
 * 函数说明：工厂扫描已填好块，持久化副本补充运行期坏块
 */
static int gd5f_bbt_load(void) {
    gd5f_private_bbt_find_best();
    if (s_bbt_pool_count == 0) {
        return GD5F_OK;
    }
    for (uint32_t i = 0; i < GD5F_BBT_SIZE; i++) {
        s_bbt[i] |= s_bbt_best[i];
    }
    return GD5F_OK;
}

/*
 * 函数功能：将当前 RAM BBT 持久化到保留池（轮转写入，掉电安全）
 * 入口参数：无
 * 返回值：0 - 成功，GD5F_ERROR - 写入失败
 * 限定条件：gd5f_private_bbt_locate_pool() 已成功执行
 * 函数说明：版本号自增；先擦除目标块再编程；损坏块标记后跳到下一槽
 */
static int gd5f_bbt_save(void) {
    if (s_bbt_pool_count == 0) {
        return GD5F_OK;
    }
    uint32_t slot = s_bbt_write_slot;

    while (s_bbt_slot_dead & (1U << slot)) {
        slot = (slot + 1) % s_bbt_pool_count;
        if (slot == s_bbt_write_slot) {
            break;  /* 所有池块均损坏 */
        }
    }

    uint32_t block = s_bbt_pool_blocks[slot];
    uint32_t page  = block * GD5F_PAGES_PER_BLOCK;
    uint32_t ver   = s_bbt_version + 1;
    uint32_t len   = GD5F_BBT_SIZE;
    uint32_t crc   = gd5f_private_crc32(s_bbt, GD5F_BBT_SIZE);

    if (gd5f_block_erase(block) != GD5F_OK) {
        s_bbt_slot_dead |= (1U << slot);
        s_bbt_write_slot = (slot + 1) % s_bbt_pool_count;
        return GD5F_ERROR;
    }

    memcpy(s_bbt_page_buf, s_bbt_magic, 4);
    memcpy(s_bbt_page_buf + 4, &ver, 4);
    memcpy(s_bbt_page_buf + 8, &len, 4);
    memcpy(s_bbt_page_buf + 12, &crc, 4);
    memcpy(s_bbt_page_buf + GD5F_BBT_HDR_SIZE, s_bbt, GD5F_BBT_SIZE);

    gd5f_write_enable();
    gd5f_program_load(0, s_bbt_page_buf, GD5F_BBT_HDR_SIZE + GD5F_BBT_SIZE);
    gd5f_program_exec(page);
    gd5f_wait_busy(100);

    s_bbt_version = ver;
    s_bbt_write_slot = (slot + 1) % s_bbt_pool_count;
    return GD5F_OK;
}

/*
 * 函数功能：获取 dhara 可用块数（= 最低保留池块号）
 * 入口参数：无
 * 返回值：dhara 可见的块数量（= 保留块池起始块号）
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：保留块池用于存储持久化 BBT，不交给 dhara 管理
 */
uint32_t gd5f_get_usable_blocks(void) {
    return s_usable_blocks;
}

/*
 * 函数功能：从闪存读取当前生效的 BBT 位图（测试/调试用）
 * 入口参数：无
 * 出口参数：p_bbt     - 位图输出缓冲  uint8_t*  不为 NULL，长度 >= GD5F_BBT_SIZE
 *           p_version - 版本号输出    uint32_t* 不为 NULL
 * 返回值：0 - 成功，GD5F_ERROR - 参数非法
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：返回闪存中版本号最高的有效 BBT 拷贝（供测试验证持久化）
 */
int32_t gd5f_bbt_dump_flash(uint8_t *p_bbt, uint32_t *p_version) {
    if (p_bbt == NULL || p_version == NULL) {
        return GD5F_ERROR;
    }
    gd5f_private_bbt_find_best();
    memcpy(p_bbt, s_bbt_best, GD5F_BBT_SIZE);
    *p_version = s_bbt_version;
    return GD5F_OK;
}

/*
 * 函数功能：擦除保留块池（撤销持久化 BBT，回到仅工厂扫描态）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：仅供测试/出厂重置；调用方需自行还原 RAM BBT
 */
int32_t gd5f_bbt_wipe_pool(void) {
    if (s_bbt_pool_count == 0) {
        return GD5F_OK;
    }
    for (uint32_t i = 0; i < s_bbt_pool_count; i++) {
        gd5f_block_erase(s_bbt_pool_blocks[i]);
    }
    s_bbt_write_slot = 0;
    s_bbt_version = 0;
    return GD5F_OK;
}

/*
 * 函数功能：从保留块池重新加载持久化 BBT 到 RAM（测试/调试用）
 * 入口参数：无
 * 返回值：0 - 成功
 * 限定条件：gd5f2gq5ue_init() 已成功调用
 * 函数说明：将闪存中版本号最高的有效副本 OR 进 RAM BBT
 */
int32_t gd5f_bbt_reload(void) {
    return gd5f_bbt_load();
}

