#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <bspgpio.h>

#include "xdsl_ctrl.h"

#if defined(ERB_DEBUG)
//To-Do: create a proc to control erb_debug
int erb_debug = 1;
static const char *cate_name[] = {
	"CTRL_2DSL_SET",
	"CTRL_2DSL_GET",
	"CTRL_2SOC_RET",
	"CTRL_2MASTER_SET",
	"CTRL_2MASTER_GET",
	"CTRL_2SLAVE_RET",
};
	
static const char *proto_name[] = {
	"XDSL_CMD_SET",
	"XDSL_CMD_GET",
	"XDSL_CMD_DATA",
	"XDSL_CMD_DATA_EOF",
	"XDSL_ERB_SET",
	"PTM_CMD_SET",
	"PTM_CMD_GET"
};

void dump_rxbuf(unsigned char *b, int l) {
	unsigned char *byte;
	int i;

	if (!erb_debug) return;

	//dump rxbuf
	byte = b;
	
	printk("rxbuf (total: %d bytes):", l);
	for(i = 0; i < l; i++) {
		if (!(i%16)) printk("\n");
		printk("%02x ", byte[i]);		
	}
	printk("\n\n");	
}

void dump_ctrlp(ctrl_pkt_t *p) {
	int i;
	unsigned char *byte;

	if (!erb_debug) return;
	
	printk("ctrlp->devnum: %d\n", p->devnum);
	printk("ctrlp->category: %s\n", cate_name[p->category]);
	printk("ctrlp->protocol: %s\n", proto_name[p->protocol]);
	printk("ctrlp->command: %d\n", p->command);
	printk("ctrlp->argsize: %d\n", p->argsize);
	printk("ctrlp->msg: 0x%x\n", p->msg);
	
	//dump arg array
	byte = p->arg;
	
	printk("ctrlp->arg:");
	for(i = 0; i < 96; i++) {
		if (!(i%16)) printk("\n");
		printk("%02x ", byte[i]);		
	}
	printk("\n");
	
	printk("ctrlp->ret: %d\n", p->ret);
	
	//dump rxbuf
	byte = p->rxbuf;
	
	printk("ctrlp->rxbuf:");
	for(i = 0; i < 96; i++) {
		if (!(i%16)) printk("\n");
		printk("%02x ", byte[i]);		
	}
	printk("\n\n");
}
#endif

#if defined(CONFIG_PTM_BONDING_MASTER)
static int erbTester_proc_read(struct seq_file *f, void *data) {
	seq_printf(f, "erb proc read\n");
	
	return 0;
}

int erbTester_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
	char proc_buffer[count+1];
	char *strptr;
	char *cmdptr;

	/* write data to the buffer */
	memset(proc_buffer, 0, sizeof(proc_buffer));
	if ( copy_from_user(proc_buffer, buffer, count) ) {
		return -EFAULT;
	}

	proc_buffer[count] = '\0';

	strptr = proc_buffer;
	if (strlen(strptr) == 0) {
		goto errout;
	}

	cmdptr = strsep(&strptr," ");
	if (cmdptr==NULL)
	{
		goto errout;
	}

	//int re865x_send_ERB(char* ERB_data, int ERB_data_len, int line_id,int sync_symbol_count,int segment_code,unsigned char* VCE_macaddr)
	/*parse command*/
	if (strncmp(cmdptr, "1",1) == 0) {
		//xdsl_ctrl_erb_init();
	};

errout:
	return count;

}	


static int read_proc_open_erbTester(struct inode *inode, struct file *file) {
	return(single_open(file, erbTester_proc_read, NULL));
}

