1 /**
2 * @file lv_fs_stdio.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "../../../lvgl.h"
10 #if LV_USE_FS_STDIO
11
12 #include <stdio.h>
13 #ifndef WIN32
14 #include <dirent.h>
15 #include <unistd.h>
16 #else
17 #include <windows.h>
18 #endif
19
20 #include "../../core/lv_global.h"
21 /*********************
22 * DEFINES
23 *********************/
24
25 #if !LV_FS_IS_VALID_LETTER(LV_FS_STDIO_LETTER)
26 #error "Invalid drive letter"
27 #endif
28
29 /**********************
30 * TYPEDEFS
31 **********************/
32 typedef struct {
33 #ifdef _WIN32
34 HANDLE dir_p;
35 char next_fn[LV_FS_MAX_PATH_LEN];
36 #else
37 DIR * dir_p;
38 #endif
39 } dir_handle_t;
40
41 /**********************
42 * STATIC PROTOTYPES
43 **********************/
44 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
45 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
46 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
47 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
48 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
49 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
50 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
51 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
52 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
53
54 /**********************
55 * STATIC VARIABLES
56 **********************/
57
58 /**********************
59 * MACROS
60 **********************/
61
62 /**********************
63 * GLOBAL FUNCTIONS
64 **********************/
65
66 /**
67 * Register a driver for the File system interface
68 */
lv_fs_stdio_init(void)69 void lv_fs_stdio_init(void)
70 {
71 /*---------------------------------------------------
72 * Register the file system interface in LVGL
73 *--------------------------------------------------*/
74
75 lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->stdio_fs_drv);
76 lv_fs_drv_init(fs_drv_p);
77
78 /*Set up fields...*/
79 fs_drv_p->letter = LV_FS_STDIO_LETTER;
80 fs_drv_p->cache_size = LV_FS_STDIO_CACHE_SIZE;
81
82 fs_drv_p->open_cb = fs_open;
83 fs_drv_p->close_cb = fs_close;
84 fs_drv_p->read_cb = fs_read;
85 fs_drv_p->write_cb = fs_write;
86 fs_drv_p->seek_cb = fs_seek;
87 fs_drv_p->tell_cb = fs_tell;
88
89 fs_drv_p->dir_close_cb = fs_dir_close;
90 fs_drv_p->dir_open_cb = fs_dir_open;
91 fs_drv_p->dir_read_cb = fs_dir_read;
92
93 lv_fs_drv_register(fs_drv_p);
94 }
95
96 /**********************
97 * STATIC FUNCTIONS
98 **********************/
99
100 /**
101 * Open a file
102 * @param drv pointer to a driver where this function belongs
103 * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
104 * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
105 * @return pointer to FIL struct or NULL in case of fail
106 */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)107 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
108 {
109 LV_UNUSED(drv);
110
111 const char * flags = "";
112
113 if(mode == LV_FS_MODE_WR) flags = "wb";
114 else if(mode == LV_FS_MODE_RD) flags = "rb";
115 else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+";
116
117 /*Make the path relative to the current directory (the projects root folder)*/
118
119 char buf[LV_FS_MAX_PATH_LEN];
120 lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
121
122 return fopen(buf, flags);
123 }
124
125 /**
126 * Close an opened file
127 * @param drv pointer to a driver where this function belongs
128 * @param file_p pointer to a FILE variable. (opened with fs_open)
129 * @return LV_FS_RES_OK: no error, the file is read
130 * any error from lv_fs_res_t enum
131 */
fs_close(lv_fs_drv_t * drv,void * file_p)132 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
133 {
134 LV_UNUSED(drv);
135 fclose(file_p);
136 return LV_FS_RES_OK;
137 }
138
139 /**
140 * Read data from an opened file
141 * @param drv pointer to a driver where this function belongs
142 * @param file_p pointer to a FILE variable.
143 * @param buf pointer to a memory block where to store the read data
144 * @param btr number of Bytes To Read
145 * @param br the real number of read bytes (Byte Read)
146 * @return LV_FS_RES_OK: no error, the file is read
147 * any error from lv_fs_res_t enum
148 */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)149 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
150 {
151 LV_UNUSED(drv);
152 *br = fread(buf, 1, btr, file_p);
153 return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
154 }
155
156 /**
157 * Write into a file
158 * @param drv pointer to a driver where this function belongs
159 * @param file_p pointer to a FILE variable
160 * @param buf pointer to a buffer with the bytes to write
161 * @param btw Bytes To Write
162 * @param bw the number of real written bytes (Bytes Written). NULL if unused.
163 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
164 */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)165 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
166 {
167 LV_UNUSED(drv);
168 *bw = fwrite(buf, 1, btw, file_p);
169 return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
170 }
171
172 /**
173 * Set the read write pointer. Also expand the file size if necessary.
174 * @param drv pointer to a driver where this function belongs
175 * @param file_p pointer to a FILE variable. (opened with fs_open )
176 * @param pos the new position of read write pointer
177 * @return LV_FS_RES_OK: no error, the file is read
178 * any error from lv_fs_res_t enum
179 */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)180 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
181 {
182 LV_UNUSED(drv);
183 int w;
184 switch(whence) {
185 case LV_FS_SEEK_SET:
186 w = SEEK_SET;
187 break;
188 case LV_FS_SEEK_CUR:
189 w = SEEK_CUR;
190 break;
191 case LV_FS_SEEK_END:
192 w = SEEK_END;
193 break;
194 default:
195 return LV_FS_RES_INV_PARAM;
196 }
197
198 fseek(file_p, pos, w);
199 return LV_FS_RES_OK;
200 }
201
202 /**
203 * Give the position of the read write pointer
204 * @param drv pointer to a driver where this function belongs
205 * @param file_p pointer to a FILE variable
206 * @param pos_p pointer to store the result
207 * @return LV_FS_RES_OK: no error, the file is read
208 * any error from lv_fs_res_t enum
209 */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)210 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
211 {
212 LV_UNUSED(drv);
213 *pos_p = ftell(file_p);
214 return LV_FS_RES_OK;
215 }
216
217 /**
218 * Initialize a 'DIR' or 'HANDLE' variable for directory reading
219 * @param drv pointer to a driver where this function belongs
220 * @param path path to a directory
221 * @return pointer to an initialized 'DIR' or 'HANDLE' variable
222 */
fs_dir_open(lv_fs_drv_t * drv,const char * path)223 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
224 {
225 LV_UNUSED(drv);
226 dir_handle_t * handle = (dir_handle_t *)lv_malloc(sizeof(dir_handle_t));
227 #ifndef WIN32
228 /*Make the path relative to the current directory (the projects root folder)*/
229 char buf[LV_FS_MAX_PATH_LEN];
230 lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
231 handle->dir_p = opendir(buf);
232 if(handle->dir_p == NULL) {
233 lv_free(handle);
234 return NULL;
235 }
236 return handle;
237 #else
238 handle->dir_p = INVALID_HANDLE_VALUE;
239 WIN32_FIND_DATAA fdata;
240
241 /*Make the path relative to the current directory (the projects root folder)*/
242 char buf[LV_FS_MAX_PATH_LEN];
243 lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s\\*", path);
244
245 lv_strcpy(handle->next_fn, "");
246 handle->dir_p = FindFirstFileA(buf, &fdata);
247 do {
248 if(lv_strcmp(fdata.cFileName, ".") == 0 || lv_strcmp(fdata.cFileName, "..") == 0) {
249 continue;
250 }
251 else {
252 if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
253 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
254 }
255 else {
256 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
257 }
258 break;
259 }
260 } while(FindNextFileA(handle->dir_p, &fdata));
261
262 if(handle->dir_p == INVALID_HANDLE_VALUE) {
263 lv_free(handle);
264 return INVALID_HANDLE_VALUE;
265 }
266 return handle;
267 #endif
268 }
269
270 /**
271 * Read the next filename form a directory.
272 * The name of the directories will begin with '/'
273 * @param drv pointer to a driver where this function belongs
274 * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
275 * @param fn pointer to a buffer to store the filename
276 * @param fn_len length of the buffer to store the filename
277 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
278 */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn,uint32_t fn_len)279 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
280 {
281 LV_UNUSED(drv);
282 if(fn_len == 0) return LV_FS_RES_INV_PARAM;
283
284 dir_handle_t * handle = (dir_handle_t *)dir_p;
285 #ifndef WIN32
286 struct dirent * entry;
287 do {
288 entry = readdir(handle->dir_p);
289 if(entry) {
290 /*Note, DT_DIR is not defined in C99*/
291 if(entry->d_type == DT_DIR) lv_snprintf(fn, fn_len, "/%s", entry->d_name);
292 else lv_strlcpy(fn, entry->d_name, fn_len);
293 }
294 else {
295 lv_strlcpy(fn, "", fn_len);
296 }
297 } while(lv_strcmp(fn, "/.") == 0 || lv_strcmp(fn, "/..") == 0);
298 #else
299 lv_strlcpy(fn, handle->next_fn, fn_len);
300
301 lv_strcpy(handle->next_fn, "");
302 WIN32_FIND_DATAA fdata;
303
304 if(FindNextFileA(handle->dir_p, &fdata) == false) return LV_FS_RES_OK;
305 do {
306 if(lv_strcmp(fdata.cFileName, ".") == 0 || lv_strcmp(fdata.cFileName, "..") == 0) {
307 continue;
308 }
309 else {
310 if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
311 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
312 }
313 else {
314 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
315 }
316 break;
317 }
318 } while(FindNextFileA(handle->dir_p, &fdata));
319
320 #endif
321 return LV_FS_RES_OK;
322 }
323
324 /**
325 * Close the directory reading
326 * @param drv pointer to a driver where this function belongs
327 * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
328 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
329 */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)330 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
331 {
332 LV_UNUSED(drv);
333 dir_handle_t * handle = (dir_handle_t *)dir_p;
334 #ifndef WIN32
335 closedir(handle->dir_p);
336 #else
337 FindClose(handle->dir_p);
338 #endif
339 lv_free(handle);
340 return LV_FS_RES_OK;
341 }
342
343 #else /*LV_USE_FS_STDIO == 0*/
344
345 #if defined(LV_FS_STDIO_LETTER) && LV_FS_STDIO_LETTER != '\0'
346 #warning "LV_USE_FS_STDIO is not enabled but LV_FS_STDIO_LETTER is set"
347 #endif
348
349 #endif /*LV_USE_FS_POSIX*/
350