//----------------------------------------------------
/**
	\file		ctk/inc/flashfs.h
	\ingroup	hardware
	\ingroup	all

	\brief		Flash wear levelling
				<<< ALL >>>

	\author		ConTeK s.r.o., Ing.Pavel Bazika
	\version	01.01
	\date		14.11.2008
*/
//----------------------------------------------------

#ifndef _FLASHFS_H_
#define _FLASHFS_H_

#include <libsets.h>
#include <ctktypes.h>
#include <hwboard.h>
#include <stmachine.h>
#include <ucrc32.h>
#include <nvmem.h>

// todo libsets
#define FLASHFS_STATISTICS				//!< Zapnuti sberu statistickych udaju

#define FLASHFS_TLB_SIZE			10		//!< Pocet polozek v TLB
#define FLASHFS_VERIFYBUF_SIZE		256		//!< Velikost bufferu pro verifikaci zapisu
#define FLASHFS_MAX_WRITE_PAGES		20		//!< Odpovida maximalnimu poctu po sobe jdoucich stranek, ktere lze zapsat behem 1 operace
static const nvptr_t FLASHFS_MIN_EMPTY_COUNT = 10;		//!< Minimalni pocet volnych stranek
static const uint16_t FLASHFS_CRC_STEP_SIZE = 128;		//!< Pocet bytu pocitanych v jednom kroku vypoctu crc

//#define FLASHFS_MAXPAGES		4096	//!< 2 MB   !<Pr presunuto do libsets!!!!!

#if FLASHFS_TLB_SIZE > 0
	#define FLASHFS_TLB
#endif

// todo - dynamicky
#define DFS_PAGE_SIZE		528

//! Datovy typ fyzicke adresy stranky
typedef uint16_t physpage_t;

//! Datovy typ virtualni adresy stranky
typedef uint16_t virtpage_t;

//! Datovy typ citace stari a stavu stranky
typedef uint8_t pagecntr_t;

#ifdef MSVC
#pragma pack(push,1)
#endif // MSVC

//! struktura ulozeni zapisovane stranky
typedef struct _FLASHFS_WRITERECORD
{
	virtpage_t		VirtAddr;		//!< Virtualni adresa stranky
	physpage_t		PhysSrc;		//!< Puvodni umisteni ve flash
	physpage_t		PhysDest;		//!< Nove umisteni ve flash
#ifdef MSVC
} FLASHFS_WRITERECORD;
#else
} __attribute__((packed)) FLASHFS_WRITERECORD;
#endif

//! struktura dat v systemove strance
typedef struct _FLASHFS_SYSPAGE
{
	uint16_t			Version;	//!< Verze filesystemu
	uint16_t			Magic;		//!< Magicke cislo

	uint16_t			WriteCount;			//!< Pocet zapisovanych stranek, nejvyssi bit udava fazi zapisu
	FLASHFS_WRITERECORD	WriteLog[FLASHFS_MAX_WRITE_PAGES];	//!< Seznam zapisovanych stranek		

#ifdef MSVC
} FLASHFS_SYSPAGE;
#else
} __attribute__((packed)) FLASHFS_SYSPAGE;
#endif

//! struktura informacni oblasti stranky
typedef struct _SPARE_STRUCT
{
	pagecntr_t	Counter;
	uint8_t		Reserved;
	virtpage_t	Addr;
	crc32_t		CRC;
#ifndef MSVC
} __attribute__((packed)) SPARE_STRUCT;
#else
} SPARE_STRUCT;
#endif // MSVC

//! velikost informaci o strance
#define SPARE_SIZE		(sizeof(SPARE_STRUCT))

//! buffer pro stranku prenaseny pres DMA
typedef struct _FLASHFS_PAGEBUF
{
	uint8_t		Data[DFS_PAGE_SIZE];
#ifndef MSVC
} __attribute__((packed)) FLASHFS_PAGEBUF;
#else
} FLASHFS_PAGEBUF;
#endif // MSVC

//! buffer pro verifikaci prensaeny pres DMA
typedef struct _FLASHFS_VERIFYBUF
{
	uint8_t		Data[FLASHFS_VERIFYBUF_SIZE];
#ifndef MSVC
} __attribute__((packed)) FLASHFS_VERIFYBUF;
#else
} FLASHFS_VERIFYBUF;
#endif // MSVC

#ifdef MSVC
#pragma pack(pop)
#endif // MSVC

//! polozka cache prekladu adres
typedef struct _TLB_ITEM
{
	virtpage_t	Virt;
	physpage_t	Phys;
} TLB_ITEM;

