#include <linux/init.h>
#include <linux/inet.h>
#include <linux/skbuff.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/ppp_channel.h>
//#include <linux/if_ppp.h>
#include <linux/atmppp.h>
#include <linux/ppp_defs.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
#include <linux/capability.h>

#include "pktGen.h"
#include "../xdsl_types.h"
#include "../xdsl_ctrl.h"

/* stream info */
#define ETHER_ADDR_LEN	6
#define PKT_TYPE_RRXMPDU         cpu_to_be16(ETH_P_SLOW)

#ifdef CONFIG_RTL_ETH_PRIV_SKB_ADV
#define MBUF_LEN	1536	
#else
#define MBUF_LEN	1700
#endif

#define TRANSFER_UNFINISH	0
#define TRANSFER_FINISHED	1
#define TRANSFER_FAILED		2

extern void xdsl_ctrl_receive( void *recv_pkt, unsigned long size);

#if defined(CONFIG_XDSL_CTRL_ON_SOC)
extern uint8   dslcmd_rxbuf[ DSL_MAX_BUF_SIZE ];

int xdsl_2cpu_recv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev);


char dmac[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
static struct packet_type xdsl_packet_type __read_mostly = {
	.type =	PKT_TYPE_RRXMPDU,
	.func =	xdsl_2cpu_recv,
};
#else

int xdsl_2dsp_event_recv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev);

static DEFINE_RWLOCK(devs_lock);
static char smac[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
static char dmac[6] = {0x00, 0xff, 0xee, 0xdd, 0xcc, 0xbb};

static struct packet_type xdsl_packet_type __read_mostly = {
	.type =	PKT_TYPE_RRXMPDU,
	.func =	xdsl_2dsp_event_recv,
};

#endif

static int cmd_get_status;
static struct net_device * eth_dev;

int xdsl_ctrl_phy_setup(void) 
{	
	eth_dev = dev_get_by_name(&init_net, ETH_WANIF);
	
	if (!eth_dev) {
		printk("Can't find Xdsl Eth Device");
		return -EINVAL;
	} 
		
	xdsl_packet_type.dev = eth_dev;
	dev_add_pack(&xdsl_packet_type);
	
	return 0;	
}

ctrl_pkt_t *get_ctrl_pkt_by_phy(void *data)
{
	struct sk_buff *skb;
	char *buf;
	ctrl_pkt_t *ctrlp;

	skb = (struct sk_buff*)data;
	buf = (char*)(skb->data);
	ctrlp = (ctrl_pkt_t*)(buf);
	return ctrlp;
}

int xdsl_ctrl_phy_free(void *mp)
{
	struct sk_buff *skb;
	skb = (struct sk_buff*) mp;
	kfree_skb(skb);

	return 0;
}

/* Test Downstream Mulitcast , port 0 to port 1*/
struct sk_buff* alloc_ctrl_packet(ctrl_pkt_t *ctrlp)
{
	struct sk_buff *send_skb;
	PktConf_t conf;
	int pktLen; 
    // add the length ofmac address and protocol id 
	unsigned short skb_len = 14 + sizeof(ctrl_pkt_t);

	send_skb = dev_alloc_skb(skb_len);
	if (!send_skb) {
		return NULL;
	}
	skb_put(send_skb, skb_len);
	memset(send_skb->data, 0 ,skb_len);
	/* setup environment */
	
	/* Generate and input a UDP-multicast packet*/
	memset(&conf, 0, sizeof(PktConf_t));  
	conf.payload.length 			= sizeof(ctrl_pkt_t);
	conf.payload.content			= (int8*)(ctrlp);
	conf.conf_ethtype 				= PKT_TYPE_RRXMPDU;
	memcpy(conf.conf_smac, eth_dev->dev_addr, ETHER_ADDR_LEN);
	memcpy(conf.conf_dmac, dmac, ETHER_ADDR_LEN);

	pktLen = pktGen(&conf, send_skb->data);		/* generate pkt in buff */
	return send_skb;
}

int xdsl_ctrl_phy_set(ctrl_pkt_t *ctrlp, unsigned char sleep)
{
	struct sk_buff *new_skb;
	new_skb = alloc_ctrl_packet(ctrlp);

	if (new_skb && (eth_dev->flags && IFF_UP)){
		new_skb->dev = eth_dev;

		#if defined(CONFIG_COMPAT_NET_DEV_OPS)
		eth_dev->hard_start_xmit(new_skb, eth_dev);
		#else
		eth_dev->netdev_ops->ndo_start_xmit(new_skb,eth_dev);
		#endif

	} else {
		printk("net device : %s not open\n", eth_dev->name);
		dev_kfree_skb_any(new_skb);
	}
	return 0;
}

#if defined(CONFIG_XDSL_CTRL_ON_SOC)
spinlock_t data_lock, rx_lock;
int data_seq_num = 0;

int xdsl_ctrl_phy_get(ctrl_pkt_t *ctrlp, unsigned char sleep)
{
	struct sk_buff *new_skb;
	int retry_cnt = 60000;

	new_skb = alloc_ctrl_packet(ctrlp);

	if (new_skb && (eth_dev->flags & IFF_UP)) {
		new_skb->dev = eth_dev;

		#if defined(CONFIG_COMPAT_NET_DEV_OPS)
		eth_dev->hard_start_xmit(new_skb, eth_dev);
		#else
		eth_dev->netdev_ops->ndo_start_xmit(new_skb, eth_dev);
		#endif

		cmd_get_status = TRANSFER_UNFINISH;

		while (cmd_get_status == TRANSFER_UNFINISH){
			schedule_timeout_interruptible(10);
			retry_cnt--;
			if (retry_cnt<=0){
				break;
			}
		}

		if (retry_cnt<=0){
			printk("xdsl_ctrl_phy_get cmd fail\n");
			return -1;
		} 
	} else {
		printk("netdevice: %s not open\n", eth_dev->name);
		dev_kfree_skb_any(new_skb);
		return -1;
	}

	if (cmd_get_status == TRANSFER_FINISHED)
		return 0;
	else
		return -1;
}

int xdsl_2cpu_event_recv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev) 
{
	char *buf;
	ctrl_pkt_t *ctrlp;
	struct net_device* ndev;

	printk("xdsl_2cpu_event_recv\n");
	
	if (!skb)
		return -1;

	buf = (char*)(skb->data);
	ctrlp = (ctrl_pkt_t*)(buf);

	switch (ctrlp->command)
	{
		case 2:
		{
			printk("VDSL LINK UP\n");
			ndev = dev_get_by_name(&init_net, "ptm0");
			if (!ndev)
				return -1;

			netif_carrier_on(ndev);
			break;
		}
		case 3:
		{
			printk("VDSL LINK DOWN\n");
			ndev = dev_get_by_name(&init_net, "ptm0");
			if (!ndev)
				return -1;

			netif_carrier_off(ndev);
			break;

		}	
		default:
			return -1;
	}

	dev_put(ndev);

	xdsl_ctrl_phy_free(skb);

	return 0;
}

