/*
 *  Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SDFAT_API_H
#define _SDFAT_API_H

#include "config.h"
#include "sdfat_fs.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/*----------------------------------------------------------------------*/
/*  Configure Constant & Macro Definitions                              */
/*----------------------------------------------------------------------*/
/* cache size (in number of sectors)                */
/* (should be an exponential value of 2)            */
#define FAT_CACHE_SIZE          128
#define FAT_CACHE_HASH_SIZE     64
#define BUF_CACHE_SIZE          256
#define BUF_CACHE_HASH_SIZE     64

/* Read-ahead related                                */
/* First config vars. should be pow of 2             */
#define FCACHE_MAX_RA_SIZE	(PAGE_SIZE)
#define DCACHE_MAX_RA_SIZE	(128*1024)

/*----------------------------------------------------------------------*/
/*  Constant & Macro Definitions                                        */
/*----------------------------------------------------------------------*/
/* type values */
#define TYPE_UNUSED             0x0000
#define TYPE_DELETED            0x0001
#define TYPE_INVALID            0x0002
#define TYPE_CRITICAL_PRI       0x0100
#define TYPE_BITMAP             0x0101
#define TYPE_UPCASE             0x0102
#define TYPE_VOLUME             0x0103
#define TYPE_DIR                0x0104
#define TYPE_FILE               0x011F
#define TYPE_SYMLINK            0x015F
#define TYPE_CRITICAL_SEC       0x0200
#define TYPE_STREAM             0x0201
#define TYPE_EXTEND             0x0202
#define TYPE_ACL                0x0203
#define TYPE_BENIGN_PRI         0x0400
#define TYPE_GUID               0x0401
#define TYPE_PADDING            0x0402
#define TYPE_ACLTAB             0x0403
#define TYPE_BENIGN_SEC         0x0800
#define TYPE_ALL                0x0FFF

/* eio values */
#define SDFAT_EIO_NONE		(0x00000000)
#define SDFAT_EIO_READ		(0x00000001)
#define SDFAT_EIO_WRITE		(0x00000002)
#define SDFAT_EIO_BDI		(0x00000004)

/* modes for volume allocation unit status */
#define VOL_AU_STAT_TOTAL	(0)
#define VOL_AU_STAT_CLEAN	(1)
#define VOL_AU_STAT_FULL	(2)

/*----------------------------------------------------------------------*/
/*  NLS Type Definitions                                                    */
/*----------------------------------------------------------------------*/

/* DOS name structure */
typedef struct {
	u8       name[DOS_NAME_LENGTH];
	u8       name_case;
} DOS_NAME_T;

/* unicode name structure */
typedef struct {
	u16      name[MAX_NAME_LENGTH+3]; /* +3 for null and for converting */
	u16      name_hash;
	u8       name_len;
} UNI_NAME_T;

/*----------------------------------------------------------------------*/
/*  Type Definitions                                                    */
/*----------------------------------------------------------------------*/
/* should be merged it to DATE_TIME_T */
typedef union {
	struct {
		u8 off : 7;
		u8 valid : 1;
	};
	u8 value;
} TIMEZONE_T;

typedef struct {
	u16      sec;        /* 0 ~ 59               */
	u16      min;        /* 0 ~ 59               */
	u16      hour;       /* 0 ~ 23               */
	u16      day;        /* 1 ~ 31               */
	u16      mon;        /* 1 ~ 12               */
	u16      year;       /* 0 ~ 127 (since 1980) */
	TIMEZONE_T tz;
} TIMESTAMP_T;

typedef struct {
	u16      Year;
	u16      Month;
	u16      Day;
	u16      Hour;
	u16      Minute;
	u16      Second;
	u16      MilliSecond;
	TIMEZONE_T Timezone;
} DATE_TIME_T;

typedef struct {
	u64      Offset;    // start sector number of the partition
	u64      Size;      // in sectors
} PART_INFO_T;

