#include "flash.h"

//#define SEND_INVALID_BLOCK_TABLE_TO_PC
#pragma DATA_SECTION(ffsystem, ".file_system");
TFFSystem ffsystem;
extern TFFSize ffsize;	
#ifdef S29GL032N
static int FlashReadBlock(int p_block, unsigned char *dst, int size)
{   
	if (FLASH_read((Uint32)dst,p_block, size)) // ECC Error
	{
		return 1;
	}
	return 0;
}

static int FlashWriteBlock(int p_block, unsigned char *src, int size)
{
	int count;
	int p_page;
	

	if (FLASH_write((Uint32)src, p_block, size)) 	// Write가 Error없이 Complete
	{
		return 1;
	}
	return 0;
}

int FlashReadFileSystem()
{
	int i, rtn, b;
#ifdef SEND_INVALID_BLOCK_TABLE_TO_PC
	char msg[64] = "Invalid Block Table ";
#endif
	FLASH_getTotalPages( MFG_SPANSION ); 		
	for (i = 0; i < 3; i++)
	{
		rtn = FlashReadBlock(i, (unsigned char *)&ffsystem, sizeof(TFFSystem));
		if (!rtn)
		{
			FlashMarkInvalidBlock(i);
		}
		else 
		{
			for (b = 0; b < FLASH_PAGES; b++)
			{
				if (ffsystem.fat[b] != FFS_FAT_INVALID) break;
			}
			if (b >= FLASH_PAGES) return 0;
			break;
		}
	}

	if (i >= 3) return 0;
	
#ifdef SEND_INVALID_BLOCK_TABLE_TO_PC
	if (ffsystem.fat[i] == FFS_FAT_INVALID) 
	{
		if (i < 10)
		{
			msg[20] = '0' + i;
			msg[21] = 0;
		} 
		else if (i < 100)
		{
			msg[20] = '0' + i / 10;
			msg[21] = '0' + i % 10;
			msg[22] = 0;
		} 
		else if (i < 1000)
		{
			msg[20] = '0' + i / 100;
			msg[21] = '0' + (i % 100) / 10;
			msg[22] = '0' + i % 10;
			msg[23] = 0;
		} 
		else
		{
			msg[20] = '0' + i / 1000;
			msg[21] = '0' + (i % 1000) / 100;
			msg[22] = '0' + (i % 100) / 10;
			msg[23] = '0' + i % 10;
			msg[24] = 0;
		} 
		DebugPutMessage(ffsystem.fat[i], msg);
	}
#endif
	return 1;
}

int FlashWriteFileSystem(int isErase)
{
	int i, rtn;
                                                        	
	for (i = 0; i < 3; i++)
	{
		if (ffsystem.fat[i] == FFS_FAT_UNUSED)
		{
			if (isErase)
			{
				if (!FLASH_erase(i))
				{
					FlashMarkInvalidBlock(i);
					ffsystem.fat[i] = FFS_FAT_INVALID;
				}
			}
			if (ffsystem.fat[i] == FFS_FAT_UNUSED)
			{
				rtn = FlashWriteBlock(i, (unsigned char *)&ffsystem, sizeof(TFFSystem));
				if (!rtn)
				{
					FlashMarkInvalidBlock(i);
					ffsystem.fat[i] = FFS_FAT_INVALID;
				}
				else break;
			}
		}
	}
	return i < 3;
}

void FlashErase(TFFHandle handle)
{
	int p_block, fat;
	TFFInfo *hFileInfo;
	
	hFileInfo = &ffsystem.fileInfo[handle];

	p_block = hFileInfo->startBlock;
	while (1)
	{	
		fat = ffsystem.fat[p_block];
		
		if (!FLASH_erase(p_block))
		{
			FlashMarkInvalidBlock(p_block);
			ffsystem.fat[p_block] = FFS_FAT_INVALID;
		}
		else
		{
			ffsystem.fat[p_block] = FFS_FAT_UNUSED;
		}
		
		if (p_block != fat)
		{
			p_block = fat;
		}
		else break;
	}
	hFileInfo->id = 0;
}

