#include <stdlib.h>
#include <stdio.h>

#if !defined(__WINDOWS__)
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#endif

#if defined(__LINUX__)
# include <linux/if.h>
# include <linux/types.h>
# include <linux/if_packet.h>
# include <linux/wanpipe_defines.h>
# include <linux/sdlasfm.h>
# include <linux/wanpipe_cfg.h>
#elif defined(__WINDOWS__)
# include <windows.h>
# include <winioctl.h>
# include <conio.h>
# include <stddef.h>	/* offsetof() */
# include <string.h>

# include "wanpipe_includes.h"
# include "wanpipe_common.h"
# include "wanpipe_time.h"	/* wp_usleep() */
# include "sdlasfm.h"
# include "sdlapci.h"
#else
# include <net/if.h>
# include <wanpipe_defines.h>
# include <sdlasfm.h>
# include <wanpipe_cfg.h>
#endif

#include "wan_aft_prg.h"

#define MASK_DEF_SECTOR_FLASH	0x00
#define MASK_USER_SECTOR_FLASH	0x08

#define AFT_SHARK_USER_SECTOR_START_ADDR	0x80000

/* Manufacturer code */
#define MCODE_CYPRESS	0x01
#define MCODE_ST	0x20

/* Device code */
#define DCODE_M29W040B	0xE3
#define DCODE_M29W800DT	0xD7
#define DCODE_M29W800DB	0x5B

#define M29W800DT_FID	0
#define M29W800DB_FID	1

struct {
	unsigned long	start_addr;
	unsigned long	start_off;
	unsigned long	len;
	unsigned int	sector_type;
} aft_shark_flash_spec [2][19] = 
	{
		{ 
			{ 0x00000, 0x00000, 0xFFFF, DEF_SECTOR_FLASH }, 
			{ 0x10000, 0x10000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x20000, 0x20000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x30000, 0x30000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x40000, 0x40000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x50000, 0x50000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x60000, 0x60000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x70000, 0x70000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x80000, 0x00000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0x90000, 0x10000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xA0000, 0x20000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xB0000, 0x30000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xC0000, 0x40000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xD0000, 0x50000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xE0000, 0x60000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xF0000, 0x70000, 0x7FFF, USER_SECTOR_FLASH },
			{ 0xF8000, 0x78000, 0x1FFF, USER_SECTOR_FLASH },
			{ 0xFA000, 0x7A000, 0x1FFF, USER_SECTOR_FLASH },
			{ 0xFC000, 0x7C000, 0x3FFF, USER_SECTOR_FLASH },
		},

		{
			{ 0x00000, 0x00000, 0x3FFF, DEF_SECTOR_FLASH },
			{ 0x04000, 0x40000, 0x1FFF, DEF_SECTOR_FLASH },
			{ 0x06000, 0x60000, 0x1FFF, DEF_SECTOR_FLASH },
			{ 0x08000, 0x80000, 0x7FFF, DEF_SECTOR_FLASH },
			{ 0x10000, 0x10000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x20000, 0x20000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x30000, 0x30000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x40000, 0x40000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x50000, 0x50000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x60000, 0x60000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x70000, 0x70000, 0xFFFF, DEF_SECTOR_FLASH },
			{ 0x80000, 0x00000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0x90000, 0x10000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xA0000, 0x20000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xB0000, 0x30000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xC0000, 0x40000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xD0000, 0x50000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xE0000, 0x60000, 0xFFFF, USER_SECTOR_FLASH },
			{ 0xF0000, 0x70000, 0xFFFF, USER_SECTOR_FLASH },
		}
	};

aftup_flash_t	aft_shark_flash = {  0x014F, AFT_CORE_X1000_SIZE };

extern int	verbose;

extern int	progress_bar(char*,int,int);
/*
 ******************************************************************************
			  FUNCTION PROTOTYPES
 ******************************************************************************
*/
		
extern int exec_read_cmd(void*,unsigned int, unsigned int, unsigned int*);
extern int exec_write_cmd(void*,unsigned int, unsigned int, unsigned int);
extern void hit_any_key(void);

