/*
 *  ======== Server.c ========
 *
 */

/* this define must precede inclusion of any xdc header file */
#define Registry_CURDESC Test__Desc
#define MODULE_NAME "Server"

/* xdctools header files */
#include <xdc/std.h>
#include <xdc/runtime/Assert.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Registry.h>

//C include
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>

/* package header files */
#include <ti/ipc/MessageQ.h>
#include <ti/ipc/MultiProc.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>

/* local header files */
#include "../shared/AppCommon.h"

/* module header file */
#include "Server.h"

/* GPIO header file */
#include <ti/drv/gpio/GPIO.h>
#include <ti/drv/gpio/soc/GPIO_v1.h>

#include <ti/csl/src/ip/gpio/V1/gpio_v2.h>
#include <ti/drv/gpio/src/GPIO_osal.h>

#define NUM_PORTS           (8u)
#define NUM_PINS_PER_PORT    (32u)
#define CALLBACK_INDEX_NOT_CONFIGURED (0xFFu)

/* Macro to extract GPIO pin number from the gpio pin configuration */
#define GPIO_GET_PIN_NUM(gpioConfig)  (((gpioConfig) & GPIO_CFG_PIN_NUM_MASK) >> \
		GPIO_CFG_PIN_NUM_SHIFT)

/* Macro to extract GPIO port number from the gpio pin configuration */
#define GPIO_GET_PORT_NUM(gpioConfig) (((gpioConfig) & GPIO_CFG_PORT_NUM_MASK) \
		>> GPIO_CFG_PORT_NUM_SHIFT)

/** \brief Mask for all pins of single gpio port */
#define GPIO_PIN_MASK_ALL           (0xFFFFFFFFU)


typedef struct GPIO_PortCallbackInfo_s {
	/* The port's 8 corresponding user defined pinId indices */
	uint8_t pinIndex[NUM_PINS_PER_PORT];
} GPIO_PortCallbackInfo;

/* Table of callbacks per port. */
GPIO_PortCallbackInfo gpioCallbackInfo[NUM_PORTS];

uint32_t portHwiCreatedBitMask = 0;

volatile bool initCalled = false;

void GPIO_setConfig_v1(uint32_t index, GPIO_PinConfig pinConfig);
void GPIO_v1_hwiFxn(uintptr_t portIdx);
inline uint32_t getPinNumber(uint32_t x);
void GPIO_init_v1(void);
void GPIO_write_v1(uint32_t index, uint32_t value);
uint8_t getGpioIntIndex(GPIO_PinConfig pinConfig);

/* module structure */
typedef struct {
    UInt16              hostProcId;         // host processor id
    MessageQ_Handle     slaveQue;           // created locally
} Server_Module;

App_Msg *           msg;

/* private data */
Registry_Desc               Registry_CURDESC;
static Server_Module        Module;

char *liatest = "0000";
/*
 *  ======== Server_init ========
 */
Void Server_init(Void)
{

    Registry_Result result;

    /* register with xdc.runtime to get a diags mask */
    result = Registry_addModule(&Registry_CURDESC, MODULE_NAME);
    Assert_isTrue(result == Registry_SUCCESS, (Assert_Id)NULL);

    /* initialize module object state */
    Module.hostProcId = MultiProc_getId("HOST");

	GPIO_init_v1();

}
/*
 *  ======== Server_create ========
 */
Int Server_create()
{
    Int                 status = 0;
    MessageQ_Params     msgqParams;
    char                msgqName[32];

    /* enable some log events */
    Diags_setMask(MODULE_NAME"+EXF");

    /* create local message queue (inbound messages) */
    MessageQ_Params_init(&msgqParams);
    sprintf(msgqName, App_SlaveMsgQueName, MultiProc_getName(MultiProc_self()));
    Module.slaveQue = MessageQ_create(msgqName, &msgqParams);

    if (Module.slaveQue == NULL) {
        status = -1;
        goto leave;
    }

    Log_print0(Diags_INFO,"Server_create: server is ready");

leave:
    Log_print1(Diags_EXIT, "<-- Server_create: %d", (IArg)status);
    return (status);
}
/*
 *  ======== Server_exec ========
 */
