1 /**
2  * @file lv_fs_fatfs.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../../lvgl.h"
10 
11 #if LV_USE_FS_FATFS
12 #include "ff.h"
13 
14 /*********************
15  *      DEFINES
16  *********************/
17 
18 #if LV_FS_FATFS_LETTER == '\0'
19     #error "LV_FS_FATFS_LETTER must be an upper case ASCII letter"
20 #endif
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 static void fs_init(void);
30 
31 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
32 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
33 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
34 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
35 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
36 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
37 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
38 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
39 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
40 
41 /**********************
42  *  STATIC VARIABLES
43  **********************/
44 
45 /**********************
46  *      MACROS
47  **********************/
48 
49 /**********************
50  *   GLOBAL FUNCTIONS
51  **********************/
52 
lv_fs_fatfs_init(void)53 void lv_fs_fatfs_init(void)
54 {
55     /*----------------------------------------------------
56      * Initialize your storage device and File System
57      * -------------------------------------------------*/
58     fs_init();
59 
60     /*---------------------------------------------------
61      * Register the file system interface in LVGL
62      *--------------------------------------------------*/
63 
64     /*Add a simple drive to open images*/
65     static lv_fs_drv_t fs_drv; /*A driver descriptor*/
66     lv_fs_drv_init(&fs_drv);
67 
68     /*Set up fields...*/
69     fs_drv.letter = LV_FS_FATFS_LETTER;
70     fs_drv.cache_size = LV_FS_FATFS_CACHE_SIZE;
71 
72     fs_drv.open_cb = fs_open;
73     fs_drv.close_cb = fs_close;
74     fs_drv.read_cb = fs_read;
75     fs_drv.write_cb = fs_write;
76     fs_drv.seek_cb = fs_seek;
77     fs_drv.tell_cb = fs_tell;
78 
79     fs_drv.dir_close_cb = fs_dir_close;
80     fs_drv.dir_open_cb = fs_dir_open;
81     fs_drv.dir_read_cb = fs_dir_read;
82 
83     lv_fs_drv_register(&fs_drv);
84 }
85 
86 /**********************
87  *   STATIC FUNCTIONS
88  **********************/
89 
90 /*Initialize your Storage device and File system.*/
fs_init(void)91 static void fs_init(void)
92 {
93     /*Initialize the SD card and FatFS itself.
94      *Better to do it in your code to keep this library untouched for easy updating*/
95 }
96 
97 /**
98  * Open a file
99  * @param drv pointer to a driver where this function belongs
100  * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
101  * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
102  * @return pointer to FIL struct or NULL in case of fail
103  */
fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)104 static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
105 {
106     LV_UNUSED(drv);
107     uint8_t flags = 0;
108 
109     if(mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS;
110     else if(mode == LV_FS_MODE_RD) flags = FA_READ;
111     else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
112 
113     FIL * f = lv_mem_alloc(sizeof(FIL));
114     if(f == NULL) return NULL;
115 
116     FRESULT res = f_open(f, path, flags);
117     if(res == FR_OK) {
118         return f;
119     }
120     else {
121         lv_mem_free(f);
122         return NULL;
123     }
124 }
125 
126 /**
127  * Close an opened file
128  * @param drv pointer to a driver where this function belongs
129  * @param file_p pointer to a FIL variable. (opened with fs_open)
130  * @return LV_FS_RES_OK: no error, the file is read
131  *         any error from lv_fs_res_t enum
132  */
fs_close(lv_fs_drv_t * drv,void * file_p)133 static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
134 {
135     LV_UNUSED(drv);
136     f_close(file_p);
137     lv_mem_free(file_p);
138     return LV_FS_RES_OK;
139 }
140 
141 /**
142  * Read data from an opened file
143  * @param drv pointer to a driver where this function belongs
144  * @param file_p pointer to a FIL variable.
145  * @param buf pointer to a memory block where to store the read data
146  * @param btr number of Bytes To Read
147  * @param br the real number of read bytes (Byte Read)
148  * @return LV_FS_RES_OK: no error, the file is read
149  *         any error from lv_fs_res_t enum
150  */
fs_read(lv_fs_drv_t * drv,void * file_p,void * buf,uint32_t btr,uint32_t * br)151 static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
152 {
153     LV_UNUSED(drv);
154     FRESULT res = f_read(file_p, buf, btr, (UINT *)br);
155     if(res == FR_OK) return LV_FS_RES_OK;
156     else return LV_FS_RES_UNKNOWN;
157 }
158 
159 /**
160  * Write into a file
161  * @param drv pointer to a driver where this function belongs
162  * @param file_p pointer to a FIL variable
163  * @param buf pointer to a buffer with the bytes to write
164  * @param btr Bytes To Write
165  * @param br the number of real written bytes (Bytes Written). NULL if unused.
166  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
167  */
fs_write(lv_fs_drv_t * drv,void * file_p,const void * buf,uint32_t btw,uint32_t * bw)168 static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
169 {
170     LV_UNUSED(drv);
171     FRESULT res = f_write(file_p, buf, btw, (UINT *)bw);
172     if(res == FR_OK) return LV_FS_RES_OK;
173     else return LV_FS_RES_UNKNOWN;
174 }
175 
176 /**
177  * Set the read write pointer. Also expand the file size if necessary.
178  * @param drv pointer to a driver where this function belongs
179  * @param file_p pointer to a FIL variable. (opened with fs_open )
180  * @param pos the new position of read write pointer
181  * @param whence only LV_SEEK_SET is supported
182  * @return LV_FS_RES_OK: no error, the file is read
183  *         any error from lv_fs_res_t enum
184  */
fs_seek(lv_fs_drv_t * drv,void * file_p,uint32_t pos,lv_fs_whence_t whence)185 static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
186 {
187     LV_UNUSED(drv);
188     switch(whence) {
189         case LV_FS_SEEK_SET:
190             f_lseek(file_p, pos);
191             break;
192         case LV_FS_SEEK_CUR:
193             f_lseek(file_p, f_tell((FIL *)file_p) + pos);
194             break;
195         case LV_FS_SEEK_END:
196             f_lseek(file_p, f_size((FIL *)file_p) + pos);
197             break;
198         default:
199             break;
200     }
201     return LV_FS_RES_OK;
202 }
203 
204 /**
205  * Give the position of the read write pointer
206  * @param drv pointer to a driver where this function belongs
207  * @param file_p pointer to a FIL variable.
208  * @param pos_p pointer to to store the result
209  * @return LV_FS_RES_OK: no error, the file is read
210  *         any error from lv_fs_res_t enum
211  */
fs_tell(lv_fs_drv_t * drv,void * file_p,uint32_t * pos_p)212 static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
213 {
214     LV_UNUSED(drv);
215     *pos_p = f_tell((FIL *)file_p);
216     return LV_FS_RES_OK;
217 }
218 
219 /**
220  * Initialize a 'DIR' variable for directory reading
221  * @param drv pointer to a driver where this function belongs
222  * @param path path to a directory
223  * @return pointer to an initialized 'DIR' variable
224  */
fs_dir_open(lv_fs_drv_t * drv,const char * path)225 static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
226 {
227     LV_UNUSED(drv);
228     DIR * d = lv_mem_alloc(sizeof(DIR));
229     if(d == NULL) return NULL;
230 
231     FRESULT res = f_opendir(d, path);
232     if(res != FR_OK) {
233         lv_mem_free(d);
234         d = NULL;
235     }
236     return d;
237 }
238 
239 /**
240  * Read the next filename from a directory.
241  * The name of the directories will begin with '/'
242  * @param drv pointer to a driver where this function belongs
243  * @param dir_p pointer to an initialized 'DIR' variable
244  * @param fn pointer to a buffer to store the filename
245  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
246  */
fs_dir_read(lv_fs_drv_t * drv,void * dir_p,char * fn)247 static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
248 {
249     LV_UNUSED(drv);
250     FRESULT res;
251     FILINFO fno;
252     fn[0] = '\0';
253 
254     do {
255         res = f_readdir(dir_p, &fno);
256         if(res != FR_OK) return LV_FS_RES_UNKNOWN;
257 
258         if(fno.fattrib & AM_DIR) {
259             fn[0] = '/';
260             strcpy(&fn[1], fno.fname);
261         }
262         else strcpy(fn, fno.fname);
263 
264     } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
265 
266     return LV_FS_RES_OK;
267 }
268 
269 /**
270  * Close the directory reading
271  * @param drv pointer to a driver where this function belongs
272  * @param dir_p pointer to an initialized 'DIR' variable
273  * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
274  */
fs_dir_close(lv_fs_drv_t * drv,void * dir_p)275 static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
276 {
277     LV_UNUSED(drv);
278     f_closedir(dir_p);
279     lv_mem_free(dir_p);
280     return LV_FS_RES_OK;
281 }
282 
283 #else /*LV_USE_FS_FATFS == 0*/
284 
285 #if defined(LV_FS_FATFS_LETTER) && LV_FS_FATFS_LETTER != '\0'
286     #warning "LV_USE_FS_FATFS is not enabled but LV_FS_FATFS_LETTER is set"
287 #endif
288 
289 #endif /*LV_USE_FS_POSIX*/
290 
291