#ifdef FLASHFS_STATISTICS

typedef struct _FLASHFS_STATS
{
	uint32_t	WriteCount;
	uint32_t	ReadCount;
	uint32_t	WriteError;
	uint32_t	ReadError;
} FLASHFS_STATS;

#endif // FLASHFS_STATISTICS

typedef struct _FLASHFS_MEMSTATUS
{
	uint16_t	PageFaulty;
	uint16_t	PageEmpty;
	uint16_t	PageValid;
} FLASHFS_MEMSTATUS;

//! struktura informaci o pameti
typedef struct _FLASHFS_MEMINFO
{
	DATAFLASH_CTX * Mem;
	nvptr_t			PageDataSize;
} FLASHFS_MEMINFO;

//! kontext automatu hledani stranky ve flash
typedef struct _FLASHFS_PHYSSEARCH_CTX
{
	STMACHINE	Stm;
	uint8_t		Status;
	virtpage_t	VirtAddr;
	physpage_t	TestAddr;
	uint8_t		AttemptCnt;
} FLASHFS_PHYSSEARCH_CTX;

//! kontext automatu hledani prazdne stranky
typedef struct _FLASHFS_EMPTYSEARCH_CTX
{
	STMACHINE	Stm;
	uint8_t		Status;		//!< Vysledek posledni operace
	physpage_t	StartPage;	//!< Stranka, u ktere vyhledavani zacalo
	physpage_t	TestPage;	//!< Stranka, ktera je prave testovana
	uint8_t		AttemptCnt;
} FLASHFS_EMPTYSEARCH_CTX;

//! kontext automatu vypoctu CRC
typedef struct _FLASHFS_CRCCALC_CTX
{
	STMACHINE		Stm;
	const uint8_t	* Buffer;
	uint16_t		Size;
	crc32_t			CRC;
} FLASHFS_CRCCALC_CTX;

//! kontext inicializacniho automatu
typedef struct _FLASHFS_INIT_CTX
{
	STMACHINE	Stm;

	physpage_t	TestAddr;
	pagecntr_t	DuplCounter;
	physpage_t	Page;
	uint8_t		AttemptCnt;
	uint16_t	WriteIdx;
	bool_t		DeleteNew;

} FLASHFS_INIT_CTX;


//! kontext cteciho automatu
typedef struct _FLASHFS_READ_CTX
{
	STMACHINE	Stm;			//!< Stav automatu

	virtpage_t	VirtAddr;		//!< Virtualni adresa stranky
	nvptr_t		IntAddr;		//!< Adresa bytu v ramci stranky
	void		* Buffer;		//!< Buffer pro ulozeni vysledku
	nvptr_t		Size;			//!< Pocet bytu k prenosu

} FLASHFS_READ_CTX;


//! kontext zapisovaciho automatu
typedef struct _FLASHFS_WRITE_CTX
{
	STMACHINE	Stm;			//!< Stav automatu
	uint8_t		Status;

	virtpage_t	VirtAddr;		//!< Stranka, do ktere se ma zapisovat
	nvptr_t		IntAddr;		//!< Adresa v ramci stranky
	const void	* Buffer;		//!< Buffer s daty k zapisu
	nvptr_t		Size;			//!< Pocet bytu k zapisu

	virtpage_t	LastPage;		//!< Posledni zapisovana stranka
	virtpage_t	CurPage;		//!< Aktualne zpracovavana stranka

	bool_t		DiscardNew;		//!< Priznak, zda se maji rusit stare nebo nove polozky
} FLASHFS_WRITE_CTX;


//!< kontext r/w automatu pracujiciho nad jednotlivymi strankami
typedef struct _FLASHFS_PAGER_CTX
{
	STMACHINE	Stm;			//!< Kontext automatu
	uint8_t		Cmd;			//!< Zpracovavany prikaz
	uint8_t		Status;			//!< Stav zpracovavani

	virtpage_t	VirtAddr;		//!< Virtualni adresa stranky
	nvptr_t		IntAddr;		//!< Interni adresa uvnitr stranky

	const void	*Buffer;		//!< Buffer s daty pro zapis

	nvptr_t		Size;			//!< Pocet bytu k zapisu

	uint8_t		BadPageMap[(FLASHFS_MAXPAGES + 7) / 8];	//!< Mapa cerstve vadnych stranek pameti (fyzickych)
	physpage_t	PhysAddr;		//!< Fyzicka stranka, se kterou se pracuje
	physpage_t	NewPhysAddr;	//!< Prazdna fyzicka stranka pro pripad zapisu
	uint8_t		AttemptCnt;		//!< Pocet pokusu o zapis/cteni
	uint8_t		PageAttemptCnt;	//!< Pocet pokusu o zapis do nektere stranky
	nvptr_t		VerifyAddr;		//!< Adresa pocatku verifikovane oblasti

} FLASHFS_PAGER_CTX;

