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