TFFHandle FlashOpen(TFFID id, int attr)
{
	int i;
	
	if (attr == FFS_OPEN_READ)
	{
		for (i = 3; i < FFS_INFO_MAX_COUNT; i++)
		{
			if (id == ffsystem.fileInfo[i].id) 
			{
				return i;
			}
		}
	}
	else if (attr == FFS_OPEN_WRITE)
	{
		for (i = 3; i < FFS_INFO_MAX_COUNT; i++)
		{
			if (id == ffsystem.fileInfo[i].id) break;
		}
		
		if (i < FFS_INFO_MAX_COUNT)
		{
			FlashErase(i);
			ffsystem.fileInfo[i].id = id;
			return i;
		} 
		else
		{
			for (i = 3; i < FFS_INFO_MAX_COUNT; i++)
			{
				if (!ffsystem.fileInfo[i].id) 
				{
					ffsystem.fileInfo[i].id = id;
					return i;
				}
			}
		}
	}
	return -1;
}

int FlashSize(TFFHandle handle)
{
	return ffsystem.fileInfo[handle].size;
}

int FlashRead(TFFHandle handle, unsigned char *buffer, int size)
{   
	int p_block, count;

	p_block = ffsystem.fileInfo[handle].startBlock;
	
	while (size > 0)
	{
		count = size > FLASH_PAGESIZE ? FLASH_PAGESIZE : size;
		if (!FlashReadBlock(p_block, buffer, count)) // Checksum Error
		{
			return 0;
		}
		else
		{
			p_block = ffsystem.fat[p_block];
					
			size -= count;
			buffer += count;
		}
	}
	return 1;
}

int FlashWrite(TFFHandle handle, unsigned char *buffer, int size, int time)
{
	int i, count;
	int p_block;
	TFFInfo *hFileInfo;
	
	hFileInfo = &ffsystem.fileInfo[handle];

	for (i = 3; i < FLASH_PAGES; i++)
	{
		if (!ffsystem.fat[i]) 
		{
			p_block = i;
			break;
		}
	}
	
	hFileInfo->startBlock = p_block;
	hFileInfo->size = size;
	hFileInfo->updatedTime = time;
	
	while (size > 0)
	{	
		count = size > FLASH_PAGESIZE ? FLASH_PAGESIZE : size;
		if (FlashWriteBlock(p_block, buffer, count))  // Write가 Error없이 Complete
		{
			size -= count;
			buffer += count;
			if (size > 0) 
			{
				for (i = p_block + 1; i < FLASH_PAGES; i++)
				{
					if (!ffsystem.fat[i]) 
					{
						ffsystem.fat[p_block] = i;
						p_block = i;
						break;
					}
				}
				if (i >= FLASH_PAGES) return 0;
			}
			else
			{
				ffsystem.fat[p_block] = p_block;
			}
		}
		else
		{
			FlashMarkInvalidBlock(p_block);
			ffsystem.fat[p_block] = FFS_FAT_INVALID;

			for (i = p_block + 1; i < FLASH_PAGES; i++)
			{
				if (!ffsystem.fat[i]) 
				{
					p_block = i;
					break;
				}
			}
			if (i >= FLASH_PAGES) return 0;
		}					
	}
	
	return FlashWriteFileSystem(1);
}

int FlashFormat()
{
	int i;
	FLASH_getTotalPages( MFG_SPANSION );                                             	
	memset(&ffsystem, 0, sizeof(TFFSystem));
	
	for(i = 0; i < FLASH_PAGES; i++)
	{
		if (!FLASH_erase(i))
		{
			FlashMarkInvalidBlock(i);
			ffsystem.fat[i] = FFS_FAT_INVALID;
		}
	}
	
	return FlashWriteFileSystem(0);
}


TFFSystem *GetFileSystem(void)
{
	return &ffsystem;
}

#endif

#ifdef FM_K9F2808
static int FlashReadBlock(int p_block, unsigned char *dst, int size)
{   
	int p_page, count;

	p_page = p_block * ffsize.page_per_block*(ffsize.byte_for_page);
	while (size > 0)
	{
		count = size < ffsize.byte_for_page ? size : ffsize.byte_for_page;
		if (FlashReadPage(dst, p_page, count)) // ECC Error
		{
			p_page+=ffsize.byte_for_page;
			size -= count;
			dst += count;
		}
		else
		{
			return 0;
		}
	}
	return 1;
}