typedef struct {
	u32      SecSize;    // sector size in bytes
	u64      DevSize;    // block device size in sectors
} DEV_INFO_T;

typedef struct {
	u32      FatType;
	u32      ClusterSize;
	u32      NumClusters;
	u32      FreeClusters;
	u32      UsedClusters;
} VOL_INFO_T;

/* directory structure */
typedef struct {
	u32      dir;
	u32      size;
	u8       flags;
} CHAIN_T;

/* hint structure */
typedef struct {
	u32      clu;
	union {
		u32 off;     // cluster offset
		s32 eidx;    // entry index
	};
} HINT_T;

typedef struct {
	spinlock_t cache_lru_lock;
	struct list_head cache_lru;
	s32 nr_caches;
	u32 cache_valid_id;	// for avoiding the race between alloc and free
} EXTENT_T;

/* first empty entry hint information */
typedef struct {
	s32 eidx;		// entry index of a directory
	s32 count;		// count of continuous empty entry
	CHAIN_T cur;		// the cluster that first empty slot exists in
} HINT_FEMP_T;

/* file id structure */
typedef struct {
	CHAIN_T dir;
	s32 entry;
	u32 type;
	u32 attr;
	u32 start_clu;
	u64 size;
	u8  flags;
	u8  reserved[3];	// padding
	u32 version;		// the copy of low 32bit of i_version to check the validation of hint_stat
	s64 rwoffset;		// file offset or dentry index for readdir
	EXTENT_T extent;	// extent cache for a file
	HINT_T	hint_bmap;	// hint for cluster last accessed
	HINT_T	hint_stat;	// hint for entry index we try to lookup next time
	HINT_FEMP_T hint_femp;	// hint for first empty entry
} FILE_ID_T;

typedef struct {
	s8 *lfn;
	s8 *sfn;
	s32 lfnbuf_len;	//usally MAX_UNINAME_BUF_SIZE
	s32 sfnbuf_len; //usally MAX_DOSNAME_BUF_SIZE, used only for vfat, not for exfat
} DENTRY_NAMEBUF_T;

typedef struct {
	u32 Attr;
	u64 Size;
	u32 NumSubdirs;
	DATE_TIME_T CreateTimestamp;
	DATE_TIME_T ModifyTimestamp;
	DATE_TIME_T AccessTimestamp;
	DENTRY_NAMEBUF_T NameBuf;
} DIR_ENTRY_T;

/* cache information */
typedef struct __cache_entry {
	struct __cache_entry *next;
	struct __cache_entry *prev;
	struct {
		struct __cache_entry *next;
		struct __cache_entry *prev;
	} hash;
	u64 sec;
	u32 flag;
	struct buffer_head   *bh;
} cache_ent_t;

/*----------------------------------------------------------------------*/
/*  Type Definitions : Wrapper & In-Core                                */
/*----------------------------------------------------------------------*/
typedef struct __FATENT_OPS_T {
	s32 (*ent_get)(struct super_block *sb, u32 loc, u32 *content);
	s32 (*ent_set)(struct super_block *sb, u32 loc, u32 content);
} FATENT_OPS_T;