int xdsl_2cpu_data_recv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev) 
{
	char *buf;
	char *mac;
	ctrl_pkt_t *ctrlp;

	spin_lock(&data_lock);
	
	if (skb){
		mac = skb_mac_header(skb) + 6;
		memcpy(dmac, mac, ETHER_ADDR_LEN);
		buf = (char*)(skb->data);
		ctrlp = (ctrl_pkt_t*)(buf);

		if (ctrlp->protocol == XDSL_CMD_DATA){
			memset(dslcmd_rxbuf, 0, sizeof(dslcmd_rxbuf));
			memcpy(&dslcmd_rxbuf[data_seq_num*DSL_RXBUF_MAX_SIZE], ctrlp->rxbuf, ctrlp->argsize);
			data_seq_num++;
		}

		if (ctrlp->protocol == XDSL_CMD_DATA_EOF){
			memset(dslcmd_rxbuf, 0, sizeof(dslcmd_rxbuf));
			memcpy(&dslcmd_rxbuf[data_seq_num*DSL_RXBUF_MAX_SIZE], ctrlp->rxbuf, ctrlp->argsize);
			data_seq_num = 0;
			cmd_get_status = TRANSFER_FINISHED;
		}

	}

	spin_unlock(&data_lock);
	xdsl_ctrl_phy_free(skb);
	return 0;
}

int xdsl_2cpu_recv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev) 
{
	int ret;
	char *buf;
	char *mac;
	ctrl_pkt_t *ctrlp;

	if (skb){
		buf = (char*)(skb->data);
		ctrlp = (ctrl_pkt_t*)(buf);
		mac = skb_mac_header(skb) + 6;
		memcpy(dmac, mac, ETHER_ADDR_LEN);
		if ((ctrlp->protocol==XDSL_CMD_DATA) || (ctrlp->protocol==XDSL_CMD_DATA_EOF)){
			ret = xdsl_2cpu_data_recv(skb, dev, pt, orig_dev);
		} else {
			ret = xdsl_2cpu_event_recv(skb, dev, pt, orig_dev);
		}
	}

	return 0;
}
#endif

#if defined(CONFIG_XDSL_CTRL_ON_DSL)
static DEFINE_SPINLOCK(recv_lock);
int xdsl_2dsp_event_recv(struct sk_buff *skb, struct net_device *dev,
		   struct packet_type *pt, struct net_device *orig_dev) 
{
	int i;
	char *mac;
	spin_lock(&recv_lock);

	if (skb){
		mac = skb_mac_header(skb) + 6;
		memcpy(dmac, mac, ETHER_ADDR_LEN);
		xdsl_ctrl_receive( skb, skb->len );
	}

	spin_unlock(&recv_lock);

	return 0;
}

#endif