#include "EEPROM_24Cxx_Remap.h"
#include "Simulated_IIC_Master.h"
#include "Watchdog.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;             
  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++)                  
  {
    WDT_Clear( );
    
    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;                               
  NVMAbsentCnt = 0;                               
  PhyAddr      = (EEPPROM_TOTAL_ROW_NUM * Col + Row) * EEPROM_PAGE_SIZE;
  
  while (RetryCnt)
  {
    RetryCnt--;
    
    ReadCnt = 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                                          
      NVMAbsentCnt++;
  }
  
  if (NVMAbsentCnt >= 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)                 
                                    {
                                      EEPROMDataWrCtrl.Busy = 0;                  
                                      
                                      if (EEPROMDataWrCtrl.WrCnt == 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)              
                                    {
                                      EEPROMDataWrCtrl.Col++;                     
                                      EEPROMDataWrCtrl.Retry = 3;                 
                                    
                                      if (EEPROMDataWrCtrl.Col > 3)               
                                      {
                                        EEPROMDataWrCtrl.Busy = 0;                
                                        
                                        if (EEPROMDataWrCtrl.WrCnt == 0)          
                                          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;
  }
}




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;                               
    
    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)                  
      return EEPROM_DATA_RW_FAIL;
      
    Offset  = 0;                                  
    
    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;                 
                                    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)              
                                    {
                                      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;
}