/*
 *
 *   Copyright (C) 2016 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission of
 *   Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been supplied,
 *   and under no circumstances can it be used with non-TI connectivity device.
 *
 */

/*
 * Application Name     -   Radio Tool / Transceiver Mode
 * Application Overview -   The main usage of the Radio Tool is to serve as a
 *                          control panel for direct access to the Radio.
 *                          It can be used for the RF evaluation and for
 *                          certification purposes (FCC, IC, ETSI, TELEC, etc.).
 * Application Details  -   doc\examples\radio_tool.pdf
 */

#include "radiotool.h"

#ifdef RADIO_TOOL_WINDOWS_DLL
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#endif

/* Definitions for template frame */
#define FRAME_TYPE          0xC8    /* QOS data */
#define FRAME_CONTROL       0x01    /* TO DS */
#define DURATION            0x00, 0x00
#define RECEIVE_ADDR        0x55, 0x44, 0x33, 0x22, 0x11, 0x00
#define TRANSMITTER_ADDR    0x00, 0x11, 0x22, 0x33, 0x44, 0x55
#define DESTINATION_ADDR    0x55, 0x44, 0x33, 0x22, 0x11, 0x00
#define FRAME_NUMBER        0x00, 0x00
#define QOS_CONTROL         0x00, 0x00

#define RA_OFFSET               4
#define TA_OFFSET               10
#define DA_OFFSET               16
#define FRAME_SIZE              1500
#define RX_BUFFER_SIZE          1470
#define RX_RECV_TIMEOUT_SEC     0
#define RX_RECV_TIMEOUT_USEC    20000

_u8 TemplateFrame[] = {
    /*---- wlan header start -----*/
    FRAME_TYPE,             /* version type and sub type */
    FRAME_CONTROL,          /* Frame control flag */
    DURATION,               /* duration */
    RECEIVE_ADDR,           /* Receiver Address */
    TRANSMITTER_ADDR,       /* Transmitter Address */
    DESTINATION_ADDR,       /* destination Address */
    FRAME_NUMBER,           /* frame number */  
    QOS_CONTROL             /* QoS control */
};

_i16 rxSocket;
_u8 DataFrame[FRAME_SIZE];

volatile _u8 txStarted;

/* */
#ifdef SL_IF_TYPE_UART
RTAPI RadioToolOpen(_u8 comPort, _u32 baudRate)
#else
RTAPI RadioToolOpen()
#endif
{
    _u8 policyVal;
    _i32 retVal = 0;

#ifdef SL_IF_TYPE_UART
    SlStudioUartIfParams_t params;
    params.BaudRate = baudRate;
    params.FlowControlEnable = 1;
    params.CommPort = comPort;

    retVal = sl_Start(NULL, (char*)&params, NULL);
#else
    retVal = sl_Start(NULL, NULL, NULL);
#endif
    
    if (retVal < 0)
        return retVal;

    retVal = sl_WlanSetMode(ROLE_STA);
    if (retVal < 0)
        return retVal;

    /* make sure policy is set to NONE */
    retVal = sl_WlanPolicySet(SL_WLAN_POLICY_CONNECTION, SL_WLAN_CONNECTION_POLICY(0, 0, 0, 0), &policyVal, 1);
    if (retVal < 0)
        return retVal;

    retVal = sl_Stop(0xFFFF);
    if (retVal < 0)
        return retVal;

#ifdef SL_IF_TYPE_UART
    retVal = sl_Start(NULL, (char*)&params, NULL);
#else
    retVal = sl_Start(NULL, NULL, NULL);
#endif

    return retVal;
}

/* */
RTAPI RadioToolClose()
{
    _i32 retVal = 0;

    retVal = sl_Stop(0xFFFF);
    if (retVal < 0)
        return RADIO_TOOL_ERROR_STOPPING_RADIO_TOOL;

    return 0;
}

