#include <rtthread.h>
#include "skeleton.h"
#include <netif/ethernetif.h>

#define MAX_ADDR_LEN	6
struct rt_skeleton_eth
{
    /* inherit from ethernet device */
    struct eth_device parent;

    /* interface address info. */
    rt_uint8_t  dev_addr[MAX_ADDR_LEN];		/* hw address	*/
};
static struct rt_skeleton_eth _skeleton_device;

void rt_skeleton_isr(int irqno)
{
	rt_uint32_t status;

	/* read status */

    /* Received the coming packet */
    if (status)
    {
	    /* disable receive interrupt */

        /* a frame has been received */
        eth_device_ready(&(_skeleton_device.parent));
    }

    /* Transmit Interrupt check */
    if (status)
    {
	}
}

static rt_err_t rt_skeleton_init(rt_device_t dev)
{
    return RT_EOK;
}

static rt_err_t rt_skeleton_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t rt_skeleton_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t rt_skeleton_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_size_t rt_skeleton_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
    rt_set_errno(-RT_ENOSYS);
    return 0;
}

static rt_err_t rt_skeleton_control(rt_device_t dev, rt_uint8_t cmd, void *args)
{
	struct rt_skeleton_eth *eth;

	eth = (struct rt_skeleton_eth*)dev;
	RT_ASSERT(eth != RT_NULL);

    switch (cmd)
    {
    case NIOCTL_GADDR:
        /* get mac address */
        if (args) rt_memcpy(args, _skeleton_device.dev_addr, 6);
        else return -RT_ERROR;
        break;

    default :
        break;
    }

    return RT_EOK;
}

rt_err_t rt_skeleton_tx( rt_device_t dev, struct pbuf* p)
{
    {
		/* q traverses through linked list of pbuf's
		 * This list MUST consist of a single packet ONLY */
		struct pbuf *q;
		rt_uint16_t pbuf_index = 0;
		rt_uint8_t word[2], word_index = 0;

		q = p;
		/* Write data into hardware, two bytes at a time
		 * Handling pbuf's with odd number of bytes correctly
		 * No attempt to optimize for speed has been made */
		while (q)
		{
			if (pbuf_index < q->len)
			{
				word[word_index++] = ((u8_t*)q->payload)[pbuf_index++];
				if (word_index == 2)
				{
					/* write data to hardware */

					word_index = 0;
				}
			}
			else
			{
				q = q->next;
				pbuf_index = 0;
			}
		}
		/* One byte could still be unsent */
		if (word_index == 1)
		{
			/* write data to hardware */

		}
    }

    return RT_EOK;
}

struct pbuf *rt_skeleton_rx(rt_device_t dev)
{
    struct pbuf* p;
    rt_uint32_t rxbyte;

    /* init p pointer */
    p = RT_NULL;
    rxbyte = 0;		/* get eth status */
    if (rxbyte)		/* there is data received */
    {
        rt_uint16_t rx_len;
        rt_uint16_t* data;

		/* get recieved packet length */
        rx_len = 0;

        /* allocate buffer */
        p = pbuf_alloc(PBUF_LINK, rx_len, PBUF_RAM);
        if (p != RT_NULL)
        {
            struct pbuf* q;
            rt_int32_t len;

            for (q = p; q != RT_NULL; q= q->next)
            {
                data = (rt_uint16_t*)q->payload;
                len = q->len;

                while (len > 0)
                {
                    *data = 0; /* read data from hardware */ 
                    data ++;
                    len -= 2;
                }
            }
        }
        else
        {
            rt_uint16_t dummy;
			rt_int32_t len;

            /* no pbuf, discard data from EMAC */
            data = &dummy;
			len = rx_len;
            while (len > 0)
            {
                *data = 0; /* read from hardware */
                len -= 2;
            }
        }
    }
    else
    {
        /* restore receive interrupt */
    }

    return p;
}

void rt_hw_skeleton_init()
{
	/* set default MAC address */
    _skeleton_device.dev_addr[0] = 0x00;
    _skeleton_device.dev_addr[1] = 0x60;
    _skeleton_device.dev_addr[2] = 0x6E;
    _skeleton_device.dev_addr[3] = 0x11;
    _skeleton_device.dev_addr[4] = 0x22;
    _skeleton_device.dev_addr[5] = 0x33;

	/* set RT-Thread device interface */
    _skeleton_device.parent.parent.init       = rt_skeleton_init;
    _skeleton_device.parent.parent.open       = rt_skeleton_open;
    _skeleton_device.parent.parent.close      = rt_skeleton_close;
    _skeleton_device.parent.parent.read       = rt_skeleton_read;
    _skeleton_device.parent.parent.write      = rt_skeleton_write;
    _skeleton_device.parent.parent.control    = rt_skeleton_control;
    _skeleton_device.parent.parent.user_data  = RT_NULL;

	/* set ethernet interface */
    _skeleton_device.parent.eth_rx     = rt_skeleton_rx;
    _skeleton_device.parent.eth_tx     = rt_skeleton_tx;

    eth_device_init(&(_skeleton_device.parent), "e0");
}