static int aft_reset_flash_shark(wan_aft_cpld_t *cpld);
static int aft_is_protected_shark(wan_aft_cpld_t *cpld, int stype);
static int aft_flash_id_shark(wan_aft_cpld_t *cpld, int mtype, int stype, int *flash_id);
static int aft_reload_flash_shark(wan_aft_cpld_t *cpld, int sector_type);
static int aft_write_flash_shark(wan_aft_cpld_t*,int, unsigned long,unsigned char*);
static int aft_read_flash_shark(wan_aft_cpld_t*,int,int,unsigned long, unsigned char**);
static unsigned char aft_read_flash_byte_shark(wan_aft_cpld_t*,int,int,unsigned long);
static int aft_erase_flash_shark(wan_aft_cpld_t*,int,int);

aftup_flash_iface_t aftup_shark_flash_iface = 
{
	aft_reset_flash_shark,
	aft_is_protected_shark,
	aft_flash_id_shark,
	aft_reload_flash_shark,
	aft_write_flash_shark,
	aft_read_flash_shark,
	aft_erase_flash_shark
};

/******************************************************************************
*			  FUNCTION DEFINITION	
******************************************************************************/
static unsigned short get_cpld_off(int adptr_type, unsigned short cpld_off)
{

	switch(adptr_type){
	case A300_ADPTR_U_1TE3:
		cpld_off &= ~AFT_BIT_DEV_ADDR_CLEAR; 
		cpld_off |= AFT_BIT_DEV_ADDR_CPLD; 
		break;
	case AFT_ADPTR_56K:
		cpld_off &= ~AFT8_BIT_DEV_ADDR_CLEAR; 
		cpld_off |= AFT8_BIT_DEV_ADDR_CPLD; 
		break;
	default:
		cpld_off |= AFT4_BIT_DEV_ADDR_CPLD; 
		break;
	}
	return cpld_off;

}
static unsigned int
write_cpld(wan_aft_cpld_t *cpld, unsigned short cpld_off,unsigned short cpld_data)
{
	cpld_off = get_cpld_off(cpld->adptr_type, cpld_off);
	exec_write_cmd(cpld->private, 0x46, 2, cpld_off);
	exec_write_cmd(cpld->private, 0x44, 2, cpld_data);
	return 0;
}

static unsigned int
read_cpld(wan_aft_cpld_t *cpld, unsigned short cpld_off)
{
	unsigned int cpld_data;
		
	cpld_off = get_cpld_off(cpld->adptr_type, cpld_off);
	exec_write_cmd(cpld->private, 0x46, 2, cpld_off);
	if (exec_read_cmd(cpld->private, 0x44, 4, &cpld_data) == 0){
		return cpld_data;
	}else{
		return 0;
	}
}

static unsigned int
__aft_write_flash_shark_byte(wan_aft_cpld_t *cpld, int stype, int mtype, unsigned long off,unsigned char data)
{
	unsigned char	offset;

	//Writing flash address to cpld
	offset = off & 0xFF;
	write_cpld(cpld, 0x05, offset);
	offset = (off >> 8) & 0xFF;
	write_cpld(cpld, 0x06, offset);
	offset = (off >> 16) & 0x7;
	if (mtype == MEMORY_TYPE_SRAM){
		offset |= MASK_MEMORY_TYPE_SRAM;
	}else if (mtype == MEMORY_TYPE_FLASH){
		offset |= MASK_MEMORY_TYPE_FLASH;
	}else{
		return -EINVAL;
	}
	if (stype == USER_SECTOR_FLASH){
		offset |= MASK_USER_SECTOR_FLASH;
	}else if (stype == DEF_SECTOR_FLASH){
		;
	}else{
		return -EINVAL;
	}
	write_cpld(cpld, 0x07, offset);
	write_cpld(cpld, 0x04, data);
        write_cpld(cpld, 0x07, 0x00);  // disable CS signal for the Boot FLASH/SRAM

	return 0;
}

static unsigned int
aft_write_flash_shark_byte(wan_aft_cpld_t *cpld, int stype, int mtype, unsigned long off,unsigned char data)
{
	unsigned long	sec_off = 0x00;

	if (stype == USER_SECTOR_FLASH){
		sec_off = AFT_SHARK_USER_SECTOR_START_ADDR;
	}
	return __aft_write_flash_shark_byte(cpld, stype, mtype, sec_off + off, data);
}