static int FlashWriteBlock(int p_block, unsigned char *src, int size)
{
	int count;
	int p_page;
	
	p_page = p_block * ffsize.page_per_block*(ffsize.byte_for_page);
	while (size > 0)
	{	
		count = size > ffsize.byte_for_page ? ffsize.byte_for_page : size;
		if (FlashWritePage(src, p_page, count))  // Write가 Error없이 Complete
		{
			p_page+=ffsize.byte_for_page;
			size -= count;
			src += count;
		}
		else
		{
			return 0;
		}					
	}
	return 1;
}

int FlashReadFileSystem()
{
	int i, rtn, b;
#ifdef SEND_INVALID_BLOCK_TABLE_TO_PC
	char msg[64] = "Invalid Block Table ";
#endif
		
	for (i = 0; i < 3; i++)
	{
		rtn = FlashReadBlock(i, (unsigned char *)&ffsystem, sizeof(TFFSystem));
		if (!rtn)
		{
			FlashMarkInvalidBlock(i);
		}
		else 
		{
			for (b = 0; b < BLOCKS_PER_DEIVCE; b++)
			{
				if (ffsystem.fat[b] != FFS_FAT_INVALID) break;
			}
			if (b >= BLOCKS_PER_DEIVCE) return 0;
			break;
		}
	}

	if (i >= 3) return 0;
	
#ifdef SEND_INVALID_BLOCK_TABLE_TO_PC
	if (ffsystem.fat[i] == FFS_FAT_INVALID) 
	{
		if (i < 10)
		{
			msg[20] = '0' + i;
			msg[21] = 0;
		} 
		else if (i < 100)
		{
			msg[20] = '0' + i / 10;
			msg[21] = '0' + i % 10;
			msg[22] = 0;
		} 
		else if (i < 1000)
		{
			msg[20] = '0' + i / 100;
			msg[21] = '0' + (i % 100) / 10;
			msg[22] = '0' + i % 10;
			msg[23] = 0;
		} 
		else
		{
			msg[20] = '0' + i / 1000;
			msg[21] = '0' + (i % 1000) / 100;
			msg[22] = '0' + (i % 100) / 10;
			msg[23] = '0' + i % 10;
			msg[24] = 0;
		} 
		DebugPutMessage(ffsystem.fat[i], msg);
	}
#endif
	return 1;
}

int FlashWriteFileSystem(int isErase)
{
	int i, rtn;
                                                        	
	for (i = 0; i < 3; i++)
	{
		if (ffsystem.fat[i] == FFS_FAT_UNUSED)
		{
			if (isErase)
			{
				if (!FlashEraseBlock(i))
				{
//					FlashMarkInvalidBlock(i);
					ffsystem.fat[i] = FFS_FAT_INVALID;
				}
			}
			if (ffsystem.fat[i] == FFS_FAT_UNUSED)
			{
				rtn = FlashWriteBlock(i, (unsigned char *)&ffsystem, sizeof(TFFSystem));
				if (!rtn)
				{
//					FlashMarkInvalidBlock(i);
					ffsystem.fat[i] = FFS_FAT_INVALID;
				}
				else break;
			}
		}
	}
	return i < 3;
}

void FlashErase(TFFHandle handle)
{
	int p_block, fat;
	TFFInfo *hFileInfo;
	
	hFileInfo = &ffsystem.fileInfo[handle];

	p_block = hFileInfo->startBlock;
	while (1)
	{	
		fat = ffsystem.fat[p_block];
		
		if (!FlashEraseBlock(p_block))
		{
//			FlashMarkInvalidBlock(p_block);
			ffsystem.fat[p_block] = FFS_FAT_INVALID;
		}
		else
		{
			ffsystem.fat[p_block] = FFS_FAT_UNUSED;
		}
		
		if (p_block != fat)
		{
			p_block = fat;
		}
		else break;
	}
	hFileInfo->id = 0;
}