typedef struct {
	s32      (*alloc_cluster)(struct super_block *, u32, CHAIN_T *, s32);
	s32      (*free_cluster)(struct super_block *, CHAIN_T *, s32);
	s32      (*count_used_clusters)(struct super_block *, u32 *);
	s32      (*init_dir_entry)(struct super_block *, CHAIN_T *, s32, u32, u32, u64);
	s32      (*init_ext_entry)(struct super_block *, CHAIN_T *, s32, s32, UNI_NAME_T *, DOS_NAME_T *);
	s32      (*find_dir_entry)(struct super_block *, FILE_ID_T *, CHAIN_T *, UNI_NAME_T *, s32, DOS_NAME_T *, u32);
	s32      (*delete_dir_entry)(struct super_block *, CHAIN_T *, s32, s32, s32);
	void     (*get_uniname_from_ext_entry)(struct super_block *, CHAIN_T *, s32, u16 *);
	s32      (*count_ext_entries)(struct super_block *, CHAIN_T *, s32, DENTRY_T *);
	s32      (*calc_num_entries)(UNI_NAME_T *);
	s32      (*check_max_dentries)(FILE_ID_T *);
	u32      (*get_entry_type)(DENTRY_T *);
	void     (*set_entry_type)(DENTRY_T *, u32);
	u32      (*get_entry_attr)(DENTRY_T *);
	void     (*set_entry_attr)(DENTRY_T *, u32);
	u8       (*get_entry_flag)(DENTRY_T *);
	void     (*set_entry_flag)(DENTRY_T *, u8);
	u32      (*get_entry_clu0)(DENTRY_T *);
	void     (*set_entry_clu0)(DENTRY_T *, u32);
	u64      (*get_entry_size)(DENTRY_T *);
	void     (*set_entry_size)(DENTRY_T *, u64);
	void     (*get_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8);
	void     (*set_entry_time)(DENTRY_T *, TIMESTAMP_T *, u8);
	u32      (*get_au_stat)(struct super_block *, s32);
} FS_FUNC_T;

typedef struct __FS_INFO_T {
	s32	 bd_opened;              // opened or not
	u32      vol_type;               // volume FAT type
	u32      vol_id;                 // volume serial number
	u64      num_sectors;            // num of sectors in volume
	u32      num_clusters;           // num of clusters in volume
	u32      cluster_size;           // cluster size in bytes
	u32      cluster_size_bits;
	u32      sect_per_clus;        // cluster size in sectors
	u32      sect_per_clus_bits;
	u64      FAT1_start_sector;      // FAT1 start sector
	u64      FAT2_start_sector;      // FAT2 start sector
	u64      root_start_sector;      // root dir start sector
	u64      data_start_sector;      // data area start sector
	u32      num_FAT_sectors;        // num of FAT sectors
	u32      root_dir;               // root dir cluster
	u32      dentries_in_root;       // num of dentries in root dir
	u32      dentries_per_clu;       // num of dentries per cluster
	u32      vol_flag;               // volume dirty flag
	struct buffer_head *pbr_bh;      // buffer_head of PBR sector

	u32      map_clu;                // allocation bitmap start cluster
	u32      map_sectors;            // num of allocation bitmap sectors
	struct buffer_head **vol_amap;      // allocation bitmap

	u16      **vol_utbl;               // upcase table

	u32      clu_srch_ptr;           // cluster search pointer
	u32      used_clusters;          // number of used clusters

	u32      prev_eio;            // block device operation error flag

	FS_FUNC_T   *fs_func;
	FATENT_OPS_T   *fatent_ops;

	s32       reserved_clusters;  // # of reserved clusters (DA)
	void        *amap;                  // AU Allocation Map

	/* fat cache */
	struct {
		cache_ent_t pool[FAT_CACHE_SIZE];
		cache_ent_t lru_list;
		cache_ent_t hash_list[FAT_CACHE_HASH_SIZE];
	} fcache;

	/* meta cache */
	struct {
		cache_ent_t pool[BUF_CACHE_SIZE];
		cache_ent_t lru_list;
		cache_ent_t keep_list;        // CACHEs in this list will not be kicked by normal lru operations
		cache_ent_t hash_list[BUF_CACHE_HASH_SIZE];
	} dcache;
} FS_INFO_T;

/*======================================================================*/
/*                                                                      */
/*                     API FUNCTION DECLARATIONS                        */
/*                  (CHANGE THIS PART IF REQUIRED)                      */
/*                                                                      */
/*======================================================================*/

/*----------------------------------------------------------------------*/
/*  External Function Declarations                                      */
/*----------------------------------------------------------------------*/

/* file system initialization & shutdown functions */
s32 fsapi_init(void);
s32 fsapi_shutdown(void);

