
#include "EEPROM_24Cxx_Remap.h"

EEPROMDataWrCtrlStruct      EEPROMDataWrCtrl;

EEPROMAccessStatusEnum EEPROM_Data_Write(uint8_t Type, uint16_t Addr, uint8_t *pData, uint8_t Len)
{
  if (EEPROMDataWrCtrl.Busy)                //有数据正在写入中,返回写入失败
    return EEPROM_DATA_RW_FAIL;

  switch (Type)
  {
    case EEPROM_HIREL_DATA_TYPE   :
      return EEPROM_HiRel_Data_Write(Addr, pData, Len);
      break;
    case EEPROM_EXTLIFE_DATA_TYPE :
      return EEPROM_DATA_RW_FAIL;
      break;
    case EEPROM_NORMAL_DATA_TYPE  :
      return EEPROM_Normal_Data_Write(Addr, pData, Len);
      break;
    default                       :
      return EEPROM_DATA_RW_FAIL;
      break;
  }
}

EEPROMAccessStatusEnum EEPROM_Get_Data_Write_Status(void)
{
  return EEPROMDataWrCtrl.Result;
}

EEPROMAccessStatusEnum EEPROM_Startup_Data_Read(uint8_t Type, uint16_t Addr, uint8_t *pData, uint8_t Len)
{
  switch (Type)
  {
    case EEPROM_HIREL_DATA_TYPE   :
      return EEPROM_Startup_HiRel_Data_Read(Addr, pData, Len);
      break;
    case EEPROM_EXTLIFE_DATA_TYPE :
      return EEPROM_DATA_RW_FAIL;
      break;
    case EEPROM_NORMAL_DATA_TYPE  :
      return EEPROM_Startup_Normal_Data_Read(Addr, pData, Len);
      break;
    default                       :
      return EEPROM_DATA_RW_FAIL;
      break;
  }
}

void EEPROM_Access_Service(void)
{
  //写入服务
  if (EEPROMDataWrCtrl.Busy)
  {
    switch (EEPROMDataWrCtrl.Type)
    {
      case EEPROM_HIREL_DATA_TYPE   :
        EEPROM_HiRel_Data_Write_Service();
        break;
      case EEPROM_EXTLIFE_DATA_TYPE :
        break;
      case EEPROM_NORMAL_DATA_TYPE  :
        EEPROM_Normal_Data_Write_Service();
        break;
      default                       :
        break;
    }
  }
  //读出服务
}

/******************************************************************************
HiRel类型数据读写
******************************************************************************/
EEPROMAccessStatusEnum EEPROM_HiRel_Data_Write(uint16_t Addr, uint8_t *pData, uint8_t Len)
{
  uint8_t i;

  if (Addr >= EEPPROM_HIREL_DATA_NUM)
    return EEPROM_DATA_ADDR_ERR;

  if ((Len == 0) || (Len > EEPPROM_HIREL_DATA_MAX_LEN))
    return EEPROM_DATA_LEN_ERR;

  EEPROMDataWrCtrl.Type    = EEPROM_HIREL_DATA_TYPE;

  for (i = 0; i < Len; i++)
  {
    EEPROMDataWrCtrl.Data[i] = pData[i];
    EEPROMDataWrCtrl.Data[Len + i] = ~pData[i];
  }

  EEPROMDataWrCtrl.DataLen = Len * 2;
  EEPROMDataWrCtrl.Row     = (uint8_t)Addr;
  EEPROMDataWrCtrl.Col     = 1;             //0列保留不使用
  EEPROMDataWrCtrl.Offset  = 0;
  EEPROMDataWrCtrl.Retry   = 3;             //每页最多重试写入三次
  EEPROMDataWrCtrl.WrLen   = 0;
  EEPROMDataWrCtrl.WrCnt   = 0;             //清零成功写入次数
  EEPROMDataWrCtrl.Busy    = 1;
  EEPROMDataWrCtrl.Result  = EEPROM_MEMORY_BUSY;

  return EEPROM_DATA_OK;
}