static unsigned char
__aft_read_flash_byte_shark(wan_aft_cpld_t *cpld, int stype, int mtype, unsigned long off)
{
	unsigned char offset;
        unsigned char data;

	//Writing flash address to cpld
	offset = off & 0xFF;
	write_cpld(cpld, 0x05, offset);
	offset = (off >> 8) & 0xFF;
	write_cpld(cpld, 0x06, offset);
	offset = (off >> 16) & 0x7;
        offset |= MASK_MEMORY_TYPE_FLASH;
//           
//	if (memory_type == MEMORY_TYPE_SRAM){
//		offset |= MASK_MEMORY_TYPE_SRAM;
//	}else if (memory_type == MEMORY_TYPE_FLASH){
//		offset |= MASK_MEMORY_TYPE_FLASH;
//	}else{
//		return -EINVAL;
//	}
	if (stype == USER_SECTOR_FLASH){
		offset |= MASK_USER_SECTOR_FLASH;
	}else if (stype == DEF_SECTOR_FLASH){
		;
	}else{
		return -EINVAL;
	}

	write_cpld(cpld, 0x07, offset);
        data = read_cpld(cpld, 0x04);
        write_cpld(cpld, 0x07, 0x00); // Disable CS for the Boot FLASH/SRAM
	return data;
}

static unsigned char
aft_read_flash_byte_shark(wan_aft_cpld_t *cpld, int stype, int mtype, unsigned long off)
{
	unsigned long	sec_off = 0x00;
	if (stype == USER_SECTOR_FLASH){
		sec_off = AFT_SHARK_USER_SECTOR_START_ADDR;
	}
	return __aft_read_flash_byte_shark(cpld, stype, mtype, sec_off + off);
}

static int
aft_erase_flash_shark(wan_aft_cpld_t *cpld, int stype, int verify)
{
	unsigned long offset = 0x00;
	unsigned char data = 0x00;
	int sector_no = 0;

	for(sector_no = 0; sector_no < 19; sector_no++){
		if (aft_shark_flash_spec[cpld->flash_index][sector_no].sector_type != stype){
			continue;
		}
		offset = aft_shark_flash_spec[cpld->flash_index][sector_no].start_off;
		__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0xAAA, 0xAA);
		__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0x555, 0x55);
		__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0xAAA, 0x80);
		__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0xAAA, 0xAA);
		__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0x555, 0x55);
		aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, offset, 0x30);

		do{
//MF			for(i=0;i<100000;i++);
			wp_usleep(1);
			data = aft_read_flash_byte_shark(
					cpld, stype,
					MEMORY_TYPE_FLASH,
					offset);
			if (data & 0x80){
				break;
			}else if (data & 0x20){
				data = aft_read_flash_byte_shark(
						cpld, stype, 
						MEMORY_TYPE_FLASH, 
						offset);
				if (data & 0x80){
					break;
				}else{
					printf("%s: Failed!\n", __FUNCTION__);
					printf("%s: Sector=%d!\n",
						__FUNCTION__,
						(stype == USER_SECTOR_FLASH) ? 
								sector_no+4:sector_no);
					return -EINVAL;
				}
			}
		} while(1);

		progress_bar("\tErasing sectors\t\t\t\t", 
					sector_no, 19);
	}
	printf("\r\tErasing sectors\t\t\t\tPassed\n");
	if (!verify) return 0;
	
	// Verify that flash is 0xFF
	for(offset = 0; offset < 0x80000; offset++){
//MF		for(i=0;i<10000;i++);
		data = aft_read_flash_byte_shark(cpld, stype, MEMORY_TYPE_FLASH, offset);	
		if (data != 0xFF){
			printf(" Failed to compare! %05lx -> %02x \n",
						offset,data);
			return -EINVAL;
		}
		if ((offset & 0x1FFF) == 0x1000){
			progress_bar("\tErasing sectors (verification)\t\t",
							offset, 0x80000);
		}
	}
	printf("\r\tErasing sectors (verification)\t\tPassed\n");
	return 0;
}

