// © RiceMotion ( Robert Carl Rice ) 2012-2016 - All rights reserved


// This software makes use of tools and libraries obtained from open source projects or released for

// use by relevant hardware manufactures. However, this software is NOT a part of any open source project.

// It is released only on a "need to know" basis for beta testers of the "RiceCNC Interpolation Engine".

// Recipents of this source code must respect the confidential nature of this software and prevent any

// distribution that could result in counterfeit copies of the "RiceCNC Interpolation Engine".


// © RiceMotion ( Robert Carl Rice ) 2012-2016 - All rights reserved

#include <MyEEProm.h>

#include <MySerial.h>


#include "driverlib/eeprom.h"

#include <driverlib/sysctl.h>

#include "driverlib/emac.h"


#include "inc/hw_eeprom.h"

#include "inc/hw_sysctl.h"

#include "inc/hw_emac.h"



MyEEProm::Un MyEEProm::U = {};


void

     MyEEProm::readEEProm() {

     

    // Get address from EEPROM

    SysCtlPeripheralEnable( SYSCTL_PERIPH_EEPROM0 ); // defined as 2048

    SysCtlDelay( 4 );     // Startup time

    EEPROMInit();          // Reset from power fail


#ifdef DEBUG

     char data[ 80 ];

     snprintf( data, 80,

          " Copy %d words form EEProm to address %p",

          sizeof( Un ),

          (void*)&U.firstEEPromWord );

     String msg = String( data );

     MySerial::sendMessageNonBlocking( &msg );

#endif


    MyEEProm::MyEEPROMRead(

          &U.firstEEPromWord,

          0,

          sizeof( Un ) ); // ROM_EEPROMRead hangs


#ifdef DEBUG

    String msg = "\n EEPROM read";

    MyEEProm::debugEePromCopyDump( &msg );

     MySerial::sendMessageNonBlocking( &msg );

#endif

     };



void

     MyEEProm::debugEePromCopyDump( String* msgPtr ) {

     

    *msgPtr  += "\n  Key";

    MyEEProm::dumpBytes( &MyEEProm::U.S.useKey,              1, msgPtr );


    *msgPtr  += "\n  MAC";

    MyEEProm::dumpBytes( MyEEProm::U.S.macAddress,      6, msgPtr );


    *msgPtr  += "\n  IP";

    MyEEProm::dumpBytes( MyEEProm::U.S.myIpAddress,     4, msgPtr );


    *msgPtr  += "\n  Router";

    MyEEProm::dumpBytes( MyEEProm::U.S.routerAddress,   4, msgPtr );


    *msgPtr  += "\n  Mask";

    MyEEProm::dumpBytes( MyEEProm::U.S.subnetMask,      4, msgPtr );


    *msgPtr  += "\n  Port ";

    *msgPtr  += String( MyEEProm::U.S.port );


    *msgPtr  += "\n\n  Remote IP";

    MyEEProm::dumpBytes( MyEEProm::U.S.remoteIpAddress, 4, msgPtr );


    *msgPtr  += "\n  Remote Port ";

    *msgPtr  += String( MyEEProm::U.S.remote_Port ); };


void

     MyEEProm::dumpBytes( byte* buffer, int count, String* msgPtr ) {

     

    char  data[4];

    while ( count-- ) {

      snprintf( data, 4, " %02X", *buffer++ );

      *msgPtr  += String( data ); }; };



void

     MyEEProm::MyEEPROMRead(

          uint32_t *pui32Data,

          uint32_t ui32PromAddress,

          uint32_t ui32Count)     {


    // Set the block and offset appropriately to read the first word.

    HWREG( EEPROM_EEBLOCK )  = ui32PromAddress >> 6;

    HWREG( EEPROM_EEOFFSET ) = ui32PromAddress >> 2 & 0x0F;


    // Convert byte count to a word count.

    ui32Count >>= 2;


    // Read each word in turn.

    while( ui32Count-- ) {

        // Read the next word through the autoincrementing register.

          uint32_t data = HWREG( EEPROM_EERDWRINC );


#ifdef DEBUG

     char msgdata[ 20 ];

     snprintf( msgdata, 20,

          " Read 0x%X",

          data );

     String msg = String( data );

     MySerial::sendMessageNonBlocking( &msg );

#endif

          

          *pui32Data++ = data;

        // Do we need to move to the next block?  This is the case if the

        // offset register has just wrapped back to 0.  Note that we only

        // write the block register if we have more data to read.  If this

        // register is written, the hardware expects a read or write operation

        // next.  If a mass erase is requested instead, the mass erase will

        // fail.

        if ( ui32Count && HWREG( EEPROM_EEOFFSET ) == 0 )

             HWREG( EEPROM_EEBLOCK )  += 1; };

};