Int Server_exec()
{
    Int                 status;
    Bool                running = TRUE;
    MessageQ_QueueId    queId;
	int 				flg = 0;

    Log_print0(Diags_ENTRY | Diags_INFO, "--> Server_exec:");

    while (running) {

        /* wait for inbound message */
        status = MessageQ_get(Module.slaveQue, (MessageQ_Msg *)&msg,
            MessageQ_FOREVER);

        if (status < 0) {
            goto leave;
        }

#if 1
		if(strstr(msg->lia,"host") == NULL)
			flg = 1;
		else
			flg = 2;

#endif

        if (msg->cmd == App_CMD_SHUTDOWN) {
            running = FALSE;
        }

        /* process the message */
        Log_print1(Diags_INFO, "Server_exec: processed cmd=0x%x", msg->cmd);

        /* send message back */
        queId = MessageQ_getReplyQueue(msg); /* type-cast not needed */

		if(flg == 1 ){
			GPIO_write_v1((AM572X_LED0),GPIO_PIN_VAL_HIGH);
        MessageQ_put(queId, (MessageQ_Msg)msg);
		}
		if(flg == 2){
			GPIO_write_v1((AM572X_LED0),GPIO_PIN_VAL_LOW);
        MessageQ_put(queId, (MessageQ_Msg)msg);
		}

		Task_sleep(1000);
    } /* while (running) */

leave:
    Log_print1(Diags_EXIT, "<-- Server_exec: %d", (IArg)status);
    return(status);
}

/*
 *  ======== Server_delete ========
 */

Int Server_delete()
{
    Int         status;

    Log_print0(Diags_ENTRY, "--> Server_delete:");

    /* delete the video message queue */
    status = MessageQ_delete(&Module.slaveQue);

    if (status < 0) {
        goto leave;
    }

leave:
    if (status < 0) {
        Log_error1("Server_finish: error=0x%x", (IArg)status);
    }

    /* disable log events */
    Log_print1(Diags_EXIT, "<-- Server_delete: %d", (IArg)status);
    Diags_setMask(MODULE_NAME"-EXF");

    return(status);
}

/*
 *  ======== Server_exit ========
 */
Void Server_exit(Void)
{
    /*
     * Note that there isn't a Registry_removeModule() yet:
     *     https://bugs.eclipse.org/bugs/show_bug.cgi?id=315448
     *
     * ... but this is where we'd call it.
     */
}

void GPIO_init_v1(void)
{
	uint32_t i, j;
	uint32_t portCfgDone[NUM_PORTS] = {0U,0U,0U,0U,0U,0U,0U,0U};
	uint32_t pinConfig;
	GPIO_v1_HwAttrs *hwAttrs = NULL;
	uint32_t portNum;

	portHwiCreatedBitMask = 0;

	/* Initialize all entries with 'not configured' key */
	for (i = 0; i < NUM_PORTS; i++) {
		for (j = 0; j < NUM_PINS_PER_PORT; j++) {
			gpioCallbackInfo[i].pinIndex[j] = CALLBACK_INDEX_NOT_CONFIGURED;
		}
	}

	    /* Perform GPIO Reset and Module Enable only once for port */
	    for(i = 0; i < GPIO_v1_config.numberOfPinConfigs; i++)
		{   
			pinConfig = GPIO_v1_config.pinConfigs[i];
			if ((pinConfig & GPIO_DO_NOT_CONFIG) !=
					GPIO_DO_NOT_CONFIG)
			{
				portNum   = GPIO_GET_PORT_NUM(pinConfig);
				if ((portNum > 0U) && (portNum <= NUM_PORTS))
				{
					hwAttrs = (GPIO_v1_HwAttrs *)&GPIO_v1_hwAttrs[(portNum - 1U)];
					if(portCfgDone[portNum - 1] == 0U)
					{
						portCfgDone[portNum - 1] = 1U;

						/* Enable the GPIO module. */
						GPIOModuleEnable(hwAttrs->baseAddr);
					}
				}
			}
		}

		/* Configure pins and create Hwis per static array content */
		for (i = 0; i < GPIO_v1_config.numberOfPinConfigs; i++) {
			if ((GPIO_v1_config.pinConfigs[i] & GPIO_DO_NOT_CONFIG) !=
					GPIO_DO_NOT_CONFIG) {
				GPIO_setConfig_v1(i, GPIO_v1_config.pinConfigs[i]);
			}
		}

		initCalled = (bool)true;
}