EEPROMAccessStatusEnum EEPROM_Startup_HiRel_Data_Read(uint16_t Addr, uint8_t *pData, uint8_t Len)
{
  uint8_t Row;
  uint8_t Col;
  uint8_t ValidDataCnt;
  uint8_t Data[EEPPROM_HIREL_DATA_MAX_LEN];
  EEPROMAccessStatusEnum Result;

  if (Addr >= EEPPROM_HIREL_DATA_NUM)
    return EEPROM_DATA_ADDR_ERR;

  if ((Len == 0) || (Len > EEPPROM_HIREL_DATA_MAX_LEN))
    return EEPROM_DATA_LEN_ERR;

  Row = (uint8_t)Addr;
  ValidDataCnt = 0;                               //从未收到过有效数据

  for (Col = 1; Col <= 3; Col++)                  //写入顺序为Col1 -> Col3, Col1中将更有可能是最新的数据(考虑写入过程中断电的情况)
  {
    wdt_reset();

    Result = EEPROM_Startup_HiRel_Page_Read(Row, Col, Data, Len);

    if (Result == EEPROM_DATA_OK)                 //读出了有效的数据
    {
      if (ValidDataCnt == 0)                      //如果这是第一次读到有效的数据
      {
        EEPROM_Copy_Data(pData, Data, Len);       //拷贝输出该数据
        ValidDataCnt++;                           //收到有效数据了
      }
      else                                        //如果已经收到过有效的数据
      {
        if (EEPROM_Compare_Data(Data, pData, Len) == 0)
          ValidDataCnt++;
      }
    }
    else if ((Result == EEPROM_MEMORY_ABSENT) || (Result == EEPROM_DATA_ADDR_ERR) || (Result == EEPROM_DATA_LEN_ERR))
      return Result;                              //异常状态则直接返回
  }

  if (ValidDataCnt == 3)
    return EEPROM_DATA_OK;
  else if ((ValidDataCnt == 2) || (ValidDataCnt == 1))
    return EEPROM_DATA_PARTIAL_LOST;
  else
    return EEPROM_DATA_LOST;
}

EEPROMAccessStatusEnum EEPROM_Startup_HiRel_Page_Read(uint8_t Row, uint8_t Col, uint8_t *pData, uint8_t Len)
{
  uint8_t  ReadCnt;
  uint8_t  RetryCnt;
  uint8_t  NVMAbsentCnt;
  uint8_t  PageData[EEPROM_PAGE_SIZE];
  uint16_t PhyAddr;
  EEPROMErrEnum   ReadResult;

  RetryCnt     = 3;                               //最多重试3次
  NVMAbsentCnt = 0;                               //清零存储器未连接计数
  PhyAddr      = (EEPPROM_TOTAL_ROW_NUM * Col + Row) * EEPROM_PAGE_SIZE;

  while (RetryCnt)
  {
    RetryCnt--;

    ReadCnt = 255;                                //尝试读取数据最多255次
    while (ReadCnt)
    {
      ReadResult = EEPROM_Instant_Sequential_Read(EEPPROM_DEVICE_ADDR, PhyAddr, PageData, Len * 2);

      if (ReadResult == EEPROM_OK)
        break;
      else if ((ReadResult == EEPROM_INVALID_DEVICE_ADDR) || (ReadResult == EEPROM_INVALID_DATA_ADDR))
        return EEPROM_DATA_ADDR_ERR;
      else if (ReadResult == EEPROM_DATA_LENGTH_ERR)
        return EEPROM_DATA_LEN_ERR;

      ReadCnt--;
    }

    if (ReadResult == EEPROM_OK)                  //成功从存储器中读出了数据
    {
      if (EEPROM_HiRel_Data_Check(PageData, Len) == 0)
      {
        EEPROM_Copy_Data(pData, PageData, Len);
        return EEPROM_DATA_OK;                    //读出的数据通过了校验,立即回传数据
      }
    }
    else                                          //255次重试都未能从存储器中读出数据
      NVMAbsentCnt++;
  }

  if (NVMAbsentCnt >= 3)                          //3次重试都没法从存储器中读出数据
    return EEPROM_MEMORY_ABSENT;                  //则认为存储器存在硬件故障
  else
    return EEPROM_DATA_RW_FAIL;                   //否则认为存储器内的数据异常
}

uint8_t EEPROM_HiRel_Data_Check(uint8_t *pData, uint8_t Len)
{
  uint8_t i;

  for (i = 0; i < Len; i++)
  {
    if (pData[i] + pData[Len + i] != 0xFF)
      return 1;
  }

  return 0;
}