void

     MyEEProm::writeEEProm() {


#ifdef DEBUG

    // Check parameters in a debug build.

     char data[ 40 ];

     snprintf( data, 40,

          " MyEEPROMProgram %d",

          sizeof( Un ) );

     String msg = String( data );

     MySerial::sendMessageNonBlocking( &msg );

#endif


    MyEEProm::MyEEPROMProgram(

          &U.firstEEPromWord,

          0,

          sizeof( Un ) ); // ROM_EEPROMRead hangs

     };


void

     MyEEProm::setUseKey( bool use ) {

     

     MyEEProm::U.S.useKey = use ?

          UseKey :

          0;

     MyEEProm::MyEEPROMProgram(

          (uint32_t*) &U.firstEEPromWord,

          0,

          4); };


uint32_t

     MyEEProm::MyEEPROMProgram(

          uint32_t *pui32Data,

          uint32_t ui32PromAddress,

          uint32_t ui32Count)

{

    // Make sure the EEPROM is idle before we start.

     while( HWREG( EEPROM_EEDONE ) & EEPROM_EEDONE_WORKING );


    // Set the block and offset appropriately to program the first word.

    HWREG( EEPROM_EEBLOCK )          = ui32PromAddress >> 6;

    HWREG( EEPROM_EEOFFSET )     = ui32PromAddress >> 2 & 0x0F;


    // Convert the byte count to a word count.

    ui32Count >>= 2;


    // Write each word in turn.

    while( ui32Count-- ){

          uint32_t data = *pui32Data++;


#ifdef DEBUG

     char msgdata[ 20 ];

     snprintf( msgdata, 20,

          " Write 0x%X",

          data );

     String msg = String( data );

     MySerial::sendMessageNonBlocking( &msg );

#endif


          // Write the next word through the autoincrementing register.

        HWREG( EEPROM_EERDWRINC ) = data;

        // Wait for the write to complete.

          while ( HWREG( EEPROM_EEDONE ) & EEPROM_EEDONE_WORKING );

        // Make sure we completed the write without errors.  Note that we

        // must check this per-word because write permission can be set per

        // block resulting in only a section of the write not being performed.

        if ( HWREG( EEPROM_EEDONE ) & ( EEPROM_EEDONE_NOPERM | EEPROM_EEDONE_INVPL ) )

             break; // An error occurred.

        // Do we need to move to the next block?  This is the case if the

        // offset register has just wrapped back to 0.  Note that we only

        // write the block register if we have more data to read.  If this

        // register is written, the hardware expects a read or write operation

        // next.  If a mass erase is requested instead, the mass erase will

        // fail.

        if( ui32Count && HWREG( EEPROM_EEOFFSET ) == 0)

            HWREG( EEPROM_EEBLOCK ) += 1;

    }

    // Return the current status to the caller.

    return( HWREG( EEPROM_EEDONE ) );

};


MyEEProm::SoftResetReason

     MyEEProm::softResetReason() {

     

     return

          U.S.softResetReason; };


void

     MyEEProm::recordResetReason(

          SoftResetReason     softResetReason ) {

     

     U.S.softResetReason     = softResetReason;

     MyEEProm::writeEEProm(); };


void

     MyEEProm::readMacAddress( byte* mac ) {


     ROM_SysCtlPeripheralEnable(     SYSCTL_PERIPH_EMAC0 );

     ROM_SysCtlPeripheralReset(     SYSCTL_PERIPH_EMAC0 );

     while( ! ROM_SysCtlPeripheralReady( SYSCTL_PERIPH_EMAC0 ) ) {};


     ROM_SysCtlPeripheralEnable(     SYSCTL_PERIPH_EPHY0 );

     ROM_SysCtlPeripheralReset(     SYSCTL_PERIPH_EPHY0 );

     while( ! ROM_SysCtlPeripheralReady( SYSCTL_PERIPH_EPHY0 ) ) {};


//     EMACReset( EMAC0_BASE );

     EMACAddrGet( EMAC0_BASE, 0, mac ); // Get MAC from hardware PHY

     };

     