/* */
RTAPI RadioStopTX(RadioTxMode_e eTxMode)
{
    _i32 retVal;

    switch(eTxMode)
    {
        case RADIO_TX_PACKETIZED:
            txStarted = 0;
            retVal = 0;
            break;

        case RADIO_TX_CW:
            sl_Send(rxSocket, NULL, 0, CW_STOP);
            //Sleep(100);
            retVal = sl_Close(rxSocket);
            break;

        case RADIO_TX_CONTINUOUS:
            retVal = sl_Close(rxSocket);
            break;

        default:
            retVal = RADIO_TOOL_ERROR_TX_TYPE_UNKNOWN;
    }

    return retVal;
}

/* */
RTAPI RadioStartTX(RadioTxMode_e eTxMode,
	_i8                ePowerLevel_Tone,
	_u8                eChannel,
	SlWlanRateIndex_e  eRate,
	_u8                ePreamble,
	RadioDataPattern_e eDataPattern,
	_u16               eSize,
	_u32               eDelay,
	_u32               eAmount,
	_u8                eOverrideCCA,
	SlTxInhibitThreshold_e eCCAThreshold,
	_u8                eEnableACKs,
    _u8                *epDstMac)
{
    _u16 loopIdx;
    _i32 length;
    _u16 pConfigLen = SL_WLAN_BSSID_LENGTH;
	_u32 amount = eAmount;
    _i32 minDelay;
	_u32 thrshld = eCCAThreshold;
	_u32 enableACKs = eEnableACKs;

    _i32 retVal = 0;

	_u32 counter = 0;

	/* Input checkings */
	if (eTxMode == RADIO_TX_CW)
	{
	    if (ePowerLevel_Tone < RADIO_TOOL_TX_CW_TONE_MIN || ePowerLevel_Tone > RADIO_TOOL_TX_CW_TONE_MAX)
		    return -1;
	}
	else if (eTxMode != RADIO_TX_CONTINUOUS || eTxMode != RADIO_TX_PACKETIZED)
	{
		if (ePowerLevel_Tone < RADIO_TOOL_TX_POWER_LEVEL_MIN || ePowerLevel_Tone > RADIO_TOOL_TX_POWER_LEVEL_MAX)
		    return -1;
	}
	else
	{
		return -1;
	}

	if (eChannel < 1 || eChannel > 13)
		return -1;

	// Consider checking all others...

    // Common settings for both Packetized and Continuous modes
    if ((RADIO_TX_PACKETIZED == eTxMode) || (RADIO_TX_CONTINUOUS == eTxMode))
    {
        /* build the frame */
        switch (eDataPattern)
        {
            case PATTERN_ALL_0:
                memset(DataFrame, 0, FRAME_SIZE);
                break;
                
            case PATTERN_ALL_1:
                memset(DataFrame, 1, FRAME_SIZE);
                break;
                
            case PATTERN_INCREMENTAL:
                for (loopIdx = 0; loopIdx < FRAME_SIZE; loopIdx++)
                    DataFrame[loopIdx] = (_u8)loopIdx;
                break;
                
            case PATTERN_DECREMENTAL:
                for (loopIdx = 0; loopIdx < FRAME_SIZE; loopIdx++)
                    DataFrame[loopIdx] = (_u8)(FRAME_SIZE - 1 - loopIdx);
                break;

			case PATTERN_PN9:
                //TODO
                break;

			case PATTERN_PN15:
                //TODO
                break;

			case PATTERN_PN23:
                //TODO
                break;
                
            default:
                memset(DataFrame, 0, FRAME_SIZE);
        }

		/* Insert Source and Target MAC addresses into data frame */
        retVal = sl_NetCfgGet(SL_NETCFG_MAC_ADDRESS_GET, NULL, &pConfigLen, &TemplateFrame[TA_OFFSET]);
		if (retVal < 0)
        {
            return retVal;
        }
        memcpy_s(&TemplateFrame[RA_OFFSET], SL_WLAN_BSSID_LENGTH, epDstMac, SL_WLAN_BSSID_LENGTH);
        memcpy_s(&TemplateFrame[DA_OFFSET], SL_WLAN_BSSID_LENGTH, epDstMac, SL_WLAN_BSSID_LENGTH);
        memcpy_s(DataFrame, sizeof(TemplateFrame), TemplateFrame, sizeof(TemplateFrame));

        /* open a RAW/DGRAM socket based on CCA override */
        rxSocket = sl_Socket(SL_AF_RF, (eOverrideCCA==1 ? SL_SOCK_RAW:SL_SOCK_DGRAM), eChannel);
        if (rxSocket < 0)
        {
            return RADIO_TOOL_ERROR_TX_CREATING_RAW_SOCKET;
        }

		/* Set CCA threshold, if not overriding CCA */
		if (eOverrideCCA == 0)
		{
			retVal = sl_SetSockOpt(rxSocket, SL_SOL_PHY_OPT, SL_SO_PHY_TX_INHIBIT_THRESHOLD, &thrshld, sizeof(thrshld));
			if (retVal < 0)
			{
				return retVal;
			}
		}
    }

    /* Individual testing cases */
    switch (eTxMode)
    {
        case RADIO_TX_PACKETIZED:
        {
            length = eSize;
            txStarted = 1;

			/* Enable/Disable ACKs */
			retVal = sl_SetSockOpt(rxSocket, SL_SOL_PHY_OPT,  SL_SO_PHY_ALLOW_ACKS ,&enableACKs, sizeof(enableACKs));
			if (retVal < 0)
			{
				return retVal;
			}

            while ((txStarted) && (length == eSize))
            {
                minDelay = (eDelay % 50);

                /* transmit the frame */
                length = sl_Send(rxSocket, DataFrame, eSize, SL_WLAN_RAW_RF_TX_PARAMS(eChannel, eRate, ePowerLevel_Tone, ePreamble));

                /* sleep for sub 50mSec duration*/
                Sleep(minDelay);
                minDelay = (eDelay - minDelay);
                while ((minDelay > 0) && (txStarted))
                {
                    Sleep(50);
                    minDelay -= 50;
                }

				/* If specifying amount of packets to transfer, count */
				if (eAmount > 0)
				{
					counter++;
					if (counter >= eAmount)
						break;
				}
            }

            if (length != eSize)
            {
                RadioStopTX(eTxMode);
                return RADIO_TOOL_ERROR_TX_FULL_SIZE_DATA;
            }

            retVal = sl_Close(rxSocket);
            break;
        }

        case RADIO_TX_CONTINUOUS:
        {
            sl_SetSockOpt(rxSocket, SL_SOL_PHY_OPT, SL_SO_PHY_NUM_FRAMES_TO_TX, &amount, sizeof(_u32)); 
            sl_Send(rxSocket, DataFrame, eSize, SL_WLAN_RAW_RF_TX_PARAMS(eChannel, eRate, ePowerLevel_Tone, ePreamble));   
            break;
        }

        case RADIO_TX_CW:
        {
            rxSocket = sl_Socket(SL_AF_RF, SL_SOCK_RAW, eChannel);
            sl_Send(rxSocket, NULL, 0, ePowerLevel_Tone);
            break;
        }

        default:
        {
            retVal = RADIO_TOOL_ERROR_TX_TYPE_UNKNOWN;
        }
    }

    return retVal;
}

