/****************************************************************************/
/*
 * MODULE              JN-AN-1162 JenNet-IP Smart Home
 *
 * DESCRIPTION         LowEnergySwitch - Implementation
 */
/****************************************************************************/
/*
 * This software is owned by NXP B.V. and/or its supplier and is protected
 * under applicable copyright laws. All rights are reserved. We grant You,
 * and any third parties, a license to use this software solely and
 * exclusively on NXP products [NXP Microcontrollers such as JN5168, JN5164].
 * You, and any third parties must reproduce the copyright and warranty notice
 * and any other legend of ownership on each copy or partial copy of the
 * software.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright NXP B.V. 2014. All rights reserved
 */
/****************************************************************************/
#ifndef JENNIC_CHIP_FAMILY_JN516x
#error Only JN516x supported!
#endif

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/
#include "jendefs.h"
#include "AppHardwareApi.h"
#include "PeripheralRegs.h"
#include "string.h"
#include "MMAC.h"
#include "AHI_AES.h"

extern PUBLIC bool_t bAHI_ReadEEPROM(
          uint8  u8Segment,
          uint8  u8Offset,
          uint8  u8Bytes,
          uint8 *pu8Buffer);

extern PUBLIC bool_t bAHI_WriteEEPROM(
          uint8  u8Segment,
          uint8  u8Offset,
          uint8  u8Bytes,
          uint8 *pu8Buffer);

#ifdef UART_DEBUG
extern PUBLIC void vDebugInit(void);
extern PUBLIC void vDebug(char *pcMessage);
extern PUBLIC void vDebugHex(uint32 u32Data, int iSize);
#else
#define vDebugInit();
#define vDebug(A);
#define vDebugHex(A,B);
#endif

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/
#ifdef MK_DIO_BUTTON
#define DIO_BUTTON_MASK (1<<MK_DIO_BUTTON) /*(1<<8) 0*/
#else
#define DIO_BUTTON_MASK 0
#endif

/* Active channel */
#define TAG_CHANNEL        (MK_CHANNEL)

/* Customer MAC address at page 5, word 7 (16-byte words, 16 words/page) */
#define MAC_ADDR1_BASE 0x01001570
/* Default MAC address at page 5, word 8 (16-byte words, 16 words/page) */
#define MAC_ADDR0_BASE 0x01001580

/****************************************************************************/
/***        Type Definitions                                              ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Function Prototypes                                     ***/
/****************************************************************************/

/****************************************************************************/
/***        Exported Variables                                            ***/
/****************************************************************************/

/****************************************************************************/
/***        Local Variables                                               ***/
/****************************************************************************/
/* bTxComplete is used to flag TX completion from ISR to main code */
volatile bool_t bTxComplete;

/* System interrupts */
volatile uint32 u32Wake;

uint32 u32FrameCounter;

uint8 const au8Key[16] __attribute__ ((aligned (4)))
    = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
       0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81};

/****************************************************************************/
/***        Functions                                                     ***/
/****************************************************************************/
/****************************************************************************
 *
 * NAME:       vIntHandlerBbcPatch
 */
/**
 * Baseband interrupt handler
 *
 * @ingroup g_rom_patch
 *
 * @return
 *
 * @note
 * Replaces Baseband part of XCV_vISR().
 * Changed order of interrupt evaluation and added fix for problem of no
 * automatic ack. for frames sent to broadcast PAN ID
 * Will be incorporated in revised baseline
 *
 ****************************************************************************/
PRIVATE void vInterruptFired(uint32 u32InterruptBitmap)
{
    if (u32InterruptBitmap & E_MMAC_INT_TX_COMPLETE)
    {
        bTxComplete = TRUE;
    }
}


void vHwDeviceIntCallback(uint32 u32DeviceId, uint32 u32ItemBitmap)
{
    u32Wake = u32ItemBitmap & E_AHI_SYSCTRL_WK0_MASK;
}

