#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�н����п��������µ�����(����д������жϵ�����)
    {

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