1 /**
2 * @file lv_fs_win32.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "../../../lvgl.h"
10 #if LV_USE_FS_WIN32
11
12 #include <windows.h>
13 #include <stdio.h>
14 #include <limits.h>
15
16 #include "../../core/lv_global.h"
17 /*********************
18 * DEFINES
19 *********************/
20
21 #if !LV_FS_IS_VALID_LETTER(LV_FS_WIN32_LETTER)
22 #error "Invalid drive letter"
23 #endif
24
25 /**********************
26 * TYPEDEFS
27 **********************/
28 typedef struct {
29 HANDLE dir_p;
30 char next_fn[LV_FS_MAX_PATH_LEN];
31 lv_fs_res_t next_error;
32 } dir_handle_t;
33
34 /**********************
35 * STATIC PROTOTYPES
36 **********************/
37
38 static bool is_dots_name(const char * name);
39 static lv_fs_res_t fs_error_from_win32(DWORD error);
40 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
41 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
42 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
43 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
44 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
45 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
46 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
47 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len);
48 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
49
50 /**********************
51 * STATIC VARIABLES
52 **********************/
53
54 /**********************
55 * MACROS
56 **********************/
57
58 /**********************
59 * GLOBAL FUNCTIONS
60 **********************/
61
62 /**
63 * Register a driver for the File system interface
64 */
lv_fs_win32_init(void)65 void lv_fs_win32_init(void)
66 {
67 /*---------------------------------------------------
68 * Register the file system interface in LVGL
69 *--------------------------------------------------*/
70
71 /*Add a simple driver to open images*/
72 lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->win32_fs_drv);
73 lv_fs_drv_init(fs_drv_p);
74
75 /*Set up fields...*/
76 fs_drv_p->letter = LV_FS_WIN32_LETTER;
77 fs_drv_p->cache_size = LV_FS_WIN32_CACHE_SIZE;
78
79 fs_drv_p->open_cb = fs_open;
80 fs_drv_p->close_cb = fs_close;
81 fs_drv_p->read_cb = fs_read;
82 fs_drv_p->write_cb = fs_write;
83 fs_drv_p->seek_cb = fs_seek;
84 fs_drv_p->tell_cb = fs_tell;
85
86 fs_drv_p->dir_close_cb = fs_dir_close;
87 fs_drv_p->dir_open_cb = fs_dir_open;
88 fs_drv_p->dir_read_cb = fs_dir_read;
89
90 lv_fs_drv_register(fs_drv_p);
91 }
92
93 /**********************
94 * STATIC FUNCTIONS
95 **********************/
96
97 /**
98 * Check the dots name
99 * @param name file or dir name
100 * @return true if the name is dots name
101 */
is_dots_name(const char * name)102 static bool is_dots_name(const char * name)
103 {
104 return name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]));
105 }
106
107 /**
108 * Convert Win32 error code to error from lv_fs_res_t enum
109 * @param error Win32 error code
110 * @return LV_FS_RES_OK: no error, the file is read
111 * any error from lv_fs_res_t enum
112 */
fs_error_from_win32(DWORD error)113 static lv_fs_res_t fs_error_from_win32(DWORD error)
114 {
115 lv_fs_res_t res;
116
117 switch(error) {
118 case ERROR_SUCCESS:
119 res = LV_FS_RES_OK;
120 break;
121 case ERROR_BAD_UNIT:
122 case ERROR_NOT_READY:
123 case ERROR_CRC:
124 case ERROR_SEEK:
125 case ERROR_NOT_DOS_DISK:
126 case ERROR_WRITE_FAULT:
127 case ERROR_READ_FAULT:
128 case ERROR_GEN_FAILURE:
129 case ERROR_WRONG_DISK:
130 res = LV_FS_RES_HW_ERR;
131 break;
132 case ERROR_INVALID_HANDLE:
133 case ERROR_INVALID_TARGET_HANDLE:
134 res = LV_FS_RES_FS_ERR;
135 break;
136 case ERROR_FILE_NOT_FOUND:
137 case ERROR_PATH_NOT_FOUND:
138 case ERROR_INVALID_DRIVE:
139 case ERROR_NO_MORE_FILES:
140 case ERROR_SECTOR_NOT_FOUND:
141 case ERROR_BAD_NETPATH:
142 case ERROR_BAD_NET_NAME:
143 case ERROR_BAD_PATHNAME:
144 case ERROR_FILENAME_EXCED_RANGE:
145 res = LV_FS_RES_NOT_EX;
146 break;
147 case ERROR_DISK_FULL:
148 res = LV_FS_RES_FULL;
149 break;
150 case ERROR_SHARING_VIOLATION:
151 case ERROR_LOCK_VIOLATION:
152 case ERROR_DRIVE_LOCKED:
153 res = LV_FS_RES_LOCKED;
154 break;
155 case ERROR_ACCESS_DENIED:
156 case ERROR_CURRENT_DIRECTORY:
157 case ERROR_WRITE_PROTECT:
158 case ERROR_NETWORK_ACCESS_DENIED:
159 case ERROR_CANNOT_MAKE:
160 case ERROR_FAIL_I24:
161 case ERROR_SEEK_ON_DEVICE:
162 case ERROR_NOT_LOCKED:
163 case ERROR_LOCK_FAILED:
164 res = LV_FS_RES_DENIED;
165 break;
166 case ERROR_BUSY:
167 res = LV_FS_RES_BUSY;
168 break;
169 case ERROR_TIMEOUT:
170 res = LV_FS_RES_TOUT;
171 break;
172 case ERROR_NOT_SAME_DEVICE:
173 case ERROR_DIRECT_ACCESS_HANDLE:
174 res = LV_FS_RES_NOT_IMP;
175 break;
176 case ERROR_TOO_MANY_OPEN_FILES:
177 case ERROR_ARENA_TRASHED:
178 case ERROR_NOT_ENOUGH_MEMORY:
179 case ERROR_INVALID_BLOCK:
180 case ERROR_OUT_OF_PAPER:
181 case ERROR_SHARING_BUFFER_EXCEEDED:
182 case ERROR_NOT_ENOUGH_QUOTA:
183 res = LV_FS_RES_OUT_OF_MEM;
184 break;
185 case ERROR_INVALID_FUNCTION:
186 case ERROR_INVALID_ACCESS:
187 case ERROR_INVALID_DATA:
188 case ERROR_BAD_COMMAND:
189 case ERROR_BAD_LENGTH:
190 case ERROR_INVALID_PARAMETER:
191 case ERROR_NEGATIVE_SEEK:
192 res = LV_FS_RES_INV_PARAM;
193 break;
194 default:
195 res = LV_FS_RES_UNKNOWN;
196 break;
197 }
198
199 return res;
200 }
201
202 /**
203 * Open a file
204 * @param drv pointer to a driver where this function belongs
205 * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
206 * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
207 * @return pointer to FIL struct or NULL in case of fail
208 */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)209 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
210 {
211 LV_UNUSED(drv);
212
213 DWORD desired_access = 0;
214
215 if(mode & LV_FS_MODE_RD) {
216 desired_access |= GENERIC_READ;
217 }
218
219 if(mode & LV_FS_MODE_WR) {
220 desired_access |= GENERIC_WRITE;
221 }
222
223 /*Make the path relative to the current directory (the projects root folder)*/
224
225 char buf[MAX_PATH];
226 lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s", path);
227
228 return (void *)CreateFileA(
229 buf,
230 desired_access,
231 FILE_SHARE_READ,
232 NULL,
233 OPEN_EXISTING,
234 FILE_ATTRIBUTE_NORMAL,
235 NULL);
236 }
237
238 /**
239 * Close an opened file
240 * @param drv pointer to a driver where this function belongs
241 * @param file_p pointer to a FILE variable. (opened with fs_open)
242 * @return LV_FS_RES_OK: no error, the file is read
243 * any error from lv_fs_res_t enum
244 */
fs_close(lv_fs_drv_t * drv,void * file_p)245 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
246 {
247 LV_UNUSED(drv);
248 return CloseHandle((HANDLE)file_p)
249 ? LV_FS_RES_OK
250 : fs_error_from_win32(GetLastError());
251 }
252
253 /**
254 * Read data from an opened file
255 * @param drv pointer to a driver where this function belongs
256 * @param file_p pointer to a FILE variable.
257 * @param buf pointer to a memory block where to store the read data
258 * @param btr number of Bytes To Read
259 * @param br the real number of read bytes (Byte Read)
260 * @return LV_FS_RES_OK: no error, the file is read
261 * any error from lv_fs_res_t enum
262 */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)263 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
264 {
265 LV_UNUSED(drv);
266 return ReadFile((HANDLE)file_p, buf, btr, (LPDWORD)br, NULL)
267 ? LV_FS_RES_OK
268 : fs_error_from_win32(GetLastError());
269 }
270
271 /**
272 * Write into a file
273 * @param drv pointer to a driver where this function belongs
274 * @param file_p pointer to a FILE variable
275 * @param buf pointer to a buffer with the bytes to write
276 * @param btw Bytes To Write
277 * @param bw the number of real written bytes (Bytes Written). NULL if unused.
278 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
279 */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)280 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
281 {
282 LV_UNUSED(drv);
283 return WriteFile((HANDLE)file_p, buf, btw, (LPDWORD)bw, NULL)
284 ? LV_FS_RES_OK
285 : fs_error_from_win32(GetLastError());
286 }
287
288 /**
289 * Set the read write pointer. Also expand the file size if necessary.
290 * @param drv pointer to a driver where this function belongs
291 * @param file_p pointer to a FILE variable. (opened with fs_open )
292 * @param pos the new position of read write pointer
293 * @return LV_FS_RES_OK: no error, the file is read
294 * any error from lv_fs_res_t enum
295 */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)296 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
297 {
298 LV_UNUSED(drv);
299
300 DWORD move_method = (DWORD) -1;
301 if(whence == LV_FS_SEEK_SET) {
302 move_method = FILE_BEGIN;
303 }
304 else if(whence == LV_FS_SEEK_CUR) {
305 move_method = FILE_CURRENT;
306 }
307 else if(whence == LV_FS_SEEK_END) {
308 move_method = FILE_END;
309 }
310
311 LARGE_INTEGER distance_to_move;
312 distance_to_move.QuadPart = pos;
313 return SetFilePointerEx((HANDLE)file_p, distance_to_move, NULL, move_method)
314 ? LV_FS_RES_OK
315 : fs_error_from_win32(GetLastError());
316 }
317
318 /**
319 * Give the position of the read write pointer
320 * @param drv pointer to a driver where this function belongs
321 * @param file_p pointer to a FILE variable
322 * @param pos_p pointer to store the result
323 * @return LV_FS_RES_OK: no error, the file is read
324 * any error from lv_fs_res_t enum
325 */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)326 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
327 {
328 LV_UNUSED(drv);
329
330 if(!pos_p) {
331 return LV_FS_RES_INV_PARAM;
332 }
333
334 LARGE_INTEGER file_pointer;
335 file_pointer.QuadPart = 0;
336
337 LARGE_INTEGER distance_to_move;
338 distance_to_move.QuadPart = 0;
339 if(SetFilePointerEx(
340 (HANDLE)file_p,
341 distance_to_move,
342 &file_pointer,
343 FILE_CURRENT)) {
344 if(file_pointer.QuadPart > LONG_MAX) {
345 return LV_FS_RES_INV_PARAM;
346 }
347 else {
348 *pos_p = file_pointer.LowPart;
349 return LV_FS_RES_OK;
350 }
351 }
352 else {
353 return fs_error_from_win32(GetLastError());
354 }
355 }
356
357 /**
358 * Initialize a 'DIR' or 'HANDLE' variable for directory reading
359 * @param drv pointer to a driver where this function belongs
360 * @param path path to a directory
361 * @return pointer to an initialized 'DIR' or 'HANDLE' variable
362 */
fs_dir_open(lv_fs_drv_t * drv,const char * path)363 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
364 {
365 LV_UNUSED(drv);
366 dir_handle_t * handle = (dir_handle_t *)lv_malloc(sizeof(dir_handle_t));
367 handle->dir_p = INVALID_HANDLE_VALUE;
368 handle->next_error = LV_FS_RES_OK;
369 WIN32_FIND_DATAA fdata;
370
371 /*Make the path relative to the current directory (the projects root folder)*/
372 char buf[LV_FS_MAX_PATH_LEN];
373 lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s\\*", path);
374
375 lv_strcpy(handle->next_fn, "");
376 handle->dir_p = FindFirstFileA(buf, &fdata);
377
378 if(handle->dir_p != INVALID_HANDLE_VALUE) {
379 do {
380 if(is_dots_name(fdata.cFileName)) {
381 continue;
382 }
383 else {
384 if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
385 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
386 }
387 else {
388 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
389 }
390 break;
391 }
392 } while(FindNextFileA(handle->dir_p, &fdata));
393 }
394
395 if(handle->dir_p == INVALID_HANDLE_VALUE) {
396 lv_free(handle);
397 handle->next_error = fs_error_from_win32(GetLastError());
398 return INVALID_HANDLE_VALUE;
399 }
400 else {
401 handle->next_error = LV_FS_RES_OK;
402 return handle;
403 }
404 }
405
406 /**
407 * Read the next filename from a directory.
408 * The name of the directories will begin with '/'
409 * @param drv pointer to a driver where this function belongs
410 * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
411 * @param fn pointer to a buffer to store the filename
412 * @param fn_len length of the buffer to store the filename
413 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
414 */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn,uint32_t fn_len)415 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn, uint32_t fn_len)
416 {
417 LV_UNUSED(drv);
418 if(fn_len == 0) return LV_FS_RES_INV_PARAM;
419
420 dir_handle_t * handle = (dir_handle_t *)dir_p;
421 lv_strlcpy(fn, handle->next_fn, fn_len);
422 lv_fs_res_t current_error = handle->next_error;
423 lv_strcpy(handle->next_fn, "");
424
425 WIN32_FIND_DATAA fdata;
426
427 while(FindNextFileA(handle->dir_p, &fdata)) {
428 if(is_dots_name(fdata.cFileName)) {
429 continue;
430 }
431 else {
432 if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
433 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
434 }
435 else {
436 lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
437 }
438 break;
439 }
440 }
441
442 if(handle->next_fn[0] == '\0') {
443 handle->next_error = fs_error_from_win32(GetLastError());
444 }
445
446 return current_error;
447 }
448
449 /**
450 * Close the directory reading
451 * @param drv pointer to a driver where this function belongs
452 * @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
453 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
454 */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)455 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
456 {
457 LV_UNUSED(drv);
458 dir_handle_t * handle = (dir_handle_t *)dir_p;
459 lv_fs_res_t res = FindClose(handle->dir_p)
460 ? LV_FS_RES_OK
461 : fs_error_from_win32(GetLastError());
462 lv_free(handle);
463 return res;
464 }
465
466 #else /*LV_USE_FS_WIN32 == 0*/
467
468 #if defined(LV_FS_WIN32_LETTER) && LV_FS_WIN32_LETTER != '\0'
469 #warning "LV_USE_FS_WIN32 is not enabled but LV_FS_WIN32_LETTER is set"
470 #endif
471
472 #endif
473