void GPIO_write_v1(uint32_t index, uint32_t value)
{
	uint32_t key;
	GPIO_v1_HwAttrs *hwAttrs = NULL;
	uint32_t pinConfig;
	uint32_t pinNum;
	uint32_t portNum;

	/* Input parameter validation */
	GPIO_osalAssert(!((((bool)true) == initCalled) &&
				(index < GPIO_v1_config.numberOfPinConfigs)));

	pinConfig = GPIO_v1_config.pinConfigs[index];
	portNum   = GPIO_GET_PORT_NUM(pinConfig);
	pinNum    = GPIO_GET_PIN_NUM(pinConfig);

	if (((portNum > 0U) && (portNum <= NUM_PORTS)) &&
			(pinNum < NUM_PINS_PER_PORT))
	{
		hwAttrs   = (GPIO_v1_HwAttrs *)&GPIO_v1_hwAttrs[(portNum - 1U)];

		key = GPIO_osalHardwareIntDisable();

		/* Clear output from pinConfig */
		GPIO_v1_config.pinConfigs[index] &= ~GPIO_CFG_OUT_HIGH;

		if (value) {
			/* Set the pinConfig output bit to high */
			GPIO_v1_config.pinConfigs[index] |= GPIO_CFG_OUT_HIGH;

		}

		GPIOPinWrite(hwAttrs->baseAddr, pinNum, value);

		GPIO_osalHardwareIntRestore(key);
	}
	sprintf(msg->lia, " *** led%d %x %x 0x%x ",index,portNum,pinNum,hwAttrs->baseAddr);
}

void GPIO_setConfig_v1(uint32_t index, GPIO_PinConfig pinConfig)
{
	uint32_t   key;
	void *         hwiHandle;
	uint32_t       gpioBase;
	uint32_t       evtType;
	uint32_t       intrEvtType;
	/* there exist a variable with same name "gpioDirection". changed to solve the misra warning "MISRA.CT.UNIQUE.ID"*/
	uint8_t        gpio_Direction;
	uint8_t        gpioPin;
	uint8_t        gpioPortIntIdx;
	uint32_t       gpioPortIntBitMask;
	GPIO_PinConfig gpioPinConfig;
	OsalRegisterIntrParams_t interruptRegParams;
	uint32_t intrLineNum = GPIO_INT_LINE_1;
	uint32_t pinConfigVal;
	uint32_t portNum;
	uint32_t pinNum;
	GPIO_v1_HwAttrs *hwAttrs = NULL;
	uint8_t ret_flag = 0U;

	GPIO_osalAssert(!(index < GPIO_v1_config.numberOfPinConfigs));

	pinConfigVal = GPIO_v1_config.pinConfigs[index];
	portNum      = GPIO_GET_PORT_NUM(pinConfigVal);
	pinNum       = GPIO_GET_PIN_NUM(pinConfigVal);

	if (((portNum > 0U) && (portNum <= NUM_PORTS)) &&
			(pinNum < NUM_PINS_PER_PORT))
	{
		hwAttrs  = (GPIO_v1_HwAttrs *)&GPIO_v1_hwAttrs[(portNum - 1U)];

		gpioBase = hwAttrs->baseAddr;
		gpioPin  = (uint8_t)pinNum;

		if ((pinConfig & GPIO_CFG_IN_INT_ONLY) == 0) {
			/* Get GPIO configuration settings */

			/* Determine settings for GPIO as input or output */
			if (pinConfig & GPIO_CFG_INPUT) {
				gpio_Direction = (uint8_t)GPIO_DIR_INPUT;
			}
			else {
				gpio_Direction = (uint8_t)GPIO_DIR_OUTPUT;
			}

			/* Make atomic update */
			key = GPIO_osalHardwareIntDisable();

			/* Set output value */
			if (gpio_Direction == GPIO_DIR_OUTPUT) {
				GPIOPinWrite(gpioBase, (uint32_t)gpioPin,
						((pinConfig & GPIO_CFG_OUT_HIGH) ? 1U : 0U));
			}

			/* Configure the GPIO pin */
			GPIODirModeSet(gpioBase, (uint32_t)gpioPin, (uint32_t)gpio_Direction);

			/* Update pinConfig with the latest GPIO configuration */
			gpioPinConfig = GPIO_v1_config.pinConfigs[index];
			gpioPinConfig &= ~GPIO_CFG_IO_MASK;
			gpioPinConfig |= (pinConfig & GPIO_CFG_IO_MASK);
			GPIO_v1_config.pinConfigs[index] = gpioPinConfig;

			GPIO_osalHardwareIntRestore(key);
		}

		/* Set type of interrupt and then clear it */
		if (pinConfig & GPIO_CFG_INT_MASK) {
			/* Calculate the gpioInterruptVectors index for the GPIO pin */
			gpioPortIntIdx = getGpioIntIndex(pinConfigVal);
			gpioPortIntBitMask = (((uint32_t)1U) << (gpioPortIntIdx - 1u));

			/* If Hwi has not already been created, do so */
			if ((portHwiCreatedBitMask & gpioPortIntBitMask) == 0) {

				/* Initialize with defaults */
				Osal_RegisterInterrupt_initParams(&interruptRegParams);

				interruptRegParams.corepacConfig.isrRoutine  = (&GPIO_v1_hwiFxn);
				interruptRegParams.corepacConfig.arg         = (uintptr_t)(portNum);

				interruptRegParams.corepacConfig.priority = GPIO_v1_config.intPriority;
				interruptRegParams.corepacConfig.name=NULL;
				interruptRegParams.corepacConfig.corepacEventNum=hwAttrs->line1EventId; /* Event going in to CPU */
				interruptRegParams.corepacConfig.intVecNum=hwAttrs->line1IntNum; /* Host Interrupt vector */

				/* Register interrupts */
				GPIO_osalRegisterInterrupt(&interruptRegParams,&(hwiHandle));

				if (hwiHandle == NULL) {
					/* error constructing the Hwi for GPIO Port */
					ret_flag = 1U;
				}
			}

			if(ret_flag == 0u)
			{
				key = GPIO_osalHardwareIntDisable();

				/* Mark the Hwi as created */
				portHwiCreatedBitMask |= gpioPortIntBitMask;

				/* Map correct interrupt type event based on the flag */
				evtType = (pinConfig & GPIO_CFG_INT_MASK) >> GPIO_CFG_INT_LSB;

				switch(evtType)
				{
					case 1U:
						intrEvtType = GPIO_INT_TYPE_FALL_EDGE;
						break;
					case 2U:
						intrEvtType = GPIO_INT_TYPE_RISE_EDGE;
						break;
					case 3U:
						intrEvtType = GPIO_INT_TYPE_BOTH_EDGE;
						break;
					case 4U:
						intrEvtType = GPIO_INT_TYPE_LEVEL_LOW;
						break;
					case 5U:
						intrEvtType = GPIO_INT_TYPE_LEVEL_HIGH;
						break;
					default:
						/** If event type is none of the above then it will be forced to
						 * FALLING EDGE interrupt event type.
						 */
						intrEvtType = GPIO_INT_TYPE_FALL_EDGE;
						break;
				}

				GPIOIntTypeSet(hwAttrs->baseAddr, (uint32_t)gpioPin, intrEvtType);
				GPIOPinIntClear(hwAttrs->baseAddr, intrLineNum, (uint32_t)gpioPin);

				/* Update pinConfig with the latest interrupt configuration */
				gpioPinConfig = GPIO_v1_config.pinConfigs[index];
				gpioPinConfig &= ~(GPIO_CFG_INT_MASK);
				gpioPinConfig |= (pinConfig & GPIO_CFG_INT_MASK);
				GPIO_v1_config.pinConfigs[index] = gpioPinConfig;

				GPIO_osalHardwareIntRestore(key);
			}
		}
	}
	return;
}

