/*********************************************************************
 * Function:        CIPHER_STATUS PHYCipher(INPUT CIPHER_MODE CipherMode, INPUT SECURITY_INPUT SecurityInput, OUTPUT BYTE *OutData, OUTPUT BYTE *OutDataLen)
 *
 * PreCondition:    Called by DataEncrypt or DataDecrypt
 *
 * Input:           CIPHER_MODE CipherMode       - Either MODE_ENCRYPTION or MODE_DECRYPTION
 *                  SECURITY_INPUT SecurityInput - Cipher operation input. Filled by DataEncryption or DataDecryption
 *
 * Output:          BYTE *OutData                - Encrypted or decrypted data, including MIC
 *                  BYTE *OutDataLen             - Data length after cipher operation, including MIC bytes
 *                  CIPHER_STATUS                - Cipher operation result
 *
 * Side Effects:    Input data get encrypted or decrypted and put into output buffer
 *
 * Overview:        This is the function that invoke the hardware cipher to do encryption and decryption
 ********************************************************************/
CIPHER_STATUS PHYCipher(INPUT CIPHER_MODE CipherMode, INPUT SECURITY_INPUT SecurityInput, OUTPUT BYTE *OutData, OUTPUT BYTE *OutDataLen)
{
    BYTE CipherRetry = CIPHER_RETRY;
    BYTE i;
    WORD loc;

    // make sure that we are not in the process of sending out a packet
    loc = 0;
    while( !TxStat.finished )
    {
        loc++;
        if( loc > 0xfff )
        {
            PHY_RESETn = 0;
            MACEnable();
            TxStat.finished = 1;
        }
#if defined(__C30__)
        if(RF_INT_PIN == 0)
        {
            RFIF = 1;
        }
#else
        if( PORTBbits.RB0 == 0 )
        {
            INTCONbits.INT0IF = 1;
        }
#endif

        Nop();
    }

CipherOperationStart:

    // step 1, set the normal FIFO
    // step 1a, fill the length of the header
    if( SecurityInput.cipherMode > 0x04 )
    {
        PHYSetLongRAMAddr(0x000, SecurityInput.HeaderLen+SecurityInput.TextLen+14);
    } else {
        PHYSetLongRAMAddr(0x000, SecurityInput.HeaderLen+14);
    }

    // step 1b, fill the length of the packet
    if( CipherMode == MODE_ENCRYPTION )
    {
        PHYSetLongRAMAddr(0x001, SecurityInput.TextLen+SecurityInput.HeaderLen+14);
    } else {
        PHYSetLongRAMAddr(0x001, SecurityInput.TextLen+SecurityInput.HeaderLen+16);// two additional bytes FCS
    }

    // step 1c, fill the header
    loc = 0x002;
    for(i = 0; i < SecurityInput.HeaderLen; i++)
    {
        PHYSetLongRAMAddr(loc++, SecurityInput.Header[i]);
    }

    // step 1d, fill the auxilary header
    PHYSetLongRAMAddr(loc++, SecurityInput.SecurityControl.Val | nwkSecurityLevel);
    for(i = 0; i < 4; i++)
    {
        PHYSetLongRAMAddr(loc++, SecurityInput.FrameCounter.v[i]);
    }
    for(i = 0; i < 8; i++)
    {
        PHYSetLongRAMAddr(loc++, SecurityInput.SourceAddress->v[i]);
    }
    PHYSetLongRAMAddr(loc++, SecurityInput.KeySeq);

    // step 1e, fill the data to be encrypted or decrypted
    for(i = 0; i < SecurityInput.TextLen; i++)
    {
        PHYSetLongRAMAddr(loc++, SecurityInput.InputText[i]);
    }

    // step 2, set nounce
    SetNonce(SecurityInput.SourceAddress, &(SecurityInput.FrameCounter), SecurityInput.SecurityControl);

    // step 3, set TXNFIFO security key
    loc = 0x280;
    for(i = 0; i < 16; i++)
    {
        PHYSetLongRAMAddr(loc++, SecurityInput.SecurityKey[i]);
    }

    // step 4, set cipher mode either encryption or decryption
    if( CipherMode == MODE_ENCRYPTION )
    {
        PHYSetShortRAMAddr(SECCR2, 0x40);
    } else {
        PHYSetShortRAMAddr(SECCR2, 0x80);
    }

    // step 5, fill the encryption mode
    PHYSetShortRAMAddr(SECCR0, SecurityInput.cipherMode);

    TxStat.cipher = 1;
    // step 6, trigger
    PHYSetShortRAMAddr(TXNMTRIG, 0x03);

    i = 0;
    while( TxStat.cipher )
    {
#if defined(__C30__)
        if(RF_INT_PIN == 0)
        {
            RFIF = 1;
        }
#else
        if( PORTBbits.RB0 == 0 )
        {
            INTCONbits.INT0IF = 1;
        }
#endif
        i++;
#if 1
        // in rare condition, the hardware cipher will stall. Handle such condition
        // here
        if(i > 0x1f)
        {
            // in certain rare cases, the RX and Upper Cipher will block each other
            // in case that happens, reset the RF chip to avoid total disfunction
            ConsolePutROMString((ROM char*)"X");
            PHYTasksPending.Val = 0;
            PHY_RESETn = 0;
            MACEnable();
            break;
        }
#endif
    }
    //PHYSetShortRAMAddr(0x0d, 0x01);

    // if MIC is generated, check MIC here
    if( (CipherMode == MODE_DECRYPTION) && (SecurityInput.cipherMode != 0x01))
    {
        BYTE MIC_check = PHYGetShortRAMAddr(0x30);
        if( MIC_check & 0x40 )
        {
            PHYSetShortRAMAddr(0x30, 0x40);
            // there is a small chance that the hardware cipher will not
            // decrypt for the first time, retry to solve this problem.
            // details documented in errata
            if( CipherRetry )
            {
                CipherRetry--;
                for(loc = 0; loc < 0x255; loc++);
                goto CipherOperationStart;
            }
            PHY_RESETn = 0;
            MACEnable();
            ConsolePutROMString((ROM char *)"MIC error");
            return CIPHER_MIC_ERROR;
        }
    }

    if( TxStat.success )
    {
        // get output data length
        *OutDataLen = PHYGetLongRAMAddr(0x001) - SecurityInput.HeaderLen - 14;
        // get the index of data encrypted or decrypted
        loc = 0x002 + SecurityInput.HeaderLen + 14;

        // if this is a decryption operation, get rid of the useless MIC and two bytes of FCS
        if( CipherMode == MODE_DECRYPTION )
        {
            switch( SecurityInput.cipherMode )
            {
            case 0x02:
            case 0x05:
                *OutDataLen -= 18;
                break;
            case 0x03:
            case 0x06:
                *OutDataLen -= 10;
                break;
            case 0x04:
            case 0x07:
                *OutDataLen -= 6;
                break;
            case 0x01:
                *OutDataLen-= 2;
                break;
            }
        }

        // copy the output data
        for(i = 0; i < *OutDataLen; i++)
        {
            OutData[i] = PHYGetLongRAMAddr(loc++);
        }
        return CIPHER_SUCCESS;
    }

    return CIPHER_ERROR;
}
    BYTE NVMInit( void )
    {
        BYTE    *memBlock;
        BYTE    result = 0;
        WORD NodeDescriptorValiditykey;
        WORD validitykey;

        SPIUnselectEEPROM();

        CLRWDT();

        #if defined(I_SUPPORT_BINDINGS)
            result |= NVMalloc( sizeof(WORD), &bindingValidityKey );
            result |= NVMalloc( sizeof(BINDING_RECORD) * MAX_BINDINGS, &apsBindingTable );
            result |= NVMalloc( BINDING_USAGE_MAP_SIZE, &bindingTableUsageMap );
            result |= NVMalloc( BINDING_USAGE_MAP_SIZE, &bindingTableSourceNodeMap );
        #endif

        #if defined(USE_EXTERNAL_NVM) && defined(STORE_MAC_EXTERNAL)
            result |= NVMalloc( sizeof(WORD), &macLongAddrValidityKey );
            result |= NVMalloc( sizeof(LONG_ADDR), &macLongAddrEE );

            GetMACAddressValidityKey(&validitykey);
            if (validitykey != MAC_ADDRESS_VALID)
            {
                PutMACAddress(macLongAddrByte);
                MACEnable();
            }

        #endif
        #if defined(I_SUPPORT_GROUP_ADDRESSING)
            result |= NVMalloc( sizeof(APS_GROUP_RECORD) * MAX_GROUP, &apsGroupAddressTable);
        #endif

        result |= NVMalloc( sizeof(NEIGHBOR_TABLE_INFO), &neighborTableInfo );
        result |= NVMalloc( sizeof(NEIGHBOR_RECORD) * MAX_NEIGHBORS, &neighborTable );

        #if defined(I_SUPPORT_ROUTING) && !defined(USE_TREE_ROUTING_ONLY)
            result |= NVMalloc( sizeof(ROUTING_ENTRY) * ROUTING_TABLE_SIZE, &routingTable );
        #endif

        result |= NVMalloc( sizeof(WORD), &nodeDescriptorValidityKey);
        result |= NVMalloc( sizeof(NODE_DESCRIPTOR), &configNodeDescriptor );
        result |= NVMalloc( sizeof(NODE_POWER_DESCRIPTOR), &configPowerDescriptor );
        // NOTE - the simple descriptor for the ZDO has been removed in later specs, so the "+1" will go away.
        result |= NVMalloc( sizeof(NODE_SIMPLE_DESCRIPTOR) * (NUM_USER_ENDPOINTS+1), &configSimpleDescriptors );

        result |= NVMalloc(sizeof(PERSISTENCE_PIB), &persistencePIB);

        #if MAX_APS_ADDRESSES > 0
            result |= NVMalloc( sizeof(WORD), &apsAddressMapValidityKey );
            result |= NVMalloc( sizeof(APS_ADDRESS_MAP) * MAX_APS_ADDRESSES, &apsAddressMap );
        #endif

        #if defined(I_SUPPORT_SECURITY)
            result |= NVMalloc( sizeof(BYTE), &nwkActiveKeyNumber );
            result |= NVMalloc( sizeof(NETWORK_KEY_INFO) * NUM_NWK_KEYS, &networkKeyInfo );
            /* location for outgoing nwk frame counters */
			result |= NVMalloc(( sizeof(DWORD_VAL)*2), &outgoingFrameCounterIndex);

        #endif
        #if I_SUPPORT_LINK_KEY == 1
                result |= NVMalloc( sizeof(APS_KEY_PAIR_DESCRIPTOR) * MAX_APPLICATION_LINK_KEY_SUPPORTED, &appLinkKeyTable );
        #endif

        #if I_SUPPORT_LINK_KEY == 1
            #if I_SUPPORT_MULTIPLE_TC_LINK_KEY == 1
                result |= NVMalloc( sizeof(TC_LINK_KEY_TABLE) * MAX_TC_LINK_KEY_SUPPORTED, &TCLinkKeyTable );
            #endif
        #endif

        #if defined(I_SUPPORT_COMMISSIONING)
            result |= NVMalloc( sizeof(BYTE), &activeSASIndex );
            result |= NVMalloc( sizeof(STARTUP_ATTRIBUTE_SET), &default_SAS );
            result |= NVMalloc( (sizeof(STARTUP_ATTRIBUTE_SET) * 2), &Commissioned_SAS );
        #endif

        #if defined(I_SUPPORT_SECURITY)
			result |= NVMalloc(( sizeof(DWORD_VAL)*2), &outgoingFrameCounterIndex);
		#endif

        if (!result)
        {
            #ifdef ENABLE_DEBUG
                ConsolePutROMString((ROM char * const)"Initializing EE...\r\n");
            #endif
            #ifdef I_SUPPORT_COMMISSIONING
                BYTE index;
            #endif
            // If the MAC Address is stored externally, then the user is responsible
            // for programming it.  They may choose to preprogram the EEPROM, or program
            // it based on other input.  It should be programmed with the PutMACAddress() macro.


  /*          // Initialize the trust center address
            //below is code is comment #if defined(I_SUPPORT_SECURITY) && (defined(I_AM_COORDINATOR) || defined(I_AM_TRUST_CENTER))
                if ((memBlock = SRAMalloc( 8 )) == NULL)
                {
                    result = 1;
                }
                else
                {
                    int i = 0;

                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[0];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[1];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[2];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[3];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[4];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[5];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[6];
                    memBlock[i++] = current_SAS.spas.TrustCenterAddress.v[7];

                    NVMWrite( trustCenterLongAddr, memBlock, 8 );
                    SRAMfree( memBlock );
                }
            #endif*/
            GetNodeDescriptorValidity(&NodeDescriptorValiditykey);
            if (NodeDescriptorValiditykey != NODE_DESCRIPTOR_VALID)
            {
                // Initialize the descriptors using the ROM copy.
                if ((memBlock = SRAMalloc( sizeof(NODE_DESCRIPTOR) )) == NULL)
                {
                    result = 1;
                }
                else
                {
                    memcpy( memBlock, (void *)&Config_Node_Descriptor, sizeof(NODE_DESCRIPTOR) );
                    NVMWrite( configNodeDescriptor, memBlock, sizeof(NODE_DESCRIPTOR) );
                    SRAMfree( memBlock );
                    NodeDescriptorValiditykey = NODE_DESCRIPTOR_VALID;
                    PutNodeDescriptorValidity(&NodeDescriptorValiditykey);
                }
            }

            if ((memBlock = SRAMalloc( sizeof(NODE_POWER_DESCRIPTOR) )) == NULL)
            {
                result = 1;
            }
            else
            {
                memcpy( memBlock, (void *)&Config_Power_Descriptor, sizeof(NODE_POWER_DESCRIPTOR) );
                NVMWrite( configPowerDescriptor, memBlock, sizeof(NODE_POWER_DESCRIPTOR) );
                SRAMfree( memBlock );
            }

            if ((memBlock = SRAMalloc( sizeof(NODE_SIMPLE_DESCRIPTOR) )) == NULL)
            {
                result = 1;
            }
            else
            {
                // NOTE - Currently, a simple descriptor is needed for the ZDO endpoint.  When this requirement
                // goes away, take off the "+1".
                int     i;

                for (i=0; i<NUM_USER_ENDPOINTS+1; i++)
                {
					if(NOW_I_AM_A_ROUTER())
						memcpypgm2ram( memBlock, (void *)Config_Simple_Descriptors_MTR + i * sizeof(NODE_SIMPLE_DESCRIPTOR), sizeof(NODE_SIMPLE_DESCRIPTOR) );
					else if (NOW_I_AM_A_CORDINATOR())
						memcpypgm2ram( memBlock, (void *)Config_Simple_Descriptors_ESP + i * sizeof(NODE_SIMPLE_DESCRIPTOR), sizeof(NODE_SIMPLE_DESCRIPTOR) );
                    //memcpypgm2ram( memBlock, (void *)Config_Simple_Descriptors + i * sizeof(NODE_SIMPLE_DESCRIPTOR), sizeof(NODE_SIMPLE_DESCRIPTOR) );
                    NVMWrite( configSimpleDescriptors + (WORD)i * (WORD)sizeof(NODE_SIMPLE_DESCRIPTOR), memBlock, sizeof(NODE_SIMPLE_DESCRIPTOR) );
                }
                SRAMfree( memBlock );
            }
            #ifdef I_SUPPORT_COMMISSIONING
                GetSAS(&current_SAS,default_SAS);
		       if( MSDCL_Commission.ValidCleanStartUp == MSDCL_COMMISSION_DATA_VALID)
		       {
					memcpy(&current_SAS.spas.ExtendedPANId.v[0], &MSDCL_Commission.ExtendedPANId[0], sizeof(MSDCL_Commission.ExtendedPANId) );
			                       
				    current_SAS.spas.PreconfiguredLinkKey[15]= MSDCL_Commission.LinkKey[15];
				    current_SAS.spas.PreconfiguredLinkKey[14]= MSDCL_Commission.LinkKey[14];
				    current_SAS.spas.PreconfiguredLinkKey[13]= MSDCL_Commission.LinkKey[13];
				    current_SAS.spas.PreconfiguredLinkKey[12]= MSDCL_Commission.LinkKey[12];
				    current_SAS.spas.PreconfiguredLinkKey[11]= MSDCL_Commission.LinkKey[11];
				    current_SAS.spas.PreconfiguredLinkKey[10]= MSDCL_Commission.LinkKey[10];
				    current_SAS.spas.PreconfiguredLinkKey[9]= MSDCL_Commission.LinkKey[9];
				    current_SAS.spas.PreconfiguredLinkKey[8]= MSDCL_Commission.LinkKey[8];
				    current_SAS.spas.PreconfiguredLinkKey[7]= MSDCL_Commission.LinkKey[7];
				    current_SAS.spas.PreconfiguredLinkKey[6]= MSDCL_Commission.LinkKey[6];
				    current_SAS.spas.PreconfiguredLinkKey[5]= MSDCL_Commission.LinkKey[5];
				    current_SAS.spas.PreconfiguredLinkKey[4]= MSDCL_Commission.LinkKey[4];
				    current_SAS.spas.PreconfiguredLinkKey[3]= MSDCL_Commission.LinkKey[3];
				    current_SAS.spas.PreconfiguredLinkKey[2]= MSDCL_Commission.LinkKey[2];
				    current_SAS.spas.PreconfiguredLinkKey[1]= MSDCL_Commission.LinkKey[1];
				    current_SAS.spas.PreconfiguredLinkKey[0]= MSDCL_Commission.LinkKey[0];
			
				    current_SAS.spas.ChannelMask.Val = MSDCL_Commission.ChannelMask.Val & 0x07FFF800UL;
			
			   		current_SAS.spas.StartupControl = MSDCL_Commission.StartupStatus;
			    
			    	//PutNeighborTableInfo();
			    	//MSDCL_Commission.DoCleanStartUp = 0;
			    	//NVMWrite( MSDCL_Commission_Locations, (BYTE*)&MSDCL_Commission, sizeof(MSDCL_Commission) );
				}

                if(current_SAS.validitykey != SAS_TABLE_VALID)
                {
                    Initdefault_SAS();
                    index = 0xFF;
                    PutActiveSASIndex(&index);
                }
            #endif
        }

        CLRWDT();

        return result;
    }