/* */
RTAPI RadioStopRX ()
{
    _i32 retVal;

    sl_WlanRxStatStop();
	//Sleep(100);
    retVal = sl_Close(rxSocket);

    return retVal;
}

/* */
RTAPI RadioStartRX(_u8 eChannel)
{
    _i32 retVal = 0;
    struct SlTimeval_t timeval;

    timeval.tv_sec = RX_RECV_TIMEOUT_SEC;
    timeval.tv_usec = RX_RECV_TIMEOUT_USEC;

    retVal = sl_WlanRxStatStart();
    if (retVal < 0)
        return retVal;

    rxSocket = sl_Socket(SL_AF_RF, SL_SOCK_RAW, eChannel);
    if (rxSocket < 0)
        return RADIO_TOOL_ERROR_RX_CREATING_RAW_SOCKET;

    retVal = sl_SetSockOpt(rxSocket, SL_SOL_SOCKET, SL_SO_RCVTIMEO, &timeval, sizeof(timeval));    // Enable receive timeout
    if (retVal < 0)
        return retVal;

    // Return value of -11 is expected
    sl_Recv(rxSocket, DataFrame, RX_BUFFER_SIZE, 0);

    return 0;
}

/* */
RTAPI RadioGetStats(_u8 *pRxStats)
{
    _i32 retVal = 0;

    retVal = sl_WlanRxStatGet((SlWlanGetRxStatResponse_t *)pRxStats, 0);
    if (retVal < 0)
        return RADIO_TOOL_ERROR_GETTING_RX_STATS;

    return 0;
}