void GPIO_v1_hwiFxn(uintptr_t portIdx)
{
	uint32_t          gpioPins;
	uint32_t          gpioBase;
	uint32_t          gpioIndex;
	uint32_t          bitNum;
	uint32_t portNo = (uint32_t)portIdx;
	GPIO_PortCallbackInfo *portCallbackInfo;
	GPIO_v1_HwAttrs *hwAttrs = (GPIO_v1_HwAttrs *)&GPIO_v1_hwAttrs[portNo - 1U];    
	uint32_t intrLineNum = GPIO_INT_LINE_1;
	portCallbackInfo = &gpioCallbackInfo[portNo -1U];
	gpioBase = hwAttrs->baseAddr;

	/* Find out which pins have their interrupt flags set */
	gpioPins = GPIORawIntStatus(gpioBase, intrLineNum, GPIO_PIN_MASK_ALL);

	/* Clear all the set bits at once */
	GPIOIntrClearMask(gpioBase, intrLineNum, gpioPins);

	/* Match each set bit to its corresponding callback function */
	while (gpioPins) {
		/* Gets the lowest order set bit number */
		bitNum = getPinNumber(gpioPins);
		gpioIndex = portCallbackInfo->pinIndex[bitNum];
		if (gpioIndex != CALLBACK_INDEX_NOT_CONFIGURED) {
			GPIO_v1_config.callbacks[gpioIndex]();
		}
		gpioPins &= ~(((uint32_t)1U) << bitNum);
	}
}

uint32_t getPinNumber(uint32_t x) {
	uint32_t idx = 0;

	while((x & 0x1U) == 0)
	{
		idx++;
		x = x>>1;
	}

	return idx;
}

uint8_t getGpioIntIndex(GPIO_PinConfig pinConfig) {

	return ((uint8_t)GPIO_GET_PORT_NUM(pinConfig));
}