void EEPROM_HiRel_Data_Write_Service(void)
{
  uint16_t  PhyAddr;

  EEPROMErrEnum       WriteErr;
  EEPROMRWResultEnum  WriteResult;

  if (EEPROMDataWrCtrl.Busy > 1)    //不是首次写入
  {
    WriteResult = EEPROM_Get_Write_Result();

    switch (WriteResult)
    {
      case EEPROM_RW_NONE        : //有致命错误导致写入发起失败或写入结果被其他应用截获而无法正确获得写入状态,中断写入
        EEPROMDataWrCtrl.Busy = 0;
        EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
        return;
        break;

      case EEPROM_RW_DONE        : //写入成功
        EEPROMDataWrCtrl.WrCnt++;                     //成功写入一页
        EEPROMDataWrCtrl.Col++;                       //写下一列
        EEPROMDataWrCtrl.Retry = 3;                   //每页最多重试写入三次

        if (EEPROMDataWrCtrl.Col > 3)                 //3列全部写完
        {
          EEPROMDataWrCtrl.Busy = 0;                  //写入完成

          if (EEPROMDataWrCtrl.WrCnt == 3)            //3页全都写入成功,数据成功写入
            EEPROMDataWrCtrl.Result = EEPROM_DATA_OK;
          else                                        //否则报数据部分写入失败
            EEPROMDataWrCtrl.Result = EEPROM_DATA_PARTIAL_LOST;

          return;
        }
        break;

      case EEPROM_RW_FAIL        : //写入失败
        EEPROMDataWrCtrl.Retry--;

        if (EEPROMDataWrCtrl.Retry == 0)              //重试3次仍然未能成功写入
        {
          EEPROMDataWrCtrl.Col++;                     //尝试写下一列
          EEPROMDataWrCtrl.Retry = 3;                 //每页最多重试写入三次

          if (EEPROMDataWrCtrl.Col > 3)               //3列全部写完
          {
            EEPROMDataWrCtrl.Busy = 0;                //写入完成

            if (EEPROMDataWrCtrl.WrCnt == 0)          //3页全都写入失败,报告数据写入失败
              EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
            else                                      //否则报数据部分写入失败
              EEPROMDataWrCtrl.Result = EEPROM_DATA_PARTIAL_LOST;

            return;
          }
        }
        break;

      case EEPROM_RW_IN_PROGRESS : //前一次写入仍未完成,继续等待
        return;
        break;

      default                    :
        EEPROMDataWrCtrl.Busy = 0;
        EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
        return;
        break;
    }
  }

  PhyAddr = (EEPPROM_TOTAL_ROW_NUM * EEPROMDataWrCtrl.Col + EEPROMDataWrCtrl.Row) * EEPROM_PAGE_SIZE;

  WriteErr = EEPROM_Page_Write(EEPPROM_DEVICE_ADDR, PhyAddr, EEPROMDataWrCtrl.Data, (uint8_t)(EEPROMDataWrCtrl.DataLen));
  EEPROMDataWrCtrl.Busy = 2;

  if (WriteErr != EEPROM_OK)
  {
    EEPROMDataWrCtrl.Busy = 0;                    //中止写入并报告错误

    if ((WriteErr == EEPROM_INVALID_DEVICE_ADDR) || (WriteErr == EEPROM_INVALID_DATA_ADDR))
      EEPROMDataWrCtrl.Result = EEPROM_DATA_ADDR_ERR;
    else if (WriteErr == EEPROM_DATA_LENGTH_ERR)
      EEPROMDataWrCtrl.Result = EEPROM_DATA_LEN_ERR;
    else
      EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
  }
}

/******************************************************************************
ExtLife类型数据读写
******************************************************************************/