PRIVATE void vSendFrame(uint8 u8Command)
{
    uint8 u8TxRepeats;
    tsMacFrame sMacFrame;
    uint8 const au8Command[5] = {0xff, 0xff, 0xfe, 0x04, 0x00};
    uint8         au8Data[13];
    uint8         u8Alen;
    uint8         u8Mlen;
    uint8         u8M;
    tuAES_Block    uNonce;
    bool_t         bValid;

    vMMAC_SetChannel(TAG_CHANNEL);

    u8TxRepeats = 0;

   /* Set Frame Control Field:
        Frame Type Data FT = 1 (bit 0-2)
        No Security SE = 0 (bit 3)
        No frame pending FP =0 (bit 4)
        No acknowledgement ACK = 0 (bit 5)
        Not Intra pan IP = 0 (bit 6)
        Dest Addr Mode = 2  (bit 10,11)
        Source Addr Mode = 0 (bit 14,15) */
    sMacFrame.u16FCF = 0xc841;

    /* Broadcast Destination PANID */
    sMacFrame.u16DestPAN = 0xffff;

    /* Broadcast Destination Short Source Address */
    sMacFrame.uDestAddr.u16Short = 0xffff;

    /* Extended Source Address... */
    /* ... Check for MAC1 */
    sMacFrame.uSrcAddr.sExt.u32L = *(uint32 *)(MAC_ADDR1_BASE + 4);
    sMacFrame.uSrcAddr.sExt.u32H = *(uint32 *)(MAC_ADDR1_BASE);

    /* ...If MAC1 is blank, use MAC0 instead */
    if (   (sMacFrame.uSrcAddr.sExt.u32L == 0xffffffff)
        && (sMacFrame.uSrcAddr.sExt.u32H == 0xffffffff))
    {
        sMacFrame.uSrcAddr.sExt.u32L = *(uint32 *)(MAC_ADDR0_BASE + 4);
        sMacFrame.uSrcAddr.sExt.u32H = *(uint32 *)(MAC_ADDR0_BASE);
    }

    sMacFrame.u8SequenceNum = u32FrameCounter & 0xFF;

    /* Payload */
    sMacFrame.uPayload.au8Byte[0] = (uint8)(u32FrameCounter >> 8);
    sMacFrame.uPayload.au8Byte[1] = (uint8)(u32FrameCounter >> 16);
    sMacFrame.uPayload.au8Byte[2] = (uint8)(u32FrameCounter >> 24);
    memcpy(&sMacFrame.uPayload.au8Byte[3], au8Command, 5);
    sMacFrame.uPayload.au8Byte[8] = u8Command;

    /* Write Length of MSDU */
    sMacFrame.u8PayloadLength = 11;

    /* Encryption... */
    /* ...Create nonce from source MAC address and frame counter */
    memcpy(&uNonce.au8[1], &sMacFrame.uSrcAddr.sExt, 8);
    memcpy(&uNonce.au8[9], &u32FrameCounter, 4);
    uNonce.au8[13] = 5; /* Security level */

    /* ...Create A data: source MAC address, frame counter, length (u8Alen=13) */
    memcpy(&au8Data[0], &sMacFrame.uSrcAddr.sExt, 8);
    memcpy(&au8Data[8], &u32FrameCounter, 4);
    au8Data[12] = 11; /* Length */
    u8Alen = 13;
    u8M = 2;
    u8Mlen = 6;

#if 1
    /* ...Display nonce and data */
    {
        int j;
        vDebug("Nonce:");
        for (j = 1; j < 14; j++)
        {
            vDebugHex(uNonce.au8[j], 2);
        }
        vDebug("\r\nAdata:");
        for (j = 0; j < (u8Alen); j++)
        {
            vDebugHex(au8Data[j], 2);
        }
        vDebug("\r\nMlen:");
        vDebugHex(u8Mlen, 2);
        vDebug("\r\nMdata:");
        for (j = 0; j < u8Mlen; j++)
        {
            vDebugHex(sMacFrame.uPayload.au8Byte[j + 3], 2);
        }
        vDebug("\r\nKey:");
        vDebugHex((uint32)au8Key, 8);
        vDebug(":");
        for (j = 0; j < 16; j++)
        {
            vDebugHex(au8Key[j], 2);
        }
        vDebug("\r\n");
    }
#endif

	void *puKey = (void *) au8Key;

    vACI_CmdWaitBusy();
    if (bACI_WriteKey((tsReg128 *)puKey) == FALSE)
    {
        /* Failed, so give up */
        return;
    }

    /* ...Transform: decrypted payload will overwrite the encrypted payload */
    vACI_OptimisedCcmStar(
                     TRUE,
                     u8M,
                     u8Alen,
                     u8Mlen,
                     &uNonce,
                     &au8Data[0],                    /* A data */
                     &sMacFrame.uPayload.au8Byte[3], /* Input/output data */
                     &sMacFrame.uPayload.au8Byte[9], /* Checksum */
                     &bValid);
    do
    {
        bTxComplete = FALSE;

        /* Start Transmit with no CCA */
        vMMAC_StartMacTransmit(&sMacFrame, E_MMAC_TX_START_NOW
                                           | E_MMAC_TX_NO_AUTO_ACK
                                           | E_MMAC_TX_NO_CCA);

        while (bTxComplete != TRUE)
        {
           /* Interrupt will be generated when finished, so can doze now */
           vAHI_CpuDoze();
        }

        u8TxRepeats++;

#if 1
        /* Sleep for 1ms second */
        u32Wake = 0;
        vAHI_WakeTimerStart(E_AHI_WAKE_TIMER_0, 32); /* 1/10th second */

        while (u32Wake == 0)
        {
           /* Interrupt will be generated when finished, so can doze now */
           vAHI_CpuDoze();
        }
#endif

    } while (u8TxRepeats < 3);
}