/* volume management functions */
s32 fsapi_mount(struct super_block *sb);
s32 fsapi_umount(struct super_block *sb);
s32 fsapi_statfs(struct super_block *sb, VOL_INFO_T *info);
s32 fsapi_sync_fs(struct super_block *sb, s32 do_sync);
s32 fsapi_set_vol_flags(struct super_block *sb, u16 new_flag, s32 always_sync);

/* file management functions */
s32 fsapi_lookup(struct inode *inode, u8 *path, FILE_ID_T *fid);
s32 fsapi_create(struct inode *inode, u8 *path, u8 mode, FILE_ID_T *fid);
s32 fsapi_read_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *rcount);
s32 fsapi_write_link(struct inode *inode, FILE_ID_T *fid, void *buffer, u64 count, u64 *wcount);
s32 fsapi_remove(struct inode *inode, FILE_ID_T *fid); /* unlink and truncate */
s32 fsapi_truncate(struct inode *inode, u64 old_size, u64 new_size);
s32 fsapi_rename(struct inode *old_parent_inode, FILE_ID_T *fid,
		struct inode *new_parent_inode, struct dentry *new_dentry);
s32 fsapi_unlink(struct inode *inode, FILE_ID_T *fid);
s32 fsapi_read_inode(struct inode *inode, DIR_ENTRY_T *info);
s32 fsapi_write_inode(struct inode *inode, DIR_ENTRY_T *info, int sync);
s32 fsapi_map_clus(struct inode *inode, u32 clu_offset, u32 *clu, int dest);
s32 fsapi_reserve_clus(struct inode *inode);

/* directory management functions */
s32 fsapi_mkdir(struct inode *inode, u8 *path, FILE_ID_T *fid);
s32 fsapi_readdir(struct inode *inode, DIR_ENTRY_T *dir_entry);
s32 fsapi_rmdir(struct inode *inode, FILE_ID_T *fid);

/* FAT & buf cache functions */
s32 fsapi_cache_flush(struct super_block *sb, int do_sync);
s32 fsapi_cache_release(struct super_block *sb);

/* extra info functions */
u32 fsapi_get_au_stat(struct super_block *sb, s32 mode);

/* extent cache functions */
void fsapi_invalidate_extent(struct inode *inode);

/* bdev management */
s32 fsapi_check_bdi_valid(struct super_block *sb);

#ifdef CONFIG_SDFAT_DFR
/*----------------------------------------------------------------------*/
/*  Defragmentation related                                             */
/*----------------------------------------------------------------------*/

s32 fsapi_dfr_get_info(struct super_block *sb, void *arg);

s32 fsapi_dfr_scan_dir(struct super_block *sb, void *args);

s32 fsapi_dfr_validate_clus(struct inode *inode, void *chunk, int skip_prev);
s32 fsapi_dfr_reserve_clus(struct super_block *sb, s32 nr_clus);
s32 fsapi_dfr_mark_ignore(struct super_block *sb, unsigned int clus);
void fsapi_dfr_unmark_ignore_all(struct super_block *sb);

s32 fsapi_dfr_map_clus(struct inode *inode, u32 clu_offset, u32 *clu);
void fsapi_dfr_writepage_endio(struct page *page);

void fsapi_dfr_update_fat_prev(struct super_block *sb, int force);
void fsapi_dfr_update_fat_next(struct super_block *sb);
void fsapi_dfr_check_discard(struct super_block *sb);
void fsapi_dfr_free_clus(struct super_block *sb, u32 clus);

s32 fsapi_dfr_check_dfr_required(struct super_block *sb, int *totalau, int *cleanau, int *fullau);
s32 fsapi_dfr_check_dfr_on(struct inode *inode, loff_t start, loff_t end, s32 cancel, const char *caller);


#ifdef CONFIG_SDFAT_DFR_DEBUG
void fsapi_dfr_spo_test(struct super_block *sb, int flag, const char *caller);
#endif	/* CONFIG_SDFAT_DFR_DEBUG */

#endif	/* CONFIG_SDFAT_DFR */


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* _SDFAT_API_H */

/* end of api.h */