/******************************************************************************
Normal类型数据读写
******************************************************************************/
EEPROMAccessStatusEnum EEPROM_Normal_Data_Write(uint16_t Addr, uint8_t *pData, uint16_t Len)
{
  uint8_t   i;
  uint16_t  PageNum;

  if (Addr > EEPPROM_DATA_MAX_ADDR)
    return EEPROM_DATA_ADDR_ERR;

  if ((Len == 0) || (Len > EEPPROM_DATA_MAX_ADDR - Addr + 1))
    return EEPROM_DATA_LEN_ERR;

  EEPROMDataWrCtrl.Type    = EEPROM_NORMAL_DATA_TYPE;

  for (i = 0; i < Len; i++)
  {
    EEPROMDataWrCtrl.Data[i] = pData[i];
  }

  EEPROMDataWrCtrl.DataLen = Len;
  PageNum = Addr / EEPROM_PAGE_SIZE;
  EEPROMDataWrCtrl.Row     = (uint8_t)(PageNum / 4) +  EEPPROM_DATA_ROW_OFFSEET;
  EEPROMDataWrCtrl.Col     = (uint8_t)(PageNum % 4);
  EEPROMDataWrCtrl.Offset  = (uint8_t)(Addr % EEPROM_PAGE_SIZE);
  EEPROMDataWrCtrl.Retry   = 3;             //每页最多重试写入三次
  EEPROMDataWrCtrl.WrLen   = 0;
  EEPROMDataWrCtrl.WrCnt   = 0;             //清零成功写入次数
  EEPROMDataWrCtrl.Busy    = 1;
  EEPROMDataWrCtrl.Result  = EEPROM_MEMORY_BUSY;

  return EEPROM_DATA_OK;
}

EEPROMAccessStatusEnum EEPROM_Startup_Normal_Data_Read(uint16_t Addr, uint8_t *pData, uint8_t Len)
{
  uint8_t   Row;
  uint8_t   Col;
  uint8_t   Offset;
  uint16_t  PageNum;

  uint16_t  RemLen;             //剩余未读取长度
  uint8_t   RetryCnt;           //重试次数
  uint16_t  PhyAddr;            //存储器物理地址
  uint8_t   CurrentLen;         //当前单次读取长度

  EEPROMErrEnum   ReadResult;

  if (Addr > EEPPROM_DATA_MAX_ADDR)
    return EEPROM_DATA_ADDR_ERR;

  if ((Len == 0) || (Len > EEPPROM_DATA_MAX_ADDR - Addr + 1))
    return EEPROM_DATA_LEN_ERR;

  RemLen  = Len;
  PageNum = Addr / EEPROM_PAGE_SIZE;
  Row     = (uint8_t)(PageNum / 4) + EEPPROM_DATA_ROW_OFFSEET;
  Col     = (uint8_t)(PageNum % 4);
  Offset  = (uint8_t)(Addr % EEPROM_PAGE_SIZE);

  while (RemLen)
  {
    PhyAddr    = (EEPPROM_TOTAL_ROW_NUM * Col + Row) * EEPROM_PAGE_SIZE + Offset;
    CurrentLen = EEPROM_PAGE_SIZE - Offset;       //每次最多读取一页
    RetryCnt   = 3;                               //每个Page最多重试读取3次

    if (CurrentLen > RemLen)                      //最后一页,剩余的待读取字节数不足一页
      CurrentLen = (uint8_t)RemLen;

    while (RetryCnt)
    {
      ReadResult = EEPROM_Instant_Sequential_Read(EEPPROM_DEVICE_ADDR, PhyAddr, &pData[Len - RemLen], CurrentLen);

      if (ReadResult == EEPROM_OK)
        break;
      else if ((ReadResult == EEPROM_INVALID_DEVICE_ADDR) || (ReadResult == EEPROM_INVALID_DATA_ADDR))
        return EEPROM_DATA_ADDR_ERR;
      else if (ReadResult == EEPROM_DATA_LENGTH_ERR)
        return EEPROM_DATA_LEN_ERR;

      RetryCnt--;
    }

    if (ReadResult != EEPROM_OK)                  //3次都未能成功从存储器中读出数据
      return EEPROM_DATA_RW_FAIL;

    Offset  = 0;                                  //Offset只用于调节首页偏移量

    Col++;                                        //转向下一页
    if (Col > 3)
    {
      Col = 0;
      Row++;
    }

    RemLen -= CurrentLen;                         //扣除已读取字节数
  }

  return EEPROM_DATA_OK;
}