static ssize_t write_proc_erbTester(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {
	return erbTester_proc_write(file, userbuf, count, NULL);
}

static struct file_operations fops_erbTester = {
	.open     = read_proc_open_erbTester,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
	.write	  = write_proc_erbTester,
};

void erbTester_proc_init(void) {
	struct proc_dir_entry *entry;
	
	entry = proc_create_data("erbTester", 0644, NULL, &fops_erbTester, NULL);
	if (!entry) printk("\n\n proc entry erbTester created fail!\n\n"); 
}

#elif defined(CONFIG_PTM_BONDING_SLAVE)
extern struct mutex slave_dsl_mutex;
static int erb_send_cnt = 0;
/*
 *      Called by DSL driver to send ERB Ethernet packet in G.Vector mode.
 *      It is actually a wrapper for nfbi tx.
 */
int re865x_send_ERB(char* ERB_data, int ERB_data_len
		,int line_id,int sync_symbol_count,int segment_code,unsigned char* VCE_macaddr)
{
	extern int xdsl_ctrl_tx_final(uint8 category, uint8 protocol, uint8 devnum, uint32 command, 
									uint32 arg, uint32 argsize, uint32 ret, uint32 seg_num);
	unsigned char *data;
	unsigned char dev_addr[6] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
	ERB_header_t* ERB_header_p;
	int len = ((ERB_data_len+27)>=60)?(ERB_data_len+27):60;
	static int should_interrupt = 0;

	erb_printk("alloc data for ERB, len = %d, ERB_data_len (from DSP) = %d, (total %d times)\n", len, ERB_data_len, erb_send_cnt);
	data= (unsigned char*)kmalloc(len, GFP_KERNEL);
	if(!data) {
		printk("[%s] unalbe to alloc data buf for ERB\n", __func__);
		return 0;
	}

	memset(data, 0, len);

	/* header */
	ERB_header_p = (ERB_header_t*)(&data[0]);
	memcpy(ERB_header_p->VCE_macaddr,VCE_macaddr,6);
	memcpy(ERB_header_p->VTU_R_macaddr,dev_addr,6);
	*(unsigned short *)(&ERB_header_p->length)      = len-14; // L2 header
	ERB_header_p->LLC_header[0]             = 0xAA;
	ERB_header_p->LLC_header[1]             = 0xAA;
	ERB_header_p->LLC_header[2]             = 0x03;
	ERB_header_p->ITU_T[0]                  = 0x00;
	ERB_header_p->ITU_T[1]                  = 0x19;
	ERB_header_p->ITU_T[2]                  = 0xA7;
	ERB_header_p->protocol_id[0]            = 0x00;
	ERB_header_p->protocol_id[1]            = 0x03;
	*(unsigned short *)(&ERB_header_p->line_id)                             =(unsigned short)(line_id&0xFFFF);
	*(unsigned short *)(&ERB_header_p->sync_sumbol_count)   =(unsigned short)(sync_symbol_count&0xFFFF);
	ERB_header_p->segment_code              =segment_code&0xFF;

	/*payload*/
	if ((ERB_data != NULL) && (&(data[27]) != NULL)) memcpy(&(data[27]),ERB_data,ERB_data_len);
	else printk("[%s] some data ptr is NULL\n", __func__);

	/*send to master*/
	mutex_lock(&slave_dsl_mutex);
	xdsl_ctrl_tx_final(CTRL_2MASTER_SET, XDSL_ERB_SET, 1, 0, data, len, 0, 0);
	mutex_unlock(&slave_dsl_mutex);

	/*free data since we've transmitted it to Master*/
	kfree(data);
	
	erb_send_cnt++;
	
	return 0;
}

static int erbTester_proc_read(struct seq_file *f, void *data) {

	seq_printf(f, "erb packet sent: %d\n", erb_send_cnt);
	
	return 0;
}

const char erb_dummy_data0[] = {
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21
};

const char erb_dummy_mac0[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x11
};

const char erb_dummy_data1[] = {
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x5a, 0x5a, 0xa5, 0xa5, 0xbe, 0xef, 0xca, 0xfe, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0x43, 0x21,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc,
	0x6b, 0x49, 0xc7, 0xd8, 0xad, 0xde, 0xca, 0xfe, 0x01, 0x23, 0x45, 0x67, 0xab, 0xcd, 0xef, 0xcc	
};

const char erb_dummy_mac1[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x22
};

const char erb_dummy_data2[] = {
	0x55, 0x66, 0x77, 0x88, 0x88, 0x77, 0x66, 0x55, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0xca, 0xfe,
	0x55, 0x66, 0x77, 0x88, 0x88, 0x77, 0x66, 0x55, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0xca, 0xfe,
	0xca, 0xfe, 0x77, 0x55, 0x66, 0x5a, 0xa5, 0xcc, 0x00, 0xbe, 0xef, 0x55, 0x66, 0xcc, 0xca, 0xfe,
	0x55, 0x66, 0x77, 0x88, 0x88, 0x77, 0x66, 0x55, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0xca, 0xfe,
	0x55, 0x66, 0x77, 0x88, 0x88, 0x77, 0x66, 0x55, 0x12, 0x34, 0x56, 0x78, 0x87, 0x65, 0xca, 0xfe
};

const char erb_dummy_mac2[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x33
};

int erbTester_proc_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
	char proc_buffer[count];
	char *strptr;
	char *cmdptr;
	static int testcnt = 0;

	/* write data to the buffer */
	memset(proc_buffer, 0, sizeof(proc_buffer));
	if ( copy_from_user(proc_buffer, buffer, count) ) {
		return -EFAULT;
	}

	proc_buffer[count] = '\0';

	strptr = proc_buffer;
	if (strlen(strptr) == 0) {
		goto errout;
	}

	cmdptr = strsep(&strptr," ");
	if (cmdptr==NULL)
	{
		goto errout;
	}

	//int re865x_send_ERB(char* ERB_data, int ERB_data_len, int line_id,int sync_symbol_count,int segment_code,unsigned char* VCE_macaddr)
	/*parse command*/
	if (strncmp(cmdptr, "1",1) == 0) {
		switch (testcnt%3) {
			case 0:
				re865x_send_ERB(erb_dummy_data0, sizeof(erb_dummy_data0), 1, 0, 0, erb_dummy_mac0);
				break;
			case 1:
				re865x_send_ERB(erb_dummy_data1, sizeof(erb_dummy_data1), 1, 0, 0, erb_dummy_mac1);
				break;
			case 2:
				re865x_send_ERB(erb_dummy_data2, sizeof(erb_dummy_data2), 1, 0, 0, erb_dummy_mac2);
				break;
		}
	};

	testcnt++;
	if (testcnt > 65536) testcnt = 0;
errout:
	return count;

}	


static int read_proc_open_erbTester(struct inode *inode, struct file *file) {
	return(single_open(file, erbTester_proc_read, NULL));
}

static ssize_t write_proc_erbTester(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {
	return erbTester_proc_write(file, userbuf, count, NULL);
}

static struct file_operations fops_erbTester = {
	.open     = read_proc_open_erbTester,
	.read     = seq_read,
	.llseek   = seq_lseek,
	.release  = single_release,
	.write	  = write_proc_erbTester,
};

void erbTester_proc_init(void) {
	struct proc_dir_entry *entry;

	entry = proc_create_data("erbTester", 0644, NULL, &fops_erbTester, NULL);
	if (!entry) printk("\n\n proc entry erbTester created fail!\n\n"); 
}
#endif