static int
aft_write_flash_shark(wan_aft_cpld_t *cpld, int stype, unsigned long off32, unsigned char* pdata)
{
	unsigned char data;
	unsigned char data1 = 0x00;
	int num_bytes = 1;
	
	data = *pdata;
	
	__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0xAAA, 0xAA);
	__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0x555, 0x55);
	__aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, 0xAAA, 0xA0);
	aft_write_flash_shark_byte(cpld, stype, MEMORY_TYPE_FLASH, off32, data);

	do{
//MF		for(i=0;i<1000;i++);
		data1 = aft_read_flash_byte_shark(cpld, stype, MEMORY_TYPE_FLASH, off32);
		if ((data1 & 0x80) == (data & 0x80)){
			break;
		}else if (data1 & 0x20){
			data1 = aft_read_flash_byte_shark(
					cpld, stype, 
					MEMORY_TYPE_FLASH, 
					off32);
			if ((data1 & 0x80) == (data & 0x80)){
				break;
			}else{
				printf("prg_flash_byte: Failed (%lX)!\n",
							off32);
				return -EINVAL;
			}
		}
	} while(1);
	return num_bytes;
}


static int
aft_read_flash_shark(wan_aft_cpld_t *cpld, int stype, int mtype, unsigned long off, unsigned char** ppdata)
{
	int num_bytes = 1;
	unsigned char* pdata = NULL;
	unsigned long	sec_off = 0x00;
	
	pdata = malloc(num_bytes);
	if (pdata == NULL) {
		printf("Failed to allocate memory (%s:%d)\n", 
				__FUNCTION__,__LINE__);
		return -ENOMEM;
	}
	memset(pdata, 0, num_bytes);
	
	if (stype == USER_SECTOR_FLASH){
		sec_off = AFT_SHARK_USER_SECTOR_START_ADDR;
	}	

	*pdata =  __aft_read_flash_byte_shark(cpld, stype, mtype, sec_off + off);
	*ppdata = pdata;
	return num_bytes;

}


static int aft_reset_flash_shark(wan_aft_cpld_t *cpld)
{
	__aft_write_flash_shark_byte(cpld, DEF_SECTOR_FLASH, MEMORY_TYPE_FLASH, 0x00, 0xF0);
	return 0;
}

static int aft_is_protected_shark(wan_aft_cpld_t *cpld, int stype)
{
	return 0;
}

static int aft_flash_id_shark(wan_aft_cpld_t *cpld, int mtype, int stype, int *flash_id)
{
	unsigned char	man_code, device_code;

	aft_reset_flash_shark(cpld);
	__aft_write_flash_shark_byte(cpld, stype, mtype, 0xAAA, 0xAA);
	__aft_write_flash_shark_byte(cpld, stype, mtype, 0x555, 0x55);
	__aft_write_flash_shark_byte(cpld, stype, mtype, 0xAAA, 0x90);

	man_code = aft_read_flash_byte_shark(cpld, stype, mtype, 0x00);
	printf("The current shark flash man id %02X , cpld = %p, mtype =%02X and stype = %02X\n", man_code, cpld, mtype, stype);

	if ((man_code != MCODE_ST) && (man_code != MCODE_CYPRESS)){
		FILE *fp = NULL;

		fp = fopen("/tmp/fw-upd-err.txt","w");

		printf("The current shark flash is not supported (man id %02X)!\n",
				man_code);

		if( fp ){
			fprintf(fp, "Firmware Upgrade Failed with manufcaturer ID as %02X!\n", man_code);
			fclose(fp);
		}
		return -EINVAL;
	}
	*flash_id = man_code << 8;

	device_code = aft_read_flash_byte_shark(cpld, stype, mtype, 0x02);
	switch(device_code){
	case DCODE_M29W800DT:
		cpld->flash_index = M29W800DT_FID;
		break;
	case DCODE_M29W800DB:
		cpld->flash_index = M29W800DB_FID;
		break;
	default:
		printf("The current flash is not supported (dev id %02X)!\n",
					device_code);
		return -EINVAL;
		break;
	}
	*flash_id |= device_code;
	aft_reset_flash_shark(cpld);
	return 0;
}

static int aft_reload_flash_shark(wan_aft_cpld_t *cpld, int sector_type)
{
	if (sector_type == USER_SECTOR_FLASH){
		/* Reload new code in to Xilinx from
                ** Flash User sector */  
		write_cpld(cpld, 0x07,0xC0); 
	}else{
		/* Reload new code in to Xilinx from
		** Flash Default sector */   
		write_cpld(cpld, 0x07,0x80);   
	}
	return 0;
}

