/**
* @file icss_nimu_eth.c
*
* @brief Ethernet Packet Driver rewritten using the NIMU Packet Architecture guidelines
*
* \par
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* \par
*/
/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */
#include <stdio.h>
#include "icss_nimu_eth.h"
#include "icss_pruinit.h"

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */
/** @brief variable to hold the status of ndk driver */
static int curr_ndk_stat[NIMU_DEVICE_NUM] = {NDK_STAT_UNKNOWN};

/**
* @internal
* @brief The EMAC Initialization Function
* @details The function is used to initialize and register the EMAC
*  with the Network Interface Management Unit (NIMU)
*
* @param[in]  hEvent Stack Event Handle.
*
* @retval Success -   0
*         Error   -   <0
*/
int EmacInit (STKEVENT_Handle hEvent);

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
/**
 * @brief EMAC_DATA
 *
 * @details
 *  The structure is used to store the private data for the
 *  EMAC controller.
 */
typedef struct EMAC_DATA
{
    PDINFO      pdi; /**< Structure for PDINFO(private info) */
}EMAC_DATA;

/**
* @internal
* @brief  The function is used to initialize and start the EMAC
*  controller and device.
*
* @param[in] ptr_net_device NETIF_DEVICE structure pointer.
*
* @retval Success -   0
*         Error   -   <0
*/
static int EmacStart (NETIF_DEVICE* ptr_net_device)
{
    EMAC_DATA*  ptr_pvt_data;

    curr_ndk_stat[0] = NDK_STAT_PROGRESS;

    /* Get the pointer to the private data */
    ptr_pvt_data = (EMAC_DATA *)ptr_net_device->pvt_data;

    /* Call low-level open function */
    if (HwPktOpen(&ptr_pvt_data->pdi) == 0)
    {
        /* Copy the MAC Address into the network interface object here. */
        mmCopy(&ptr_net_device->mac_address[0], &ptr_pvt_data->pdi.bMacAddr[0], 6);

        /* Set the 'initial' Receive Filter */
        ptr_pvt_data->pdi.Filter = ETH_PKTFLT_ALL;//ETH_PKTFLT_MULTICAST

        /* Inform the world that we are operational. */
        info_printf ("Nimu: EMAC started\n");

        curr_ndk_stat[0] = NDK_STAT_UP;

        return 0;
    }

    curr_ndk_stat[0] = NDK_STAT_DOWN;

    /* Error: EMAC failed to start. */
    return -1;
}

/**
* @internal
* @brief  The function is used to de-initialize and stop the EMAC
*  controller and device.
*
* @param[in] ptr_net_device NETIF_DEVICE structure pointer.
*
* @retval Success -   0
*         Error   -   <0
*/
static int EmacStop (NETIF_DEVICE* ptr_net_device)
{
    EMAC_DATA*  ptr_pvt_data;

    /* Get the pointer to the private data */
    ptr_pvt_data = (EMAC_DATA *)ptr_net_device->pvt_data;

    /* Call low-level close function */
    HwPktClose (&ptr_pvt_data->pdi);

    /* Shut down the Ethernet controller. */
    HwPktShutdown();

    /* Flush out our pending queue */
    while( PBMQ_count(&ptr_pvt_data->pdi.PBMQ_rx) )
        PBM_free( PBMQ_deq(&ptr_pvt_data->pdi.PBMQ_rx) );

    curr_ndk_stat[0] = NDK_STAT_DOWN;
    /* EMAC Controller has been stopped. */
    return 0;
}

/**
* @internal
* @brief  The function is used to poll the EMAC controller to check
*  if there has been any activity
*
* @param[in] ptr_net_device NETIF_DEVICE structure pointer
* @param[in]  timer_tick Flag to poll the driver.
*
* @retval none
*/
static void EmacPoll (NETIF_DEVICE* ptr_net_device, uint timer_tick)
{
    EMAC_DATA*  ptr_pvt_data;

    /* Get the pointer to the private data */
    ptr_pvt_data = (EMAC_DATA *)ptr_net_device->pvt_data;

    /* Poll the driver. */
    _HwPktPoll (&ptr_pvt_data->pdi, timer_tick);

    return;
}

/**
* @internal
* @brief  The function is the interface routine invoked by the NDK stack to
*  pass packets to the driver
*
* @param[in] ptr_net_device NETIF_DEVICE structure pointer
* @param[in]  hPkt Packet to be sent out on wire.
*
* @retval Success -   0
*         Error   -   <0
*/
static int EmacSend (NETIF_DEVICE* ptr_net_device, PBM_Handle hPkt)
{
    EMAC_DATA*  ptr_pvt_data;

    /* Get the pointer to the private data */
    ptr_pvt_data = (EMAC_DATA *)ptr_net_device->pvt_data;

    /* Make sure the driver does not transmit packet less than min. as per the
     * Ethernet standards. */
    if( PBM_getValidLen(hPkt) < 60 )
        PBM_setValidLen (hPkt, 60 );		// FW our count does not include CRC

    /* We do not do any packet size checks here. If the packet
     * is too big to fit in the MTU configured on the peripheral,
     * then the driver/CSL layer catches the error.
     */
    /* Enqueue the packet and send it for transmission. */
    PBMQ_enq (&ptr_pvt_data->pdi.PBMQ_tx, hPkt);

    /* Pass the packet to the controller if the transmitter is free. */
    if(ptr_pvt_data->pdi.TxFree )
    {
        HwPktTxNext(&ptr_pvt_data->pdi);
    }

    /* Packet has been successfully transmitted. */
    return 0;
}

