#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;
}