/* */
RTAPI RadioGetMacAddr(_u8 *pMacAddress)
{
    _i32    retVal = 0;
    _u16    pConfigLen = SL_MAC_ADDR_LEN;

    retVal = sl_NetCfgGet(SL_NETCFG_MAC_ADDRESS_GET, NULL, &pConfigLen, pMacAddress);
    if (retVal < 0)
        return RADIO_TOOL_ERROR_GETTING_MAC_ADDR;

    return 0;
}

/* */
RTAPI RadioGetDeviceVersion(_u8 *pDevVersion, _u16 *pHDLength, _u8 *pHDVersion)
{
    _i32    retVal = 0;
    _u8     pConfigOpt;
    _u16    pConfigLen;
	_u16    maxHostDriverVerTextLength = 16;
	*pHDLength = strlen(SL_DRIVER_VERSION);
    SlDeviceVersion_t deviceVersion;

    pConfigOpt = SL_DEVICE_GENERAL_VERSION;
    pConfigLen = sizeof(SlDeviceVersion_t);

	/* Acquire device version */
    retVal = sl_DeviceGet(SL_DEVICE_GENERAL, &pConfigOpt, &pConfigLen, (_u8 *)(&deviceVersion));
    if (retVal < 0)
        return RADIO_TOOL_ERROR_GETTING_DEV_VERSION;

    memcpy_s(pDevVersion, pConfigLen, &deviceVersion, pConfigLen);
	memcpy_s(pHDVersion, maxHostDriverVerTextLength, SL_DRIVER_VERSION, strlen(SL_DRIVER_VERSION));

    return 0;
}

/*!
 *  \brief      This function handles general events
 *  \param[in]  pDevEvent - Pointer to stucture containing general event info
 *  \return     None
 */
void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *pDevEvent)
{
    /* Unused in this application */
}

/*
 *  \brief      This function handles WLAN async events
 *  \param[in]  pWlanEvent - Pointer to the structure containg WLAN event info
 *  \return     None
 */
void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent)
{
    /* Unused in this application */
}

/*!
 *  \brief      This function handles asynchronous socket events.
 *  \param[in]  pSock - Pointer to the structure containing socket event info
 *  \return     None
 */
void SimpleLinkSockEventHandler(SlSockEvent_t *pSock)
{
    /* Unused in this application */
}

/*!
 *  \brief      This function handles network events such as IP acquisition, IP leased, IP released etc.
 *  \param[in]  pNetAppEvent - Pointer to the structure containing acquired IP
 *  \return     None
 */
void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pNetAppEvent)
{
    /* Unused in this application */
}

/*!
 *  \brief      This function handles resource request
 *  \param[in]  pNetAppRequest - Contains the resource requests
 *  \param[in]  pNetAppResponse - Should be filled by the user with the relevant response information
 *  \return     None
 */
void SimpleLinkNetAppRequestHandler(SlNetAppRequest_t  *pNetAppRequest, SlNetAppResponse_t *pNetAppResponse)
{
    /* Unused in this application */
}

/*!
 *  \brief      This function gets triggered when HTTP Server receives application defined GET and POST HTTP tokens.
 *  \param[in]  pHttpServerEvent - Pointer indicating http server event
 *  \param[in]  pHttpServerResponse - Pointer indicating http server response
 *  \return     None
 */
void SimpleLinkHttpServerCallback(SlNetAppHttpServerEvent_t *pHttpEvent, SlNetAppHttpServerResponse_t *pHttpResponse)
{
    /* Unused in this application */
}

/*!
 *  \brief      This function handles fatal error reports
 *  \param[in]  pFatalErrorEvent - Contains the fatal error data
 *  \return     None
 */
void SimpleLinkFatalErrorEvtHdlr(SlDeviceFatal_t *pFatalErrorEvent)
{
    /* Unused in this application */
}