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