/*******************************************************************************
** Document: FW_flash_api.c
** Copyright (c) 2004 R&D Center, Tailyn Communication Company, Ltd.  
** Created by: Chien Chung Lo
** Date: Dec. 24, 2004
** Description: Flash Firmware interface API.
** Version: 0.1
*******************************************************************************/
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <stdio.h>
#include <FW_flash/FW_flash_api.h>

#include <umap_type.h>

#define msg(x,y) printf(x,y)

/*------------------------------------------------------------
   copy to flash
--------------------------------------------------------------*/
UMAP_S32 FW_FlashCopy(char file[256],int partition)
{
	int handle;
	struct flash_option_info info;
	unsigned short parameter; //0:all partition, 1:partition one, 2:partition two                
	int rc;
	unsigned int saddr,p_size;
	
	handle = open(file,O_RDONLY);
	if(handle == -1){
		printf("No such file\n");
		return -1;
	}
	switch(partition){
		case ONE_PARTITION:
			saddr = PARTITION_ONE_OFFSET;
			p_size = ALL_PARTITION_SIZE;
			parameter = 0;
			rc = FW_FlashErase(partition, parameter);
			if(rc) return -1;
			break;
		case TWO_PARTITION:
			p_size = ONE_PARTITION_SIZE;
			rc = FW_ReadFlash(FLASH_BASE_ADDRESS + PARAMETER_OFFSET, 1, &parameter);
			if(rc){printf("Read flash error\n"); return -1;}
			if((parameter & 0x3) == 0x2) //2:partition two
			{saddr = PARTITION_ONE_OFFSET; parameter = 1;}
			else                      //1:partition one
			{saddr = PARTITION_TWO_OFFSET; parameter = 2;}
			rc = FW_FlashErase(partition, parameter);
			if(rc) return -1;
			break;
		default: 
			printf("Invalid partition value\n");
			return -1;
	}//switch
	
	rc = 0;
	info.type = SECTOR_PROGRAM;
	while(p_size > 0){
		saddr = saddr + rc;
		if((parameter != 2) && (saddr == WINMON_OFFSET))
			saddr = WINMON_OFFSET + PARTITION_ONE_OFFSET;
		rc = read(handle, info.data, WORD_SIZE * 2);
		if((rc == -1) || (p_size < rc)) return -1;
		if((rc & 0x1)) rc++;
		info.word_size = rc / 2;
		info.addr_offset = saddr / 2;
		if(rc == 0)  p_size = 0; 
		else if(FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info))
			return -1;
		p_size -= rc;
	}
	
	close(handle);
	rc = FW_FlashParameter(parameter);
	if(rc) return -1;
	return 0;
}
/*------------------------------------------------------------
   flash erase
--------------------------------------------------------------*/
UMAP_S32 FW_FlashErase(int partition,unsigned short parameter)
{
	struct flash_option_info info;
	int rc;
	
	info.type = SECTOR_ERASE;
	
	switch(partition){
		case ONE_PARTITION:
			info.sector_offset = PARTITION_ONE_OFFSET;
			info.sector_size = 0x38;
			rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
			if(rc) return -1;
			sleep(1);
			info.sector_offset = WINMON_OFFSET + PARTITION_ONE_OFFSET;
			info.sector_size = 0xb8;
			rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
			if(rc) return -1;
			break;
		case TWO_PARTITION:
			if(parameter == 1){
				info.sector_offset = PARTITION_ONE_OFFSET;
				info.sector_size = 0x38;
				rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
				if(rc) return -1;
				sleep(1);
				info.sector_offset = WINMON_OFFSET + PARTITION_ONE_OFFSET;
				info.sector_size = 0x40;
				rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
				if(rc) return -1;
			}
			else{
				info.sector_offset = PARTITION_TWO_OFFSET;
				info.sector_size = 0x78;
				rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
				if(rc) return -1;
			}
			break;
		default:
			return -1;
	}
	sleep(1);
	return 0;
}
/*------------------------------------------------------------
   update flash parameter
--------------------------------------------------------------*/
UMAP_S32 FW_FlashParameter(unsigned short parameter)
{
	struct flash_option_info info;
	int rc;
	
	info.type = SECTOR_ERASE;
	info.sector_offset = PARAMETER_OFFSET;
	info.sector_size = 1;
	rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
	if(rc) return -1;
	sleep(1);
	info.type = SECTOR_PROGRAM;
	info.addr_offset = PARAMETER_OFFSET / 2;
	info.word_size = 1;
	info.data[0] = parameter;
	rc = FW_WriteFlash(FLASH_BASE_ADDRESS, 1, &info);
	if(rc) return -1;
	return 0;
}

/*------------------------------------------------------------
   read from flash
--------------------------------------------------------------*/
UMAP_S32 FW_ReadFlash(UMAP_U32 addr, UMAP_U32 size, void *data)
{
	UMAP_S32 flash_file;
	UMAP_S32 result;
	
	flash_file = open(FLASH_DEVICE_FILE_NAME, O_RDONLY);
	if(flash_file < 0)
	{
		result = flash_file;
		msg("%s\n","open error");
		return result; // open fail!!
	}

	addr += VIRTUAL_OFFSET;
	result = lseek(flash_file, addr, SEEK_SET);
	if(result != addr)
	{	
		close(flash_file);
		msg("lseek error address : %x\n",result);
		return result; // lseek fail!!
	}
	
	result = read(flash_file, data, size);
	if(result < 0)
	{	
		close(flash_file);
		msg("%s\n","Read form Flash error");
		return result; // read fail!!
	}
	
	close(flash_file);
		
	return 0; // succeed
}

/*---------------------------------------------------------------
   write to flash
-----------------------------------------------------------------*/
UMAP_S32 FW_WriteFlash(UMAP_U32 addr, UMAP_U32 size, void *data)
{
	UMAP_S32 flash_file;
	UMAP_S32 result;

//msg("%s\n","Write");
	flash_file = open(FLASH_DEVICE_FILE_NAME, O_WRONLY);
	if(flash_file < 0)
	{
		result = flash_file;
		msg("%s\n","open error");
		return result; // open fail!!
	}
	
	addr += VIRTUAL_OFFSET;
	result = lseek(flash_file, addr, SEEK_SET);
	if(result != addr)
	{	
		close(flash_file);
		msg("lseek error address : %x\n",result);
		return result; // lseek fail!!
	}
	
	result = write(flash_file, data, size);
	if(result < 0)
	{	
		close(flash_file);
		msg("Write to Flash error:%d\n",result);
		return result; // read fail!!
	}
	
	close(flash_file);
		
	return 0; // succeed
}
