ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/uboot/fs/tffs/file.c b/marvell/uboot/fs/tffs/file.c
new file mode 100755
index 0000000..ea35f82
--- /dev/null
+++ b/marvell/uboot/fs/tffs/file.c
@@ -0,0 +1,706 @@
+/*
+ * file.c
+ *
+ * Include interface about file operation
+ * implementation file.
+ *
+ * Copyright (C) knightray@gmail.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ The file has been modified by antone@marvell.com (see "Marvell fixed")
+ in order to:
+ - achive best write performance
+ - fix cluster allocation boundary cases
+*/
+
+#include "file.h"
+#include "tffs.h"
+#include "common.h"
+#include "debug.h"
+#include "dir.h"
+#include "dirent.h"
+#include "fat.h"
+#include "hai.h"
+
+#ifndef DBG_FILE
+#undef DBG
+#define DBG nulldbg
+#endif
+
+/* private function declaration. */
+
+static BOOL
+_parse_open_mode(
+ IN byte * open_mode,
+ OUT uint32 * popen_mode
+);
+
+static void
+_file_seek(
+ IN tfile_t * pfile,
+ IN uint32 offset
+);
+
+static int32
+_initialize_file(
+ IN tffs_t * ptffs,
+ IN tdir_t * pdir,
+ IN tdir_entry_t * pdir_entry,
+ OUT tfile_t * pfile
+);
+
+static int32
+_get_next_sec(
+ IN tfile_t * pfile
+);
+
+/*----------------------------------------------------------------------------------------------------*/
+
+int32
+TFFS_fopen(
+ IN tffs_handle_t hfs,
+ IN byte * file_path,
+ IN byte * open_mode,
+ OUT tfile_handle_t * phfile)
+{
+ int32 ret;
+ tffs_t * ptffs = (tffs_t *)hfs;
+ byte * fname, * path;
+ byte * dup_file_path;
+ tfile_t * pfile;
+ tdir_t * pdir;
+ tdir_entry_t * pdir_entry;
+
+ if (!hfs || !file_path || !open_mode || !phfile)
+ return ERR_TFFS_INVALID_PARAM;
+
+ ret = TFFS_OK;
+ pfile = (tfile_t *)Malloc(sizeof(tfile_t));
+ dup_file_path = dup_string(file_path);
+ pdir_entry = dirent_malloc();
+ fname = (byte *)Malloc(DNAME_MAX);
+ pfile->secbuf = (ubyte *)Malloc(ptffs->pbs->byts_per_sec);
+ Memset(pfile->secbuf, 0, ptffs->pbs->byts_per_sec);
+
+ path = dup_file_path;
+ if (!divide_path(dup_file_path, fname)) {
+ ret = ERR_TFFS_INVALID_PATH;
+ goto _release;
+ }
+
+ if (!_parse_open_mode(open_mode, &pfile->open_mode)) {
+ ret = ERR_TFFS_INVALID_OPENMODE;
+ goto _release;
+ }
+
+ if ((dir_init(ptffs, path, &pdir)) != DIR_OK) {
+ ret = ERR_TFFS_INVALID_PATH;
+ goto _release;
+ }
+
+ if (dirent_find(pdir, fname, pdir_entry) != DIRENTRY_OK) {
+
+ DBG("%s(): can't find file [%s] at [%s]\n", __FUNCTION__, fname, path);
+ if (pfile->open_mode == OPENMODE_READONLY) {
+ ret = ERR_TFFS_FILE_NOT_EXIST;
+ goto _release;
+ }
+
+ if (!dirent_init(fname, 0, TRUE, pdir_entry)) {
+ ret = ERR_TFFS_INVALID_PATH;
+ goto _release;
+ }
+
+ if ((ret = dir_append_direntry(pdir, pdir_entry)) != DIR_OK) {
+ if (ret == ERR_DIR_NO_FREE_SPACE) {
+ ret = ERR_TFFS_NO_FREE_SPACE;
+ }
+ else {
+ ret = ERR_TFFS_DEVICE_FAIL;
+ }
+ goto _release;
+ }
+ }
+
+ ret = _initialize_file(ptffs, pdir, pdir_entry, pfile);
+ if (ret == FILE_OK) {
+ *phfile = (tfile_handle_t)pfile;
+ /* Marvell fixed: sync the dir sector, so changes to it are
+ possible while the file is open. Re-fetch and update on close */
+ dir_write_sector(pdir);
+ }
+
+_release:
+ Free(fname);
+ Free(dup_file_path);
+ return ret;
+}
+
+int32
+TFFS_rmfile(
+ IN tffs_handle_t hfs,
+ IN byte * file_path)
+{
+ int32 ret;
+ byte * fname, * path;
+ byte * dup_file_path;
+ tdir_t * pdir;
+ tffs_t * ptffs;
+ tdir_entry_t * pdir_entry;
+
+ ret = TFFS_OK;
+
+ if (!hfs || !file_path)
+ return ERR_TFFS_INVALID_PARAM;
+
+ ptffs = (tffs_t *)hfs;
+ dup_file_path = dup_string(file_path);
+ fname = (byte *)Malloc(DNAME_MAX);
+ pdir_entry = dirent_malloc();
+
+ path = dup_file_path;
+ if (!divide_path(dup_file_path, fname)) {
+ ret = ERR_TFFS_INVALID_PATH;
+ goto _release;
+ }
+
+ if ((dir_init(ptffs, path, &pdir)) != DIR_OK) {
+ ret = ERR_TFFS_INVALID_PATH;
+ goto _release;
+ }
+
+ if ((ret = dirent_find(pdir, fname, pdir_entry)) == DIRENTRY_OK) {
+ if (dirent_get_dir_attr(pdir_entry) & ATTR_DIRECTORY) {
+ ret = ERR_TFFS_IS_NOT_A_FILE;
+ goto _release;
+ }
+
+ if (dir_del_direntry(pdir, fname) != DIR_OK) {
+ ret = ERR_TFFS_REMOVE_FILE_FAIL;
+ goto _release;
+ }
+
+ if (fat_free_clus(pdir->ptffs->pfat, dirent_get_clus(pdir_entry)) != FAT_OK) {
+ ret = ERR_TFFS_REMOVE_FILE_FAIL;
+ goto _release;
+ }
+
+ ret = TFFS_OK;
+ }
+ else {
+ ret = ERR_TFFS_NO_SUCH_FILE;
+ goto _release;
+ }
+
+_release:
+ Free(fname);
+ Free(dup_file_path);
+ dirent_release(pdir_entry);
+ dir_destroy(pdir);
+ return ret;
+}
+
+int32
+file_write_bulk(
+ IN tfile_t * pfile,
+ IN uint32 *size,
+ IN ubyte *ptr);
+
+int32
+TFFS_fwrite(
+ IN tfile_handle_t hfile,
+ IN uint32 buflen,
+ IN ubyte * ptr)
+{
+ tfile_t * pfile = (tfile_t *)hfile;
+ tdir_entry_t * pdir_entry;
+ tdir_t * __attribute__ ((unused)) pdir;
+ uint32 __attribute__ ((unused)) write_size;
+ uint32 written_size;
+ uint32 __attribute__ ((unused)) file_size;
+ int32 ret;
+
+ if (!hfile || !ptr)
+ return ERR_TFFS_INVALID_PARAM;
+
+ if (pfile->open_mode == OPENMODE_READONLY)
+ return ERR_TFFS_READONLY;
+
+ pdir = pfile->pdir;
+ pdir_entry = pfile->pdir_entry;
+ file_size = dirent_get_file_size(pdir_entry);
+ write_size = buflen;
+ written_size = 0;
+
+ while (written_size < buflen) {
+ uint32 write_once_size;
+ uint32 remsize = buflen - written_size;
+
+ /* Marvell fixed */
+ if (!pfile->cur_sec_offset && (remsize>pfile->ptffs->pbs->byts_per_sec*2)) {
+ ret = file_write_bulk(pfile, &remsize, ptr + written_size);
+ if (ret != FILE_OK)
+ break; /* ret translated below is not used anyway */
+ written_size = buflen - remsize;
+ /* Marvell fixed: detection for new cluster is
+ based on cur_sec==sec_per_clus - automatic */
+ pfile->cur_sec++; /* next sector to write to */
+ continue; /* handle the remaining bytes if any */
+ }
+
+ write_once_size = min(pfile->ptffs->pbs->byts_per_sec - pfile->cur_sec_offset,
+ remsize);
+
+ Memcpy(pfile->secbuf + pfile->cur_sec_offset, ptr + written_size,
+ write_once_size);
+ written_size += write_once_size;
+ pfile->cur_sec_offset += write_once_size;
+
+ if (pfile->cur_sec_offset == pfile->ptffs->pbs->byts_per_sec) {
+ ret = file_write_sector(pfile);
+ if (ret == FILE_OK) {
+ /* Marvell fixed: detectionfor new cluster is
+ based on cur_sec==sec_per_clus - automatic */
+ pfile->cur_sec++; /* next sector to write to */
+ pfile->cur_sec_offset = 0;
+ }
+ else if (ret == ERR_FILE_NO_FREE_CLUSTER) {
+ ret = ERR_TFFS_NO_FREE_SPACE;
+ break;
+ }
+ else {
+ ret = ERR_TFFS_DEVICE_FAIL;
+ break;
+ }
+ }
+ }
+
+ /* Marvell fixed: Do not update on error, even if(written>0) */
+ if ((written_size > 0) && (ret == FILE_OK)) {
+ dirent_update_wrt_time(pdir_entry);
+ dirent_update_wrt_date(pdir_entry);
+ pfile->file_size += written_size;
+ }
+
+ return written_size;
+}
+
+
+
+int32
+TFFS_fread(
+ IN tfile_handle_t hfile,
+ IN uint32 buflen,
+ OUT ubyte * ptr)
+{
+ tfile_t * pfile = (tfile_t *)hfile;
+ tdir_entry_t * pdir_entry;
+ tdir_t * __attribute__ ((unused)) pdir;
+ uint32 read_size;
+ uint32 readin_size;
+ uint32 file_size;
+ int32 ret;
+
+ if (!hfile || !ptr)
+ return ERR_TFFS_INVALID_PARAM;
+
+ pdir = pfile->pdir;
+ pdir_entry = pfile->pdir_entry;
+ file_size = dirent_get_file_size(pdir_entry);
+ read_size = min(buflen, file_size - pfile->cur_fp_offset);
+ readin_size = 0;
+
+ while (readin_size < read_size) {
+
+ if (pfile->cur_sec_offset + (read_size - readin_size) >= pfile->ptffs->pbs->byts_per_sec) {
+
+ Memcpy(ptr + readin_size, pfile->secbuf + pfile->cur_sec_offset,
+ pfile->ptffs->pbs->byts_per_sec - pfile->cur_sec_offset);
+ readin_size += pfile->ptffs->pbs->byts_per_sec - pfile->cur_sec_offset;
+
+ ret = file_read_sector(pfile);
+ if (ret == FILE_OK) {
+ pfile->cur_sec_offset = 0;
+ continue;
+ }
+ else if (ret == ERR_FILE_EOF) {
+ //WARN("%s(): unexpect file end at %d\n", __FUNCTION__, __LINE__);
+ break;
+ }
+ else {
+ ERR("%s(): read file data sector failed at %d\n", __FUNCTION__, __LINE__);
+ ret = ERR_TFFS_DEVICE_FAIL;
+ goto _end;
+ }
+ }
+ else {
+ Memcpy(ptr + readin_size, pfile->secbuf + pfile->cur_sec_offset,
+ read_size - readin_size);
+ pfile->cur_sec_offset += (read_size - readin_size);
+ readin_size += (read_size - readin_size);
+ }
+ }
+
+ if (readin_size > 0) {
+ dirent_update_lst_acc_date(pdir_entry);
+ pfile->cur_fp_offset += readin_size;
+ ret = readin_size;
+ }
+ else {
+ ret = ERR_TFFS_FILE_EOF;
+ }
+
+_end:
+ return ret;
+}
+
+/* Marvell:
+ * Flush file buffer, update directory with size
+ * (like TFFS_fclose but without destroy)
+ */
+int32
+TFFS_fflush(
+ IN tfile_handle_t hfile)
+{
+ int32 ret;
+ tfile_t * pfile = (tfile_t *)hfile;
+
+ if (!hfile)
+ return ERR_TFFS_INVALID_PARAM;
+
+ ret = TFFS_OK;
+ /* Marvell fixed */
+ if (pfile->cur_sec_offset)
+ if (file_write_sector(pfile) != FILE_OK) {
+ return ERR_TFFS_DEVICE_FAIL;
+ }
+
+ dirent_set_file_size(pfile->pdir_entry, pfile->file_size);
+ if (dir_update_direntry(pfile->pdir, pfile->pdir_entry) != DIR_OK) {
+ ret = ERR_TFFS_DEVICE_FAIL;
+ }
+
+ return ret;
+}
+
+int32
+TFFS_fclose(
+ IN tfile_handle_t hfile)
+{
+ int32 ret;
+ tfile_t * pfile = (tfile_t *)hfile;
+
+ if (!hfile)
+ return ERR_TFFS_INVALID_PARAM;
+
+ ret = TFFS_fflush(hfile);
+
+ dir_destroy(pfile->pdir);
+ dirent_release(pfile->pdir_entry);
+ Free(pfile->secbuf);
+ Free(pfile);
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------------------------------*/
+
+int32
+file_read_sector(
+ IN tfile_t * pfile)
+{
+ int32 ret;
+ tffs_t * ptffs = pfile->ptffs;
+
+ if (pfile->cur_clus == 0) {
+ ret = ERR_FILE_EOF;
+ }
+ else {
+ if (fat_get_next_sec(ptffs->pfat, &pfile->cur_clus, &pfile->cur_sec)) {
+ if (cache_readsector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
+ pfile->secbuf) == CACHE_OK) {
+ ret = FILE_OK;
+ }
+ else {
+ ret = ERR_FILE_DEVICE_FAIL;
+ }
+ }
+ else {
+ ret = ERR_FILE_EOF;
+ }
+ }
+
+ return ret;
+}
+
+int32
+file_write_sector(
+ IN tfile_t * pfile)
+{
+ int32 ret;
+ tffs_t * ptffs = pfile->ptffs;
+ tdir_entry_t * pdir_entry = pfile->pdir_entry;
+ uint32 new_clus;
+
+ ret = FILE_OK;
+ if (pfile->cur_clus == 0) {
+ int32 fatret;
+
+ if ((fatret = fat_malloc_clus(ptffs->pfat, FAT_INVALID_CLUS, &new_clus)) == FAT_OK) {
+ DBG("%s: new_clus = %d\n", __FUNCTION__, new_clus);
+ dirent_set_clus(pdir_entry, new_clus);
+ pfile->cur_clus = new_clus;
+ pfile->cur_sec = 0;
+ }
+ else if (fatret == ERR_FAT_NO_FREE_CLUSTER) {
+ return ERR_FILE_NO_FREE_CLUSTER;
+ }
+ else {
+ return ERR_FILE_DEVICE_FAIL;
+ }
+ }
+ /* Marvell fixed */
+ if (pfile->cur_sec >= ptffs->pbs->sec_per_clus) {
+ /* need a new cluster */
+ ret = _get_next_sec(pfile);
+ if (ret != FILE_OK) {
+ ERR("%s(): get next sector failed at %d with ret = %d\n", __FUNCTION__, __LINE__, ret);
+ return ret;
+ }
+ }
+
+ if (ret == FILE_OK) {
+ if (cache_writesector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
+ pfile->secbuf) != CACHE_OK) {
+ ret = ERR_FILE_DEVICE_FAIL;
+ }
+ }
+
+ return ret;
+}
+/*
+ * Copyright (C) 2008 Marvell International Ltd. (antone@marvell.com)
+ */
+
+
+/* Marvell fixed - new function for better write throughput */
+int32
+file_write_bulk(
+ IN tfile_t * pfile,
+ IN uint32 *size,
+ IN ubyte *ptr)
+{
+ int32 ret;
+ tffs_t * ptffs = pfile->ptffs;
+ tdir_entry_t * pdir_entry = pfile->pdir_entry;
+ uint32 cur_clus;
+ uint32 nsectors = *size/pfile->ptffs->pbs->byts_per_sec;
+ uint32 remsectors = nsectors;
+ uint32 gotsectors;
+ uint32 len=0;
+ uint32 cur_sec;
+ ret = FILE_OK;
+
+ if (pfile->cur_clus == 0) {
+ int32 fatret;
+
+ if ((fatret = fat_malloc_clus(ptffs->pfat, FAT_INVALID_CLUS, &cur_clus)) == FAT_OK) {
+ DBG("%s: new_clus = %d\n", __FUNCTION__, cur_clus);
+ dirent_set_clus(pdir_entry, cur_clus);
+ pfile->cur_clus = cur_clus;
+ pfile->cur_sec = 0;
+ }
+ else if (fatret == ERR_FAT_NO_FREE_CLUSTER) {
+ return ERR_FILE_NO_FREE_CLUSTER;
+ }
+ else {
+ return ERR_FILE_DEVICE_FAIL;
+ }
+ }
+
+ if (pfile->cur_sec >= ptffs->pbs->sec_per_clus) {
+ /* need a new cluster */
+ ret = _get_next_sec(pfile);
+ if (ret != FILE_OK) {
+ ERR("%s(): get next sector failed at %d with ret = %d\n", __FUNCTION__, __LINE__, ret);
+ return ret;
+ }
+ }
+
+ cur_sec = pfile->cur_sec;
+ cur_clus = pfile->cur_clus;
+ gotsectors = 0;
+ for (remsectors = nsectors; remsectors; ) {
+ uint32 ns = ptffs->pbs->sec_per_clus - pfile->cur_sec;
+ uint32 prev_clus = pfile->cur_clus;
+ if (ns >= remsectors) {
+ ns = remsectors;
+ pfile->cur_sec += ns - 1; /* used as of now */
+ }
+ else {
+ pfile->cur_sec += ns - 1;
+ ret = _get_next_sec(pfile);
+ if (ret != FILE_OK) {
+ ERR("%s(): get next sector failed at %d with ret = %d\n", __FUNCTION__, __LINE__, ret);
+ ret = ERR_FILE_NO_FREE_CLUSTER;
+ break;
+ }
+ }
+ gotsectors += ns;
+ /* allocate clusters as long as the clusters are continuos */
+ if ((pfile->cur_clus != (prev_clus + 1)) || (ns == remsectors)) {
+ ret = HAI_writesectors(ptffs->hdev, clus2sec(ptffs, cur_clus) + cur_sec,
+ ptr + len, gotsectors);
+ if (ret != HAI_OK) {
+ ret = ERR_FILE_DEVICE_FAIL;
+ break;
+ }
+ len += gotsectors*pfile->ptffs->pbs->byts_per_sec;
+ /* Setup the next continuos chunk start point */
+ cur_clus = pfile->cur_clus;
+ cur_sec = pfile->cur_sec; /* relevant if remsectors>0 */
+ gotsectors = 0;
+ }
+ remsectors -= ns;
+ }
+ *size -= (nsectors-remsectors)*pfile->ptffs->pbs->byts_per_sec;
+ return ret;
+}
+/*
+ * Copyright (C) knightray@gmail.com
+ */
+
+
+/*----------------------------------------------------------------------------------------------------*/
+
+static int32
+_initialize_file(
+ IN tffs_t * ptffs,
+ IN tdir_t * pdir,
+ IN tdir_entry_t * pdir_entry,
+ OUT tfile_t * pfile)
+{
+ int32 ret;
+
+ pfile->ptffs = ptffs;
+ pfile->pdir = pdir;
+ pfile->pdir_entry = pdir_entry;
+ ret = FILE_OK;
+
+ pfile->start_clus = dirent_get_clus(pdir_entry);
+ pfile->file_size = dirent_get_file_size(pdir_entry);
+ pfile->cur_clus = pfile->start_clus;
+ pfile->cur_sec = 0;
+ pfile->cur_sec_offset = 0;
+ pfile->cur_fp_offset = 0;
+
+ if (pfile->open_mode == OPENMODE_APPEND) {
+ pfile->cur_fp_offset = dirent_get_file_size(pdir_entry);
+
+ _file_seek(pfile, pfile->cur_fp_offset);
+ if (pfile->cur_clus != 0) {
+ if (cache_readsector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
+ pfile->secbuf) != CACHE_OK) {
+ ret = ERR_TFFS_DEVICE_FAIL;
+ }
+ }
+ }
+ else if (pfile->open_mode == OPENMODE_WRITE) {
+ if (pfile->start_clus != 0) {
+ if (fat_free_clus(ptffs->pfat, pfile->start_clus) != FAT_OK) {
+ ERR("%s(): %d fat_free_clus failed.\n", __FUNCTION__, __LINE__);
+ ret = ERR_TFFS_FAT;
+ }
+ }
+
+ dirent_set_file_size(pdir_entry , 0);
+ dirent_set_clus(pdir_entry, 0);
+
+ pfile->file_size = 0;
+ pfile->cur_clus = 0;
+ pfile->cur_sec = 0;
+ pfile->cur_sec_offset = 0;
+ pfile->cur_fp_offset = 0;
+ }
+ else {
+ if (pfile->cur_clus != 0) {
+ if (cache_readsector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
+ pfile->secbuf) != CACHE_OK) {
+ ret = ERR_TFFS_DEVICE_FAIL;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void
+_file_seek(
+ IN tfile_t * pfile,
+ IN uint32 offset)
+{
+ int32 cur_offset;
+
+ cur_offset = offset;
+ while (cur_offset - pfile->ptffs->pbs->byts_per_sec > 0 &&
+ fat_get_next_sec(pfile->ptffs->pfat, &pfile->cur_clus, &pfile->cur_sec)) {
+ cur_offset -= pfile->ptffs->pbs->byts_per_sec;
+ }
+ pfile->cur_sec_offset = cur_offset;
+}
+
+static int32
+_get_next_sec(
+ IN tfile_t * pfile)
+{
+ int32 ret;
+ tffs_t * ptffs = pfile->ptffs;
+ uint32 new_clus;
+
+ ret = FILE_OK;
+
+ if (pfile->cur_sec + 1 < ptffs->pbs->sec_per_clus) {
+ pfile->cur_sec++;
+ }
+ else {
+ int32 fatret;
+
+ if ((fatret = fat_malloc_clus(ptffs->pfat, pfile->cur_clus, &new_clus)) == FAT_OK) {
+ pfile->cur_clus = new_clus;
+ pfile->cur_sec = 0;
+ }
+ else if (fatret == ERR_FAT_NO_FREE_CLUSTER) {
+ ret = ERR_FILE_NO_FREE_CLUSTER;
+ }
+ else {
+ ret = ERR_FILE_DEVICE_FAIL;
+ }
+ }
+
+ return ret;
+}
+
+static BOOL
+_parse_open_mode(
+ IN byte * open_mode,
+ OUT uint32 * popen_mode)
+{
+ if (!Strcmp(open_mode, "r")) {
+ *popen_mode = OPENMODE_READONLY;
+ }
+ else if (!Strcmp(open_mode, "w")) {
+ *popen_mode = OPENMODE_WRITE;
+ }
+ else if (!Strcmp(open_mode, "a")) {
+ *popen_mode = OPENMODE_APPEND;
+ }
+ else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+