1 /**
2  * @file lv_fs_littlefs.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../../lvgl.h"
10 
11 #if LV_USE_FS_LITTLEFS
12 #include "lfs.h"
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 
18 #if LV_FS_LITTLEFS_LETTER == '\0'
19     #error "LV_FS_LITTLEFS_LETTER must be an upper case ASCII letter"
20 #endif
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 
30 static void fs_init(void);
31 
32 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
33 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
34 
35 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
36 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
37 
38 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
39 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
40 
41 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
42 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
43 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
44 
45 /**********************
46  *  STATIC VARIABLES
47  **********************/
48 
49 /**********************
50  * GLOBAL PROTOTYPES
51  **********************/
52 
53 /**********************
54  *      MACROS
55  **********************/
56 
57 /**********************
58  *   GLOBAL FUNCTIONS
59  **********************/
60 
lv_fs_littlefs_init(void)61 void lv_fs_littlefs_init(void)
62 {
63     /*----------------------------------------------------
64      * Initialize your storage device and File System
65      * -------------------------------------------------*/
66     fs_init();
67 
68     /*---------------------------------------------------
69      * Register the file system interface in LVGL
70      *--------------------------------------------------*/
71 
72     /*Add a simple drive to open images*/
73     static lv_fs_drv_t fs_drv; /*A driver descriptor*/
74     lv_fs_drv_init(&fs_drv);
75 
76     /*Set up fields...*/
77     fs_drv.letter = LV_FS_LITTLEFS_LETTER;
78     fs_drv.cache_size = LV_FS_LITTLEFS_CACHE_SIZE;
79 
80     fs_drv.open_cb = fs_open;
81     fs_drv.close_cb = fs_close;
82     fs_drv.read_cb = fs_read;
83     fs_drv.write_cb = fs_write;
84     fs_drv.seek_cb = fs_seek;
85     fs_drv.tell_cb = fs_tell;
86 
87     fs_drv.dir_open_cb = fs_dir_open;
88     fs_drv.dir_close_cb = fs_dir_close;
89     fs_drv.dir_read_cb = fs_dir_read;
90 
91     /*#if LV_USE_USER_DATA*/
92     fs_drv.user_data = NULL;
93     /*#endif*/
94 
95     lv_fs_drv_register(&fs_drv);
96 }
97 
98 /**
99  * Convenience function to attach registered driver to lfs_t structure by driver-label
100  * @param label     the label assigned to the driver when it was registered
101  * @param lfs_p     the pointer to the lfs_t structure initialized by external code/library
102  * @return          pointer to a driver descriptor or NULL on error
103  */
lv_fs_littlefs_set_driver(char label,void * lfs_p)104 lv_fs_drv_t * lv_fs_littlefs_set_driver(char label, void * lfs_p)
105 {
106     lv_fs_drv_t * drv_p = lv_fs_get_drv(label);
107     if(drv_p != NULL) drv_p->user_data = (lfs_t *) lfs_p;
108     return drv_p;
109 }
110 
111 /**********************
112  *   STATIC FUNCTIONS
113  **********************/
114 
115 /*Initialize your Storage device and File system.*/
fs_init(void)116 static void fs_init(void)
117 {
118     /* Initialize the internal flash or SD-card and LittleFS itself.
119      * Better to do it in your code to keep this library untouched for easy updating */
120 }
121 
122 /**
123  * Open a file
124  * @param drv       pointer to a driver where this function belongs
125  * @param path      path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
126  * @param mode      read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
127  * @return          pointer to a file descriptor or NULL on error
128  */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)129 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
130 {
131     lfs_t * lfs_p = drv->user_data;
132     uint32_t flags = 0;
133 
134     flags = mode == LV_FS_MODE_RD ? LFS_O_RDONLY
135             : mode == LV_FS_MODE_WR ? LFS_O_WRONLY
136             : mode == (LV_FS_MODE_WR | LV_FS_MODE_RD) ? LFS_O_RDWR : 0;
137 
138     lfs_file_t * file_p = lv_mem_alloc(sizeof(lfs_file_t));
139     if(file_p == NULL) return NULL;
140 
141     int result = lfs_file_open(lfs_p, file_p, path, flags);
142 
143     if(result != LFS_ERR_OK) {
144         lv_mem_free(file_p);
145         return NULL;
146     }
147 
148     return file_p;
149 }
150 
151 /**
152  * Close an opened file
153  * @param drv       pointer to a driver where this function belongs
154  * @param file_p    pointer to a file_t variable. (opened with fs_open)
155  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
156  */
fs_close(lv_fs_drv_t * drv,void * file_p)157 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
158 {
159     lfs_t * lfs_p = drv->user_data;
160 
161     int result = lfs_file_close(lfs_p, file_p);
162     lv_mem_free(file_p);
163     /*lv_mem_free( lfs_p );*/ /*allocated and freed by outside-code*/
164 
165     if(result != LFS_ERR_OK) return LV_FS_RES_UNKNOWN;
166     return LV_FS_RES_OK;
167 }
168 
169 /**
170  * Read data from an opened file
171  * @param drv       pointer to a driver where this function belongs
172  * @param file_p    pointer to a file_t variable.
173  * @param buf       pointer to a memory block where to store the read data
174  * @param btr       number of Bytes To Read
175  * @param br        the real number of read bytes (Byte Read)
176  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
177  */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)178 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
179 {
180     lfs_t * lfs_p = drv->user_data;
181 
182     lfs_ssize_t result = lfs_file_read(lfs_p, file_p, buf, btr);
183     if(result < 0) return LV_FS_RES_UNKNOWN;
184 
185     *br = (uint32_t) result;
186     return LV_FS_RES_OK;
187 }
188 
189 /**
190  * Write into a file
191  * @param drv       pointer to a driver where this function belongs
192  * @param file_p    pointer to a file_t variable
193  * @param buf       pointer to a buffer with the bytes to write
194  * @param btw       Bytes To Write
195  * @param bw        the number of real written bytes (Bytes Written). NULL if unused.
196  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
197  */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)198 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
199 {
200 #ifndef LFS_READONLY
201     lfs_t * lfs_p = drv->user_data;
202 
203     lfs_ssize_t result = lfs_file_write(lfs_p, file_p, buf, btw);
204     if(result < 0 || lfs_file_sync(lfs_p, file_p) < 0) return LV_FS_RES_UNKNOWN;
205 
206     *bw = (uint32_t) result;
207     return LV_FS_RES_OK;
208 #else
209     return LV_FS_RES_NOT_IMP;
210 #endif
211 }
212 
213 /**
214  * Set the read write pointer. Also expand the file size if necessary.
215  * @param drv       pointer to a driver where this function belongs
216  * @param file_p    pointer to a file_t variable. (opened with fs_open )
217  * @param pos       the new position of read write pointer
218  * @param whence    tells from where to interpret the `pos`. See @lv_fs_whence_t
219  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
220  */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)221 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
222 {
223     lfs_t * lfs_p = drv->user_data;
224 
225     int lfs_whence = whence == LV_FS_SEEK_SET ? LFS_SEEK_SET
226                      : whence == LV_FS_SEEK_CUR ? LFS_SEEK_CUR
227                      : whence == LV_FS_SEEK_END ? LFS_SEEK_END : 0;
228 
229     lfs_soff_t result = lfs_file_seek(lfs_p, file_p, pos, lfs_whence);
230     if(result < 0) return LV_FS_RES_UNKNOWN;
231 
232     /*pos = result;*/ /*not supported by lv_fs*/
233     return LV_FS_RES_OK;
234 }
235 
236 /**
237  * Give the position of the read write pointer
238  * @param drv       pointer to a driver where this function belongs
239  * @param file_p    pointer to a file_t variable.
240  * @param pos_p     pointer to where to store the result
241  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
242  */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)243 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
244 {
245     lfs_t * lfs_p = drv->user_data;
246 
247     lfs_soff_t result = lfs_file_tell(lfs_p, file_p);
248     if(result < 0) return LV_FS_RES_UNKNOWN;
249 
250     *pos_p = (uint32_t) result;
251     return LV_FS_RES_OK;
252 }
253 
254 /**
255  * Initialize a 'lv_fs_dir_t' variable for directory reading
256  * @param drv       pointer to a driver where this function belongs
257  * @param path      path to a directory
258  * @return          pointer to the directory read descriptor or NULL on error
259  */
fs_dir_open(lv_fs_drv_t * drv,const char * path)260 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
261 {
262     lfs_t * lfs_p = drv->user_data;
263 
264     lfs_dir_t * dir_p = lv_mem_alloc(sizeof(lfs_dir_t));
265     if(dir_p == NULL) return NULL;
266 
267     int result = lfs_dir_open(lfs_p, dir_p, path);
268     if(result != LFS_ERR_OK) {
269         lv_mem_free(dir_p);
270         return NULL;
271     }
272 
273     return dir_p;
274 }
275 
276 /**
277  * Read the next filename form a directory.
278  * The name of the directories will begin with '/'
279  * @param drv       pointer to a driver where this function belongs
280  * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
281  * @param fn        pointer to a buffer to store the filename
282  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
283  */
fs_dir_read(lv_fs_drv_t * drv,void * rddir_p,char * fn)284 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * rddir_p, char * fn)
285 {
286     lfs_t * lfs_p = drv->user_data;
287     struct lfs_info info;
288     int result;
289 
290     info.name[0] = '\0';
291 
292     do {
293         result = lfs_dir_read(lfs_p, rddir_p, &info);
294         if(result > 0) {
295             if(info.type == LFS_TYPE_DIR) {
296                 fn[0] = '/';
297                 strcpy(&fn[1], info.name);
298             }
299             else strcpy(fn, info.name);
300         }
301         else if(result == 0) fn[0] = '\0'; /*dir-scan ended*/
302         else return LV_FS_RES_UNKNOWN;
303 
304     } while(!strcmp(fn, "/.") || !strcmp(fn, "/.."));
305 
306     return LV_FS_RES_OK;
307 }
308 
309 /**
310  * Close the directory reading
311  * @param drv       pointer to a driver where this function belongs
312  * @param rddir_p   pointer to an initialized 'lv_fs_dir_t' variable
313  * @return          LV_FS_RES_OK: no error or  any error from @lv_fs_res_t enum
314  */
fs_dir_close(lv_fs_drv_t * drv,void * rddir_p)315 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * rddir_p)
316 {
317     lfs_t * lfs_p = drv->user_data;
318 
319     int result = lfs_dir_close(lfs_p, rddir_p);
320     lv_mem_free(rddir_p);
321 
322     if(result != LFS_ERR_OK) return LV_FS_RES_UNKNOWN;
323     return LV_FS_RES_OK;
324 }
325 
326 #else /*LV_USE_FS_LITTLEFS == 0*/
327 
328 #if defined(LV_FS_LITTLEFS_LETTER) && LV_FS_LITTLEFS_LETTER != '\0'
329     #warning "LV_USE_FS_LITTLEFS is not enabled but LV_FS_LITTLEFS_LETTER is set"
330 #endif
331 
332 #endif /*LV_USE_FS_POSIX*/
333