/**1* @file lv_ufs.c2* Implementation of RAM file system which do NOT support directories.3* The API is compatible with the lv_fs_int module.4*/56/*********************7* INCLUDES8*********************/9#include "lv_ufs.h"10#if USE_LV_FILESYSTEM1112#include "lv_ll.h"13#include <string.h>14#include <stdio.h>15#include <errno.h>16#include "lv_gc.h"1718#if defined(LV_GC_INCLUDE)19# include LV_GC_INCLUDE20#endif /* LV_ENABLE_GC */212223/*********************24* DEFINES25*********************/2627/**********************28* TYPEDEFS29**********************/3031/**********************32* STATIC PROTOTYPES33**********************/34static lv_ufs_ent_t * lv_ufs_ent_get(const char * fn);35static lv_ufs_ent_t * lv_ufs_ent_new(const char * fn);3637/**********************38* STATIC VARIABLES39**********************/40static bool inited = false;4142/**********************43* MACROS44**********************/4546/**********************47* GLOBAL FUNCTIONS48**********************/4950/**51* Create a driver for ufs and initialize it.52*/53void lv_ufs_init(void)54{55lv_ll_init(&LV_GC_ROOT(_lv_file_ll), sizeof(lv_ufs_ent_t));5657lv_fs_drv_t ufs_drv;58memset(&ufs_drv, 0, sizeof(lv_fs_drv_t)); /*Initialization*/5960ufs_drv.file_size = sizeof(lv_ufs_file_t);61ufs_drv.rddir_size = sizeof(lv_ufs_dir_t);62ufs_drv.letter = UFS_LETTER;63ufs_drv.ready = lv_ufs_ready;6465ufs_drv.open = lv_ufs_open;66ufs_drv.close = lv_ufs_close;67ufs_drv.remove = lv_ufs_remove;68ufs_drv.read = lv_ufs_read;69ufs_drv.write = lv_ufs_write;70ufs_drv.seek = lv_ufs_seek;71ufs_drv.tell = lv_ufs_tell;72ufs_drv.size = lv_ufs_size;73ufs_drv.trunc = lv_ufs_trunc;74ufs_drv.free = lv_ufs_free;7576ufs_drv.dir_open = lv_ufs_dir_open;77ufs_drv.dir_read = lv_ufs_dir_read;78ufs_drv.dir_close = lv_ufs_dir_close;7980lv_fs_add_drv(&ufs_drv);8182inited = true;83}8485/**86* Give the state of the ufs87* @return true if ufs is initialized and can be used else false88*/89bool lv_ufs_ready(void)90{91return inited;92}9394/**95* Open a file in ufs96* @param file_p pointer to a lv_ufs_file_t variable97* @param fn name of the file. There are no directories so e.g. "myfile.txt"98* @param mode element of 'fs_mode_t' enum or its 'OR' connection (e.g. FS_MODE_WR | FS_MODE_RD)99* @return LV_FS_RES_OK: no error, the file is opened100* any error from lv__fs_res_t enum101*/102lv_fs_res_t lv_ufs_open(void * file_p, const char * fn, lv_fs_mode_t mode)103{104lv_ufs_file_t * fp = file_p; /*Convert type*/105lv_ufs_ent_t * ent = lv_ufs_ent_get(fn);106107fp->ent = NULL;108109/*If the file not exists ...*/110if(ent == NULL) {111if((mode & LV_FS_MODE_WR) != 0) { /*Create the file if opened for write*/112ent = lv_ufs_ent_new(fn);113if(ent == NULL) return LV_FS_RES_FULL; /*No space for the new file*/114} else {115return LV_FS_RES_NOT_EX; /*Can not read not existing file*/116}117}118119/*Can not write already opened and const data files*/120if((mode & LV_FS_MODE_WR) != 0) {121if(ent->oc != 0) return LV_FS_RES_LOCKED;122if(ent->const_data != 0) return LV_FS_RES_DENIED;123}124125/*No error, the file can be opened*/126fp->ent = ent;127fp->ar = mode & LV_FS_MODE_RD ? 1 : 0;128fp->aw = mode & LV_FS_MODE_WR ? 1 : 0;129fp->rwp = 0;130ent->oc ++;131132return LV_FS_RES_OK;133}134135136/**137* Create a file with a constant data138* @param fn name of the file (directories are not supported)139* @param const_p pointer to a constant data140* @param len length of the data pointed by 'const_p' in bytes141* @return LV_FS_RES_OK: no error, the file is read142* any error from lv__fs_res_t enum143*/144lv_fs_res_t lv_ufs_create_const(const char * fn, const void * const_p, uint32_t len)145{146lv_ufs_file_t file;147lv_fs_res_t res;148149/*Error if the file already exists*/150res = lv_ufs_open(&file, fn, LV_FS_MODE_RD);151if(res == LV_FS_RES_OK) {152lv_ufs_close(&file);153return LV_FS_RES_DENIED;154}155156lv_ufs_close(&file);157158res = lv_ufs_open(&file, fn, LV_FS_MODE_WR);159if(res != LV_FS_RES_OK) return res;160161lv_ufs_ent_t * ent = file.ent;162163if(ent->data_d != NULL) return LV_FS_RES_DENIED;164165ent->data_d = (void *) const_p;166ent->size = len;167ent->const_data = 1;168169res = lv_ufs_close(&file);170if(res != LV_FS_RES_OK) return res;171172return LV_FS_RES_OK;173}174175/**176* Close an opened file177* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open)178* @return LV_FS_RES_OK: no error, the file is read179* any error from lv__fs_res_t enum180*/181lv_fs_res_t lv_ufs_close(void * file_p)182{183lv_ufs_file_t * fp = file_p; /*Convert type*/184185if(fp->ent == NULL) return LV_FS_RES_OK;186187/*Decrement the Open counter*/188if(fp->ent->oc > 0) {189fp->ent->oc--;190}191192return LV_FS_RES_OK;193}194195/**196* Remove a file. The file can not be opened.197* @param fn '\0' terminated string198* @return LV_FS_RES_OK: no error, the file is removed199* LV_FS_RES_DENIED: the file was opened, remove failed200*/201lv_fs_res_t lv_ufs_remove(const char * fn)202{203lv_ufs_ent_t * ent = lv_ufs_ent_get(fn);204if(ent == NULL) return LV_FS_RES_DENIED; /*File not exists*/205206/*Can not be deleted is opened*/207if(ent->oc != 0) return LV_FS_RES_DENIED;208209lv_ll_rem(&LV_GC_ROOT(_lv_file_ll), ent);210lv_mem_free(ent->fn_d);211ent->fn_d = NULL;212if(ent->const_data == 0) {213lv_mem_free(ent->data_d);214ent->data_d = NULL;215}216217lv_mem_free(ent);218219return LV_FS_RES_OK;220}221222/**223* Read data from an opened file224* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open )225* @param buf pointer to a memory block where to store the read data226* @param btr number of Bytes To Read227* @param br the real number of read bytes (Byte Read)228* @return LV_FS_RES_OK: no error, the file is read229* any error from lv__fs_res_t enum230*/231lv_fs_res_t lv_ufs_read(void * file_p, void * buf, uint32_t btr, uint32_t * br)232{233lv_ufs_file_t * fp = file_p; /*Convert type*/234235lv_ufs_ent_t * ent = fp->ent;236*br = 0;237238if(ent->data_d == NULL || ent->size == 0) { /*Don't read empty files*/239return LV_FS_RES_OK;240} else if(fp->ar == 0) { /*The file is not opened for read*/241return LV_FS_RES_DENIED;242}243244/*No error, read the file*/245if(fp->rwp + btr > ent->size) { /*Check too much bytes read*/246*br = ent->size - fp->rwp;247} else {248*br = btr;249}250251/*Read the data*/252uint8_t * data8_p;253if(ent->const_data == 0) {254data8_p = (uint8_t *) ent->data_d;255} else {256data8_p = ent->data_d;257}258259data8_p += fp->rwp;260memcpy(buf, data8_p, *br);261262fp->rwp += *br; /*Refresh the read write pointer*/263264return LV_FS_RES_OK;265}266267/**268* Write data to an opened file269* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open)270* @param buf pointer to a memory block which content will be written271* @param btw the number Bytes To Write272* @param bw The real number of written bytes (Byte Written)273* @return LV_FS_RES_OK: no error, the file is read274* any error from lv__fs_res_t enum275*/276lv_fs_res_t lv_ufs_write(void * file_p, const void * buf, uint32_t btw, uint32_t * bw)277{278lv_ufs_file_t * fp = file_p; /*Convert type*/279*bw = 0;280281if(fp->aw == 0) return LV_FS_RES_DENIED; /*Not opened for write*/282283lv_ufs_ent_t * ent = fp->ent;284285/*Reallocate data array if it necessary*/286uint32_t new_size = fp->rwp + btw;287if(new_size > ent->size) {288uint8_t * new_data = lv_mem_realloc(ent->data_d, new_size);289lv_mem_assert(new_data);290if(new_data == NULL) return LV_FS_RES_FULL; /*Cannot allocate the new memory*/291292ent->data_d = new_data;293ent->size = new_size;294}295296/*Write the file*/297uint8_t * data8_p = (uint8_t *) ent->data_d;298data8_p += fp->rwp;299memcpy(data8_p, buf, btw);300*bw = btw;301fp->rwp += *bw;302303return LV_FS_RES_OK;304}305306/**307* Set the read write pointer. Also expand the file size if necessary.308* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open )309* @param pos the new position of read write pointer310* @return LV_FS_RES_OK: no error, the file is read311* any error from lv__fs_res_t enum312*/313lv_fs_res_t lv_ufs_seek(void * file_p, uint32_t pos)314{315lv_ufs_file_t * fp = file_p; /*Convert type*/316lv_ufs_ent_t * ent = fp->ent;317318/*Simply move the rwp before EOF*/319if(pos < ent->size) {320fp->rwp = pos;321} else { /*Expand the file size*/322if(fp->aw == 0) return LV_FS_RES_DENIED; /*Not opened for write*/323324uint8_t * new_data = lv_mem_realloc(ent->data_d, pos);325lv_mem_assert(new_data);326if(new_data == NULL) return LV_FS_RES_FULL; /*Out of memory*/327328ent->data_d = new_data;329ent->size = pos;330fp->rwp = pos;331}332333return LV_FS_RES_OK;334}335336/**337* Give the position of the read write pointer338* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open )339* @param pos_p pointer to to store the result340* @return LV_FS_RES_OK: no error, the file is read341* any error from lv__fs_res_t enum342*/343lv_fs_res_t lv_ufs_tell(void * file_p, uint32_t * pos_p)344{345lv_ufs_file_t * fp = file_p; /*Convert type*/346347*pos_p = fp->rwp;348349return LV_FS_RES_OK;350}351352/**353* Truncate the file size to the current position of the read write pointer354* @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open )355* @return LV_FS_RES_OK: no error, the file is read356* any error from lv__fs_res_t enum357*/358lv_fs_res_t lv_ufs_trunc(void * file_p)359{360lv_ufs_file_t * fp = file_p; /*Convert type*/361lv_ufs_ent_t * ent = fp->ent;362363if(fp->aw == 0) return LV_FS_RES_DENIED; /*Not opened for write*/364365void * new_data = lv_mem_realloc(ent->data_d, fp->rwp);366lv_mem_assert(new_data);367if(new_data == NULL) return LV_FS_RES_FULL; /*Out of memory*/368369ent->data_d = new_data;370ent->size = fp->rwp;371372return LV_FS_RES_OK;373}374375/**376* Give the size of the file in bytes377* @param file_p file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open )378* @param size_p pointer to store the size379* @return LV_FS_RES_OK: no error, the file is read380* any error from lv__fs_res_t enum381*/382lv_fs_res_t lv_ufs_size(void * file_p, uint32_t * size_p)383{384lv_ufs_file_t * fp = file_p; /*Convert type*/385lv_ufs_ent_t * ent = fp->ent;386387*size_p = ent->size;388389return LV_FS_RES_OK;390}391392/**393* Initialize a lv_ufs_read_dir_t variable to directory reading394* @param rddir_p pointer to a 'ufs_dir_t' variable395* @param path uFS doesn't support folders so it has to be ""396* @return LV_FS_RES_OK or any error from lv__fs_res_t enum397*/398lv_fs_res_t lv_ufs_dir_open(void * rddir_p, const char * path)399{400lv_ufs_dir_t * lv_ufs_rddir_p = rddir_p;401402lv_ufs_rddir_p->last_ent = NULL;403404if(path[0] != '\0') return LV_FS_RES_NOT_EX; /*Must be "" */405else return LV_FS_RES_OK;406}407408/**409* Read the next file name410* @param dir_p pointer to an initialized 'ufs_dir_t' variable411* @param fn pointer to buffer to sore the file name412* @return LV_FS_RES_OK or any error from lv__fs_res_t enum413*/414lv_fs_res_t lv_ufs_dir_read(void * dir_p, char * fn)415{416lv_ufs_dir_t * ufs_dir_p = dir_p;417418if(ufs_dir_p->last_ent == NULL) {419ufs_dir_p->last_ent = lv_ll_get_head(&LV_GC_ROOT(_lv_file_ll));420} else {421ufs_dir_p->last_ent = lv_ll_get_next(&LV_GC_ROOT(_lv_file_ll), ufs_dir_p->last_ent);422}423424if(ufs_dir_p->last_ent != NULL) {425strcpy(fn, ufs_dir_p->last_ent->fn_d);426} else {427fn[0] = '\0';428}429430return LV_FS_RES_OK;431}432433/**434* Close the directory reading435* @param rddir_p pointer to an initialized 'ufs_dir_t' variable436* @return LV_FS_RES_OK or any error from lv__fs_res_t enum437*/438lv_fs_res_t lv_ufs_dir_close(void * rddir_p)439{440(void)rddir_p;441return LV_FS_RES_OK;442}443444/**445* Give the size of a drive446* @param total_p pointer to store the total size [kB]447* @param free_p pointer to store the free site [kB]448* @return LV_FS_RES_OK or any error from 'lv_fs_res_t'449*/450lv_fs_res_t lv_ufs_free(uint32_t * total_p, uint32_t * free_p)451{452453#if LV_MEM_CUSTOM == 0454lv_mem_monitor_t mon;455456lv_mem_monitor(&mon);457*total_p = LV_MEM_SIZE >> 10; /*Convert bytes to kB*/458*free_p = mon.free_size >> 10;459#else460*free_p = 0;461#endif462return LV_FS_RES_OK;463}464465/**********************466* STATIC FUNCTIONS467**********************/468469/**470* Gives the lv_ufs_entry from a filename471* @param fn filename ('\0' terminated string)472* @return pointer to the dynamically allocated entry with 'fn' filename.473* NULL if no entry found with that name.474*/475static lv_ufs_ent_t * lv_ufs_ent_get(const char * fn)476{477lv_ufs_ent_t * fp;478479LL_READ(LV_GC_ROOT(_lv_file_ll), fp) {480if(strcmp(fp->fn_d, fn) == 0) {481return fp;482}483}484485return NULL;486}487488/**489* Create a new entry with 'fn' filename490* @param fn filename ('\0' terminated string)491* @return pointer to the dynamically allocated new entry.492* NULL if no space for the entry.493*/494static lv_ufs_ent_t * lv_ufs_ent_new(const char * fn)495{496lv_ufs_ent_t * new_ent = NULL;497new_ent = lv_ll_ins_head(&LV_GC_ROOT(_lv_file_ll)); /*Create a new file*/498lv_mem_assert(new_ent);499if(new_ent == NULL) return NULL;500501new_ent->fn_d = lv_mem_alloc(strlen(fn) + 1); /*Save the name*/502lv_mem_assert(new_ent->fn_d);503if(new_ent->fn_d == NULL) return NULL;504505strcpy(new_ent->fn_d, fn);506new_ent->data_d = NULL;507new_ent->size = 0;508new_ent->oc = 0;509new_ent->const_data = 0;510511return new_ent;512}513514#endif /*USE_LV_FILESYSTEM*/515516517518