void EEPROM_Normal_Data_Write_Service(void)
{
  uint16_t  PhyAddr;

  EEPROMErrEnum       WriteErr;
  EEPROMRWResultEnum  WriteResult;

  if (EEPROMDataWrCtrl.Busy > 1)    //不是首次写入
  {
    WriteResult = EEPROM_Get_Write_Result();

    switch (WriteResult)
    {
      case EEPROM_RW_NONE        : //有致命错误导致写入发起失败或写入结果被其他应用截获而无法正确获得写入状态,中断写入
        EEPROMDataWrCtrl.Busy = 0;
        EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
        return;
        break;

      case EEPROM_RW_DONE        : //写入成功
        EEPROMDataWrCtrl.Offset  = 0;                 //Offset只用于调节首页偏移量
        EEPROMDataWrCtrl.Col++;                       //转向下一页
        if (EEPROMDataWrCtrl.Col > 3)
        {
          EEPROMDataWrCtrl.Col   = 0;
          EEPROMDataWrCtrl.Row++;
        }

        EEPROMDataWrCtrl.WrCnt  += EEPROMDataWrCtrl.WrLen;
        EEPROMDataWrCtrl.Retry   = 3;                 //每页最多重试写入三次

        if (EEPROMDataWrCtrl.WrCnt >= EEPROMDataWrCtrl.DataLen)
        {
          EEPROMDataWrCtrl.Busy = 0;                  //写入完成
          EEPROMDataWrCtrl.Result = EEPROM_DATA_OK;
          return;
        }
        break;

      case EEPROM_RW_FAIL        : //写入失败
        EEPROMDataWrCtrl.Retry--;

        if (EEPROMDataWrCtrl.Retry == 0)              //重试3次仍然未能成功写入
        {
          EEPROMDataWrCtrl.Busy = 0;                  //中止写入并报告写入失败
          EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
          return;
        }
        break;

      case EEPROM_RW_IN_PROGRESS : //前一次写入仍未完成,继续等待
        return;
        break;

      default                    :
        EEPROMDataWrCtrl.Busy = 0;
        EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
        return;
        break;
    }
  }

  PhyAddr    = (EEPPROM_TOTAL_ROW_NUM * EEPROMDataWrCtrl.Col + EEPROMDataWrCtrl.Row) * EEPROM_PAGE_SIZE + EEPROMDataWrCtrl.Offset;
  EEPROMDataWrCtrl.WrLen = EEPROM_PAGE_SIZE - EEPROMDataWrCtrl.Offset;            //每次最多写入一页

  if (EEPROMDataWrCtrl.WrLen > EEPROMDataWrCtrl.DataLen - EEPROMDataWrCtrl.WrCnt) //最后一页,剩余的待写入字节数不足一页
    EEPROMDataWrCtrl.WrLen = EEPROMDataWrCtrl.DataLen - EEPROMDataWrCtrl.WrCnt;

  WriteErr = EEPROM_Page_Write(EEPPROM_DEVICE_ADDR, PhyAddr, &EEPROMDataWrCtrl.Data[EEPROMDataWrCtrl.WrCnt], EEPROMDataWrCtrl.WrLen);
  EEPROMDataWrCtrl.Busy = 2;

  if (WriteErr != EEPROM_OK)
  {
    EEPROMDataWrCtrl.Busy = 0;                    //中止写入并报告错误

    if ((WriteErr == EEPROM_INVALID_DEVICE_ADDR) || (WriteErr == EEPROM_INVALID_DATA_ADDR))
      EEPROMDataWrCtrl.Result = EEPROM_DATA_ADDR_ERR;
    else if (WriteErr == EEPROM_DATA_LENGTH_ERR)
      EEPROMDataWrCtrl.Result = EEPROM_DATA_LEN_ERR;
    else
      EEPROMDataWrCtrl.Result = EEPROM_DATA_RW_FAIL;
  }
}

void EEPROM_Copy_Data(uint8_t *pDstData, uint8_t *pSrcData, uint8_t Len)
{
  uint8_t i;

  for (i = 0; i < Len; i++)
    pDstData[i] = pSrcData[i];
}

uint8_t EEPROM_Compare_Data(uint8_t *pDstData, uint8_t *pSrcData, uint8_t Len)
{
  uint8_t i;

  for (i = 0; i < Len; i++)
  {
    if (pDstData[i] - pSrcData[i])
      return 1;
  }

  return 0;
}