/****************************************************************************
 *
 * NAME: AppWarmStart
 *
 * DESCRIPTION:
 * Entry point for application from boot loader. Simply jumps to AppColdStart
 * as, in this instance, application will never warm start.
 *
 * RETURNS:
 * Never returns.
 *
 ****************************************************************************/
PUBLIC void AppWarmStart(void)
{
	/* Disable watchdog */
    vAHI_WatchdogStop();

	/* Wait for clock to stabilise */
    while(bAHI_Clock32MHzStable() == FALSE);

    /* Enable MAC, and enable interrupts */
    vMMAC_Enable();
    vMMAC_EnableInterrupts(vInterruptFired);

    /* Init AHI. This will clear the wake interrupt */
    u32AHI_Init();

    vDebugInit();

    /* Disable internal pull-ups */
    vAHI_DioSetPullup    ((uint32) 0x0, DIO_BUTTON_MASK);
    /* Make button DIO an input */
    vAHI_DioSetDirection ((uint32) DIO_BUTTON_MASK, 0x0);

    /* Enable DIO interrupts */
    vAHI_SysCtrlRegisterCallback(vHwDeviceIntCallback);
    vAHI_WakeTimerEnable(E_AHI_WAKE_TIMER_0, TRUE);

    /* Start radio */
    vMMAC_ConfigureRadio();

	/* Button mask used to set command ? */
	#if (DIO_BUTTON_MASK)
	{
	    uint32 u32Buttons;

		/* Read the input */
		u32Buttons = u32AHI_DioReadInput() & DIO_BUTTON_MASK;
		vDebug("u32Buttons value is ");
		vDebugHex(u32Buttons, 8);
		vDebug("\r\n");

		/* Button pressed ? */
		if ((u32Buttons & DIO_BUTTON_MASK) == 0)
		{
			uint8 u8Command;

			/* Read, increment and write frame counter. Don't care about wrapping
			   around; let it happen */
			vDebug("About to read EEPROM");
			(void)bAHI_ReadEEPROM(1, 0, sizeof(uint32), (uint8 *)&u32FrameCounter);
			u32FrameCounter++;
			(void)bAHI_WriteEEPROM(1, 0, sizeof(uint32), (uint8 *)&u32FrameCounter);
			vDebug("EEPROM read, value is ");
			vDebugHex(u32FrameCounter, 8);
			vDebug("\r\n");

			/* Derive command from frame counter */
			u8Command = (uint8) u32FrameCounter & 0x1;
			/* Send command */
			vSendFrame(u8Command);
		}
	}
	/* No button just toggle on/off locally ? */
	#else
	{
		uint8 u8Command;

		/* Read, increment and write frame counter. Don't care about wrapping
		   around; let it happen */
		vDebug("About to read EEPROM");
		(void)bAHI_ReadEEPROM(1, 0, sizeof(uint32), (uint8 *)&u32FrameCounter);
		u32FrameCounter++;
		(void)bAHI_WriteEEPROM(1, 0, sizeof(uint32), (uint8 *)&u32FrameCounter);
		vDebug("EEPROM read, value is ");
		vDebugHex(u32FrameCounter, 8);
		vDebug("\r\n");

		/* Derive command from frame counter */
	    u8Command = (uint8) u32FrameCounter & 0x1;
		/* Send command */
    	vSendFrame(u8Command);
	}
	#endif

	/* Powered by energy harvester ? */
	#ifdef MK_ENERGY_HARVESTER
	{
		/* Loop until power runs out */
    	while (1);
	}
	/* Battery powered ? */
	#else
	{
		#if 0
		/* Set up interrupt on rising DIO 8 edge */
	    vAHI_DioSetDirection   ((uint32) (1<<8), 0x0);
	    vAHI_DioInterruptEdge  ((uint32) (1<<8), 0x0);
	    vAHI_DioInterruptEnable((uint32) (1<<8), 0x0);
		/* Deep sleep to preserve battery */
		vAHI_Sleep(E_AHI_SLEEP_OSCOFF_RAMOFF /*E_AHI_SLEEP_DEEP*/);
		#else
		/* Deep sleep to preserve battery */
		vAHI_Sleep(E_AHI_SLEEP_DEEP);
		#endif
		/* Loop until sleep */
		while(1);
	}
	#endif
}

/****************************************************************************
 *
 * NAME: AppColdStart
 *
 * DESCRIPTION:
 * Entry point for application from boot loader. Initialises system and runs
 * main loop.
 *
 * RETURNS:
 * Never returns.
 *
 ****************************************************************************/
PUBLIC void AppColdStart(void)
{
    /* AppWarmStart continues the start sequence */
    AppWarmStart();

    while(1); /* Don't need this, but leave it in as it makes the code smaller
               * (hints to compiler that it doesn't have to restore registers
               * on exit from function) */
}
