1 /**
2  * @file lv_fs_posix.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../../lvgl.h"
10 
11 #if LV_USE_FS_POSIX
12 
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <dirent.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include "../../core/lv_global.h"
20 
21 /*********************
22  *      DEFINES
23  *********************/
24 
25 #if !LV_FS_IS_VALID_LETTER(LV_FS_POSIX_LETTER)
26     #error "Invalid drive letter"
27 #endif
28 
29 /** The reason for 'fd + 1' is because open() may return a legal fd with a value of 0,
30   * preventing it from being judged as NULL when converted to a pointer type.
31   */
32 #define FILEP2FD(file_p) ((lv_uintptr_t)file_p - 1)
33 #define FD2FILEP(fd) ((void *)(lv_uintptr_t)(fd + 1))
34 
35 /**********************
36  *      TYPEDEFS
37  **********************/
38 
39 /**********************
40  *  STATIC PROTOTYPES
41  **********************/
42 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
43 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
44 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
45 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
46 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
47 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
48 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
49 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
50 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
51 static lv_fs_res_t fs_errno_to_res(int errno_val);
52 
53 /**********************
54  *  STATIC VARIABLES
55  **********************/
56 
57 /**********************
58  *      MACROS
59  **********************/
60 
61 /**********************
62  *   GLOBAL FUNCTIONS
63  **********************/
64 
65 /**
66  * Register a driver for the File system interface
67  */
lv_fs_posix_init(void)68 void lv_fs_posix_init(void)
69 {
70     /*---------------------------------------------------
71      * Register the file system interface in LVGL
72      *--------------------------------------------------*/
73 
74     lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->posix_fs_drv);
75     lv_fs_drv_init(fs_drv_p);
76 
77     /*Set up fields...*/
78     fs_drv_p->letter = LV_FS_POSIX_LETTER;
79     fs_drv_p->cache_size = LV_FS_POSIX_CACHE_SIZE;
80 
81     fs_drv_p->open_cb = fs_open;
82     fs_drv_p->close_cb = fs_close;
83     fs_drv_p->read_cb = fs_read;
84     fs_drv_p->write_cb = fs_write;
85     fs_drv_p->seek_cb = fs_seek;
86     fs_drv_p->tell_cb = fs_tell;
87 
88     fs_drv_p->dir_close_cb = fs_dir_close;
89     fs_drv_p->dir_open_cb = fs_dir_open;
90     fs_drv_p->dir_read_cb = fs_dir_read;
91 
92     lv_fs_drv_register(fs_drv_p);
93 }
94 
95 /**********************
96  *   STATIC FUNCTIONS
97  **********************/
98 
99 /**
100  * Open a file
101  * @param drv   pointer to a driver where this function belongs
102  * @param path  path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
103  * @param mode  read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
104  * @return a file handle or -1 in case of fail
105  */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)106 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
107 {
108     LV_UNUSED(drv);
109 
110     int flags = 0;
111     if(mode == LV_FS_MODE_WR) flags = O_WRONLY | O_CREAT;
112     else if(mode == LV_FS_MODE_RD) flags = O_RDONLY;
113     else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = O_RDWR | O_CREAT;
114 
115     /*Make the path relative to the current directory (the projects root folder)*/
116     char buf[LV_FS_MAX_PATH_LEN];
117     lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path);
118 
119     int fd = open(buf, flags, 0666);
120     if(fd < 0) {
121         LV_LOG_WARN("Could not open file: %s, flags: 0x%x, errno: %d", buf, flags, errno);
122         return NULL;
123     }
124 
125     return FD2FILEP(fd);
126 }
127 
128 /**
129  * Close an opened file
130  * @param drv       pointer to a driver where this function belongs
131  * @param file_p    a file handle. (opened with fs_open)
132  * @return LV_FS_RES_OK: no error, the file is read
133  *         any error from lv_fs_res_t enum
134  */
fs_close(lv_fs_drv_t * drv,void * file_p)135 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
136 {
137     LV_UNUSED(drv);
138 
139     int fd = FILEP2FD(file_p);
140     int ret = close(fd);
141     if(ret < 0) {
142         LV_LOG_WARN("Could not close file: %d, errno: %d", fd, errno);
143         return fs_errno_to_res(errno);
144     }
145 
146     return LV_FS_RES_OK;
147 }
148 
149 /**
150  * Read data from an opened file
151  * @param drv       pointer to a driver where this function belongs
152  * @param file_p    a file handle variable.
153  * @param buf       pointer to a memory block where to store the read data
154  * @param btr       number of Bytes To Read
155  * @param br        the real number of read bytes (Byte Read)
156  * @return LV_FS_RES_OK: no error, the file is read
157  *         any error from lv_fs_res_t enum
158  */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)159 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
160 {
161     LV_UNUSED(drv);
162 
163     int fd = FILEP2FD(file_p);
164     ssize_t ret = read(fd, buf, btr);
165     if(ret < 0) {
166         LV_LOG_WARN("Could not read file: %d, errno: %d", fd, errno);
167         return fs_errno_to_res(errno);
168     }
169 
170     *br = (uint32_t)ret;
171     return LV_FS_RES_OK;
172 }
173 
174 /**
175  * Write into a file
176  * @param drv       pointer to a driver where this function belongs
177  * @param file_p    a file handle variable
178  * @param buf       pointer to a buffer with the bytes to write
179  * @param btw       Bytes To Write
180  * @param bw        the number of real written bytes (Bytes Written). NULL if unused.
181  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
182  */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)183 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
184 {
185     LV_UNUSED(drv);
186 
187     int fd = FILEP2FD(file_p);
188     ssize_t ret = write(fd, buf, btw);
189     if(ret < 0) {
190         LV_LOG_WARN("Could not write file: %d, errno: %d", fd, errno);
191         return fs_errno_to_res(errno);
192     }
193 
194     *bw = (uint32_t)ret;
195     return LV_FS_RES_OK;
196 }
197 
198 /**
199  * Set the read write pointer. Also expand the file size if necessary.
200  * @param drv       pointer to a driver where this function belongs
201  * @param file_p    a file handle variable. (opened with fs_open )
202  * @param pos       the new position of read write pointer
203  * @return LV_FS_RES_OK: no error, the file is read
204  *         any error from lv_fs_res_t enum
205  */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)206 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
207 {
208     LV_UNUSED(drv);
209     int w;
210     switch(whence) {
211         case LV_FS_SEEK_SET:
212             w = SEEK_SET;
213             break;
214         case LV_FS_SEEK_CUR:
215             w = SEEK_CUR;
216             break;
217         case LV_FS_SEEK_END:
218             w = SEEK_END;
219             break;
220         default:
221             return LV_FS_RES_INV_PARAM;
222     }
223 
224     int fd = FILEP2FD(file_p);
225     off_t offset = lseek(fd, pos, w);
226     if(offset < 0) {
227         LV_LOG_WARN("Could not seek file: %d, errno: %d", fd, errno);
228         return fs_errno_to_res(errno);
229     }
230 
231     return LV_FS_RES_OK;
232 }
233 
234 /**
235  * Give the position of the read write pointer
236  * @param drv       pointer to a driver where this function belongs
237  * @param file_p    a file handle variable
238  * @param pos_p     pointer to store the result
239  * @return LV_FS_RES_OK: no error, the file is read
240  *         any error from lv_fs_res_t enum
241  */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)242 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
243 {
244     LV_UNUSED(drv);
245 
246     int fd = FILEP2FD(file_p);
247     off_t offset = lseek(fd, 0, SEEK_CUR);
248     if(offset < 0) {
249         LV_LOG_WARN("Could not get position of file: %d, errno: %d", fd, errno);
250         return fs_errno_to_res(errno);
251     }
252 
253     *pos_p = (uint32_t)offset;
254     return LV_FS_RES_OK;
255 }
256 
257 /**
258  * Initialize a 'fs_read_dir_t' variable for directory reading
259  * @param drv   pointer to a driver where this function belongs
260  * @param path  path to a directory
261  * @return pointer to an initialized 'DIR' or 'HANDLE' variable
262  */
fs_dir_open(lv_fs_drv_t * drv,const char * path)263 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
264 {
265     LV_UNUSED(drv);
266 
267     /*Make the path relative to the current directory (the projects root folder)*/
268     char buf[256];
269     lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path);
270 
271     void * dir = opendir(buf);
272     if(!dir) {
273         LV_LOG_WARN("Could not open directory: %s, errno: %d", buf, errno);
274         return NULL;
275     }
276 
277     return dir;
278 }
279 
280 /**
281  * Read the next filename from a directory.
282  * The name of the directories will begin with '/'
283  * @param drv       pointer to a driver where this function belongs
284  * @param dir_p     pointer to an initialized 'DIR' or 'HANDLE' variable
285  * @param fn        pointer to a buffer to store the filename
286  * @param fn_len    length of the buffer to store the filename
287  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
288  */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn,uint32_t fn_len)289 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
290 {
291     LV_UNUSED(drv);
292     if(fn_len == 0) return LV_FS_RES_INV_PARAM;
293 
294     struct dirent * entry;
295     do {
296         entry = readdir(dir_p);
297         if(entry) {
298             if(entry->d_type == DT_DIR) lv_snprintf(fn, fn_len, "/%s", entry->d_name);
299             else lv_strlcpy(fn, entry->d_name, fn_len);
300         }
301         else {
302             lv_strlcpy(fn, "", fn_len);
303         }
304     } while(lv_strcmp(fn, "/.") == 0 || lv_strcmp(fn, "/..") == 0);
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 dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
313  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
314  */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)315 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
316 {
317     LV_UNUSED(drv);
318 
319     int ret = closedir(dir_p);
320     if(ret < 0) {
321         LV_LOG_WARN("Could not close directory: errno: %d", errno);
322         return fs_errno_to_res(errno);
323     }
324 
325     return LV_FS_RES_OK;
326 }
327 
328 /**
329  * Convert an errno value to a lv_fs_res_t value
330  * @param errno_val an errno value
331  * @return a corresponding lv_fs_res_t value
332  */
fs_errno_to_res(int errno_val)333 static lv_fs_res_t fs_errno_to_res(int errno_val)
334 {
335     switch(errno_val) {
336         case 0:
337             return LV_FS_RES_OK;
338 
339         case EIO: /* I/O error */
340             return LV_FS_RES_HW_ERR;
341 
342         case EFAULT: /* Bad address */
343             return LV_FS_RES_FS_ERR;
344 
345         case ENOENT: /* No such file or directory */
346             return LV_FS_RES_NOT_EX;
347 
348         case ENOSPC: /* No space left on device */
349             return LV_FS_RES_FULL;
350 
351         case EALREADY: /* Operation already in progress */
352             return LV_FS_RES_LOCKED;
353 
354         case EACCES: /* Permission denied */
355             return LV_FS_RES_DENIED;
356 
357         case EBUSY: /* Device or resource busy */
358             return LV_FS_RES_BUSY;
359 
360         case ETIMEDOUT: /* Connection timed out */
361             return LV_FS_RES_TOUT;
362 
363         case ENOSYS: /* Invalid system call number */
364             return LV_FS_RES_NOT_IMP;
365 
366         case ENOMEM: /* Out of memory */
367             return LV_FS_RES_OUT_OF_MEM;
368 
369         case EINVAL: /* "Invalid argument" */
370             return LV_FS_RES_INV_PARAM;
371 
372         default:
373             break;
374     }
375 
376     return LV_FS_RES_UNKNOWN;
377 }
378 
379 #else /*LV_USE_FS_POSIX == 0*/
380 
381 #if defined(LV_FS_POSIX_LETTER) && LV_FS_POSIX_LETTER != '\0'
382     #warning "LV_USE_FS_POSIX is not enabled but LV_FS_POSIX_LETTER is set"
383 #endif
384 
385 #endif /*LV_USE_FS_POSIX*/
386