/**
* @internal
* @brief  The function is called by the NDK core stack to receive any packets
*  from the driver
*
* @param[in] ptr_net_device NETIF_DEVICE structure pointer
*
* @retval none
*/
static void EmacPktService (NETIF_DEVICE* ptr_net_device)
{
    EMAC_DATA*  ptr_pvt_data;
    PBM_Handle          hPacket;
	
    /* Get the pointer to the private data */
    ptr_pvt_data = (EMAC_DATA *)ptr_net_device->pvt_data;

    /* Give all queued packets to the Ether module */
    while (PBMQ_count(&ptr_pvt_data->pdi.PBMQ_rx))
    {    
        
        /* Dequeue a packet from the driver receive queue. */
        hPacket = PBMQ_deq(&ptr_pvt_data->pdi.PBMQ_rx);

        /* Prepare the packet so that it can be passed up the networking stack.
         * If this 'step' is not done the fields in the packet are not correct
         * and the packet will eventually be dropped.  */
        PBM_setIFRx (hPacket, ptr_net_device);

        /* Pass the packet to the NDK Core stack. */
        NIMUReceivePacket(hPacket);
    }

    /* Work has been completed; the receive queue is empty... */
    return;
}

/**
* @internal
* @brief  The function is called by the NDK core stack to configure the
*   the driver
*
* @param[in] ptr_net_device NETIF_DEVICE structure pointer
* @param[in]  cmd Ioctl command
* @param[in]  pBuf Mac address to be added or deleted
* @param[in]  size Size of Mac Address
*
* @retval Success -   0
*         Error   -   <0
*/
static int Emacioctl (NETIF_DEVICE* ptr_net_device, uint cmd, void* pBuf, uint size)
{
    EMAC_DATA*  ptr_pvt_data;

    /* Get the pointer to the private data */
    ptr_pvt_data = (EMAC_DATA *)ptr_net_device->pvt_data;

    /* call the switch driver's Ioctl handler to take care of this. */
    return HwPktIoctl(&ptr_pvt_data->pdi, cmd, pBuf, size);

}


/**
* @internal
* @brief The EMAC Initialization Function
* @details The function is used to initialize and register the EMAC
*  with the Network Interface Management Unit (NIMU)
*
* @param[in]  hEvent Stack Event Handle.
*
* @retval Success -   0
*         Error   -   <0
*/
int EmacInit (STKEVENT_Handle hEvent)
{
    EMAC_DATA*  	ptr_pvt_data;
	NETIF_DEVICE*   ptr_device;
	int 			hdrSize, trailSize;
	Uint8			j;

    /* Allocate memory for the private data */
    ptr_pvt_data = mmAlloc(sizeof(EMAC_DATA));
    if (ptr_pvt_data == NULL)
    {
    	info_printf ("Nimu Error: Unable to allocate private memory data\n");
        return -1;
    }

    /* Initialize the allocated memory block. */
    mmZeroInit (ptr_pvt_data, sizeof(EMAC_DATA));

    /* Initialize the RX Queue */
    PBMQ_init(&ptr_pvt_data->pdi.PBMQ_rx);

    /* Initialize the packet drivers. */
    HwPktInit();

    /* Initialize the private data */
    mmZeroInit(&ptr_pvt_data->pdi, sizeof(PDINFO));

    /* Set physical index */
    ptr_pvt_data->pdi.PhysIdx = 0;
    ptr_pvt_data->pdi.hEvent  = hEvent;

    /* MCast List is EMPTY */
    ptr_pvt_data->pdi.MCastCnt    = 0;

	for (j=0; j<6; j++)
	{
		ptr_pvt_data->pdi.bMacAddr[j] = ifMAC[j];
	}

    /* Init Logical Device */
    /* ptr_pvt_data->pdi.hEther = hEther; */

    /* Allocate memory for the EMAC. */
	ptr_device = mmAlloc(sizeof(NETIF_DEVICE));
    if (ptr_device == NULL)
    {
    	info_printf ("Nimu Error: Unable to allocate memory for the EMAC\n");
        return -1;
    }

    /* Initialize the allocated memory block. */
    mmZeroInit (ptr_device, sizeof(NETIF_DEVICE));

    /* Populate the Network Interface Object. */
    strcpy (ptr_device->name, "eth0");
    ptr_device->mtu         = ETH_MAX_PAYLOAD - ETHHDR_SIZE;
    ptr_device->pvt_data    = (void *)ptr_pvt_data;

    /* Populate the Driver Interface Functions. */
    ptr_device->start       = EmacStart;
    ptr_device->stop        = EmacStop;
    ptr_device->poll        = EmacPoll;
    ptr_device->send        = EmacSend;
    ptr_device->pkt_service = EmacPktService;
    ptr_device->ioctl       = Emacioctl;
    ptr_device->add_header  = NIMUAddEthernetHeader;

    /* Register the device with NIMU */
    if (NIMURegister (ptr_device) < 0)
    {
    	info_printf ("Nimu Error: Unable to register the EMAC\n");
        return -1;
    }

    NIMUGetRsvdSizeInfo(&hdrSize, &trailSize);	// FW usual result is 20 which is 6 more than normal header offset
    hdrSize = 0;								// set to allow 32 bit aligned buffers
    NIMUSetRsvdSizeInfo(hdrSize, trailSize);

    info_printf ("Nimu: Registration of EMAC successful\n");
    return 0;

}

/**
* @brief The function is used to get the status of ndk driver
*
* @param[in]  instID Stack Driver instance(0 in the implementation)
*
* @retval ndk driver status
*/
int GetNdkStatus(Uint8 instID)
{
	return curr_ndk_stat[instID];
}
