/**
 * cpsw_nimu_eth.c
 * 
 *
 * Copyright (c) 2012 Texas Instruments Incorporated ALL RIGHTS RESERVED
 *
*/

#include "cpsw_nimu_eth.h"
#include "cpsw_ethdriver.h"
#include "cpsw_impl.h"


#ifdef UIA_DEBUG
#include <xdc/runtime/Log.h>
#include <ti/uia/events/UIABenchmark.h>
#endif

/* The EMAC Initialization Function. */
int CpswEmacInit (STKEVENT_Handle hEvent);

/* Used to check status of NDK shutdown */
int shutdown_complete;

/* Defines the num of ports, set using boardType */
extern UInt8 cpsw_num_ports;
extern UInt8 cpsw_num_mac_ports;
extern MAC_CONFIG mac_configuration;

/**
 * @brief
 *   CPSW_EMAC_DATA
 *
 * @details
 *  The structure is used to store the private data for the
 *  EMAC controller.
 */
typedef struct CPSW_EMAC_DATA
{
/**
  * @brief   Private Information
  */
	CPSW_PDINFO      pdi;
}CPSW_EMAC_DATA;


/**
 *  @b CpswEmacStart
 *  @n
 *  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
 *  @retval
 *      Error   -   <0
 */
static int CpswEmacStart (NETIF_DEVICE* ptr_net_device)
{

    CPSW_EMAC_DATA*  ptr_pvt_data;

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

    /* Call low-level open function */
    if (CpswHwPktOpen(&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;

        /* Inform the world that we are operational. */
        info_printf ("EMAC has been started successfully\n");
        return 0;
    }

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


/**
 *  @b CpswEmacStop
 *  @n
 *  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
 *  @retval
 *      Error   -   <0
 */
static int CpswEmacStop (NETIF_DEVICE* ptr_net_device)
{
	CPSW_EMAC_DATA*  ptr_pvt_data;

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

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

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

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

    shutdown_complete = 1;

    /* EMAC Controller has been stopped. */
    return 0;
}


/**
 *  @b CpswEmacPoll
 *  @n
 *  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
 *      void
 */
static void CpswEmacPoll (NETIF_DEVICE* ptr_net_device, uint timer_tick)
{
	CPSW_EMAC_DATA*  ptr_pvt_data;

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

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

    return;
}


/**
 *  @b CpswEmacSend
 *  @n
 *  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
 *  @retval
 *      Error   -   <0
 */
static int CpswEmacSend (NETIF_DEVICE* ptr_net_device, PBM_Handle hPkt)
{
#ifdef UIA_DEBUG
	Log_write1(UIABenchmark_start, (xdc_IArg)"NDK_send");
#endif
	CPSW_EMAC_DATA*  ptr_pvt_data;

    /* Get the pointer to the private data */
    ptr_pvt_data = (CPSW_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 );	/* era 64 */

    /* 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 )
    	CpswHwPktTxNext(&ptr_pvt_data->pdi);
#ifdef UIA_DEBUG
    Log_write1(UIABenchmark_stop, (xdc_IArg)"NDK_send");
#endif
    /* Packet has been successfully transmitted. */
    return 0;
}


/**
 *  @b CpswEmacPktService
 *  @n
 *  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
 *      void
 */
static void CpswEmacPktService (NETIF_DEVICE* ptr_net_device)
{
	CPSW_EMAC_DATA*  ptr_pvt_data;
    PBM_Handle          hPacket;
	
    /* Get the pointer to the private data */
    ptr_pvt_data = (CPSW_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);
#ifdef DEBUG_STATS
        cpsw_raweth_stat.nimuRxCnt++;
#endif
    }

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


/**
 *  @b CpswEmacioctl
 *  @n
 *  The function is called by the NDK core stack to configure 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
 *  @retval
 *      Error   -   <0
 */
static int Cpswioctl (NETIF_DEVICE* ptr_net_device, uint cmd, void* pBuf, uint size)
{
	CPSW_EMAC_DATA*  ptr_pvt_data;

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

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

}


/**
 *  @b CpswEmacInit
 *  @n
 *  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
 *  @retval
 *      Error   -   <0
 */
int CpswEmacInit (STKEVENT_Handle hEvent)
{
	if(1 == mac_configuration.resvd) {
		cpsw_num_mac_ports = mac_configuration.numberPorts;
    	cpsw_num_ports = mac_configuration.numberPorts + 1;
	}
    else {
    	mac_configuration.phyAddr[0] = CPSW3G_DEFAULT_PORT_NO;
    	mac_configuration.numberPorts = CPSW3G_DEFAULT_NUM_MAC_PORTS;
    	mac_configuration.macModeFlags = CPSW3G_DEFUAULT_MAC_MODE;
    	cpsw_num_mac_ports = mac_configuration.numberPorts;
    	cpsw_num_ports = mac_configuration.numberPorts + 1;
    }

	CPSW_EMAC_DATA*  	ptr_pvt_data;
	NETIF_DEVICE*   ptr_device;

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

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

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

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

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

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

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

    /* Default MAC Address (can be overwritten by CpswHwPktOpen()) */
    ptr_pvt_data->pdi.bMacAddr[0] = 0x08;
    ptr_pvt_data->pdi.bMacAddr[1] = 0x00;
    ptr_pvt_data->pdi.bMacAddr[2] = 0x28;
    ptr_pvt_data->pdi.bMacAddr[3] = 0xFF;
    ptr_pvt_data->pdi.bMacAddr[4] = 0xFF;
    ptr_pvt_data->pdi.bMacAddr[5] = 0x20;

    /* 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 ("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, "eth1");
    ptr_device->mtu         = ETH_MAX_PAYLOAD - ETHHDR_SIZE;
    ptr_device->pvt_data    = (void *)ptr_pvt_data;

    /* Populate the Driver Interface Functions. */
    ptr_device->start       = CpswEmacStart;
    ptr_device->stop        = CpswEmacStop;
    ptr_device->poll        = CpswEmacPoll;
    ptr_device->send        = CpswEmacSend;
    ptr_device->pkt_service = CpswEmacPktService;
    ptr_device->ioctl       = Cpswioctl;
    ptr_device->add_header  = NIMUAddEthernetHeader;

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

    info_printf ("Registeration of the EMAC Successful\n");
    return 0;

}