//! prikazy
typedef enum _FLASHFS_CMD
{
	FCMD_NONE = 0,
	FCMD_READ,
	FCMD_WRITE,
	FCMD_INITLOAD
} FLASHFS_CMD;

//! parametry prikazu cteni
typedef struct _FLASHFS_CMD_READ
{
	virtpage_t	Page;
	nvptr_t		Addr;
} FLASHFS_CMD_READ;

//! parametry prikazu zapisu
typedef struct _FLASHFS_CMD_WRITE
{
	virtpage_t	Page;
	nvptr_t		Addr;
} FLASHFS_CMD_WRITE;

//! kontext FLASHFS
typedef struct _FLASHFS_CTX
{
	NVMEMDESC			Parent;

	bool_t				flInitialized;

	// stav a konfigurace
	physpage_t			FirstPage;			//!< Prvni stranka v ovladanem rozsahu
	physpage_t			LastPage;			//!< Posledni stranka v ovladanem rozsahu
	physpage_t			FirstEmptyPage;		//!< Stranka, kterou se zahaji vyhledavani prazdne stranky
	physpage_t			SysPagePhys;		//!< Fyzicka adresa systemove stranky
	virtpage_t			PageBuffVirtAddr;	//!< Virtualni adresa stranky prave ulozene v PageBuffer

	// informace o pameti
	FLASHFS_MEMINFO		MemInfo;

#ifdef FLASHFS_STATISTICS
	FLASHFS_STATS		* Stats;
#endif // FLASHFS_STATISTICS

	FLASHFS_MEMSTATUS	MemStatus;

	// hlavni ridici automat
	STMACHINE			Stm;
	FLASHFS_CMD			Command;

	union {
		// automat ziskani fyzicke adresy stranky
		FLASHFS_PHYSSEARCH_CTX	Phys;
		// automat vyhledavani prazdne stranky
		FLASHFS_EMPTYSEARCH_CTX	Empty;
		// automat pro vypocet CRC
		FLASHFS_CRCCALC_CTX		CRC;
	} HelperCtx;

	union {
		// automat spousteny pri inicializaci
		FLASHFS_INIT_CTX		Init;
		// automat cteni dat z dataflash
		FLASHFS_READ_CTX		Read;
		// automat zapisu dat do dataflash
		FLASHFS_WRITE_CTX		Write;
	} OPCtx;

	// automat cteni/zapisu
	FLASHFS_PAGER_CTX			Pager;

	union {
		FLASHFS_CMD_READ		Read;
		FLASHFS_CMD_WRITE		Write;
	} Cmd;

	// vysledek hledani stranky
	physpage_t		PhysAddr;

#ifdef FLASHFS_TLB
	// vyrovnavaci pamet
	TLB_ITEM		TLB[FLASHFS_TLB_SIZE];
	uint8_t			TLBIndex;
#endif // FLASHFS_TLB_SIZE

	// Buffer pro praci se systemovou strankou
	FLASHFS_SYSPAGE	SysPageBuf;

	// verejny buffer pro cteni infa - vyuziva inicializace a write
	DECL_DMA_STRUCT_ITEM(SPARE_STRUCT, SpareBuf);
	DECL_DMA_STRUCT_ITEM(FLASHFS_PAGEBUF, PageBuffer);
	DECL_DMA_STRUCT_ITEM(FLASHFS_VERIFYBUF, VerifyBuf);	// Buffer pro verifikaci zapisu

} FLASHFS_CTX;

#ifdef FLASHFS_STATISTICS

static inline void FlashFSSetStats(FLASHFS_CTX * const _ctx, FLASHFS_STATS * const _stats)
{
	_ctx->Stats = _stats;
}

static inline void FlashFSStopStats(FLASHFS_CTX * const _ctx)
{
	_ctx->Stats = NULL;
}

#endif //FLASHFS_STATISTICS

void FlashFSInitLoad(FLASHFS_CTX * const _ctx);

bool_t FlashFSInitialize(FLASHFS_CTX * const _ctx, DATAFLASH_CTX * const _dflash, const nvptr_t _startAddr, const nvptr_t _size);

void FlashFSFinalize(FLASHFS_CTX * const _ctx);

#endif /*_FLASHFS_H_*/