TFFHandle FlashOpen(TFFID id, int attr)
{
	int i;
	
	if (attr == FFS_OPEN_READ)
	{
		for (i = 3; i < FFS_INFO_MAX_COUNT; i++)
		{
			if (id == ffsystem.fileInfo[i].id) 
			{
				return i;
			}
		}
	}
	else if (attr == FFS_OPEN_WRITE)
	{
		for (i = 3; i < FFS_INFO_MAX_COUNT; i++)
		{
			if (id == ffsystem.fileInfo[i].id) break;
		}
		
		if (i < FFS_INFO_MAX_COUNT)
		{
			FlashErase(i);
			ffsystem.fileInfo[i].id = id;
			return i;
		} 
		else
		{
			for (i = 3; i < FFS_INFO_MAX_COUNT; i++)
			{
				if (!ffsystem.fileInfo[i].id) 
				{
					ffsystem.fileInfo[i].id = id;
					return i;
				}
			}
		}
	}
	return -1;
}

int FlashSize(TFFHandle handle)
{
	return ffsystem.fileInfo[handle].size;
}

int FlashRead(TFFHandle handle, unsigned char *buffer, int size)
{   
	int p_block, count, next_p_block;
	int test = 0;
	int temp_block;
	p_block = ffsystem.fileInfo[handle].startBlock;
	while (size > 0)
	{
		count = size > ffsize.block_max_size ? ffsize.block_max_size : size;
	
		temp_block = ffsystem.fat[p_block];
		if(temp_block == 0xFFFF)
		{
			p_block++;
			continue;
		}

		if (!FlashReadBlock(p_block, buffer, count)) // Checksum Error
		{
			return 0;
		}
		else
		{
			p_block = ffsystem.fat[p_block];

			next_p_block = ffsystem.fat[p_block];
			if(next_p_block == 0xFFFF)
			{
				p_block++;
			}			

			size -= count;
			buffer += count;
		}
		if (p_block >= BLOCKS_PER_DEIVCE) return 0;
	}
	return 1;
}

int FlashWrite(TFFHandle handle, unsigned char *buffer, int size, int time)
{
	int i, count;
	int p_block;
	TFFInfo *hFileInfo;
	
	hFileInfo = &ffsystem.fileInfo[handle];

	for (i = 3; i < BLOCKS_PER_DEIVCE; i++)
	{
		if (!ffsystem.fat[i]) 
		{
			p_block = i;
			break;
		}
	}
	
	hFileInfo->startBlock = p_block;
	hFileInfo->size = size;
	hFileInfo->updatedTime = time;
	
	while (size > 0)
	{	
		count = size > ffsize.block_max_size ? ffsize.block_max_size : size;
		if (FlashWriteBlock(p_block, buffer, count))  // Write가 Error없이 Complete
		{
			size -= count;
			buffer += count;
			if (size > 0) 
			{
				for (i = p_block + 1; i < BLOCKS_PER_DEIVCE; i++)
				{
					if (!ffsystem.fat[i]) 
					{
						ffsystem.fat[p_block] = i;
						p_block = i;
						break;
					}
				}
				if (i >= BLOCKS_PER_DEIVCE) return 0;
			}
			else
			{
				ffsystem.fat[p_block] = p_block;
			}
		}
		else
		{
//			FlashMarkInvalidBlock(p_block);
			ffsystem.fat[p_block] = FFS_FAT_INVALID;

			for (i = p_block + 1; i < BLOCKS_PER_DEIVCE; i++)
			{
				if (!ffsystem.fat[i]) 
				{
					p_block = i;
					break;
				}
			}
			if (i >= BLOCKS_PER_DEIVCE) return 0;
		}					
	}
	
	return FlashWriteFileSystem(1);
}

int FlashFormat()
{
	int i;
                                                        	
	memset(&ffsystem, 0, sizeof(TFFSystem));
	
	for(i = 0; i < BLOCKS_PER_DEIVCE; i++)
	{
		if (!FlashEraseBlock(i))
		{
//			FlashMarkInvalidBlock(i);
			ffsystem.fat[i] = FFS_FAT_INVALID;
		}
	}
	
	return FlashWriteFileSystem(0);
}


TFFSystem *GetFileSystem(void)
{
	return &ffsystem;
}

#endif