void

     MyEEProm::setEthernetAddresses(

          char*     data,

          String* msgPtr ) {

    char    myIpAddress    [20]  = "";

    char    routerAddress  [20]  = "";

    char    subnetMask     [20]  = "";

    uint16_t  port                     = 8888;


    sscanf( data+1,

          "%s %s %s %u",

          myIpAddress,

          routerAddress,

          subnetMask,

          &port );


#ifdef DEBUG

    snprintf( data, 100,

          " Parameters\n  IP %s\n  Router %s\n  Mask %s\n  Port %u",

          myIpAddress,

          routerAddress,

          subnetMask,

          port );

    *msgPtr  += String( data );

#endif


     byte     mac[ 6 ];

     readMacAddress( mac );


    snprintf( data, 100,

          " MAC %02X:%02X:%02X:%02X:%02X:%02X",

          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] );

    *msgPtr  += String( data );


    bool change  = false;


    if ( MyEEProm::U.S.useKey != UseKey ) {

          MyEEProm::U.S.useKey = UseKey;

          change    = true; };


    compare( MyEEProm::U.S.macAddress, mac, 6, &change );

    // Parse the ip addresses

    parseIpAddress( myIpAddress,   MyEEProm::U.S.myIpAddress,   &change );

    parseIpAddress( routerAddress, MyEEProm::U.S.routerAddress, &change );

    parseIpAddress( subnetMask,    MyEEProm::U.S.subnetMask,    &change );

    

    if ( port != MyEEProm::U.S.port ) {

          MyEEProm::U.S.port = port;

          change    = true; };

    if ( change ) {

          *msgPtr   += "\n Recording changes\n ";

#ifdef DEBUG

          MyEEProm::debugEePromCopyDump( msgPtr );

#endif

          MyEEProm::writeEEProm(); }


    else

          *msgPtr   += "\n Already recorded\n "; };



void

     MyEEProm::parseIpAddress(

          char*          stringPtr,

          byte*          readByte,

          bool*     change ) {

     

    byte ip[]   = { 0, 0, 0, 0 };

    char* ptr   = stringPtr;

    for ( int count = 0; count < 4; count++ ) {

          ip[ count ]  = strtol( ptr, &ptr, 10 );

          if ( *ptr != '.' && *ptr != ',' ) break;

          ptr++; };

    compare( readByte, ip, 4, change ); };



void

     MyEEProm::compare(

          byte*          readByte,

          byte*          writeByte,

          int               length,

          bool*          change ) {


    while ( length-- ) {

          if ( *writeByte  != *readByte ) {

               *change = true;

               *readByte     = *writeByte; };

          readByte++;

          writeByte++; }; };


void

     MyEEProm::recordConnectionFrom(

          IPAddress     remote_IP,

          uint16_t     remote_port ) {

     

     uint32_t  rawRemote = uint32_t( remote_IP );


     bool change      = false;

     MyEEProm::compare(

          MyEEProm::U.S.remoteIpAddress,

          (byte*) &rawRemote,

          4,

          &change );

     

     if ( remote_port != MyEEProm::U.S.remote_Port ) {

          MyEEProm::U.S.remote_Port = remote_port;

          change      = true; };


     if ( change ) {

          String msg = " Recording remote address and port";

          MySerial::sendMessageNonBlocking( &msg );

          MyEEProm::writeEEProm(); };

     };


bool

     MyEEProm::remoteIsRecorded (

          IPAddress     remote_IP,

          uint16_t     remote_port ) {


     uint32_t     compareRawRemote     = uint32_t( remote_IP );

     byte*          compareRemoteBytes     = (byte*) &compareRawRemote;


     byte*          recordedRemoteBytes     = MyEEProm::U.S.remoteIpAddress;


     for ( int i = 0; i < 4; i++ ) {

          if ( *compareRemoteBytes++ != *recordedRemoteBytes++ )

               return false; };


     if ( remote_port != MyEEProm::U.S.remote_Port ) {

          return false; };

     

     return true; };