1 /*
2  * Copyright (c) 2018-2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <lvgl.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/fs/fs.h>
10 #include "lvgl_fs.h"
11 #include "lv_conf.h"
12 #include LV_STDLIB_INCLUDE
13 
lvgl_fs_ready(lv_fs_drv_t * drv)14 static bool lvgl_fs_ready(lv_fs_drv_t *drv)
15 {
16 	return true;
17 }
18 
errno_to_lv_fs_res(int err)19 static lv_fs_res_t errno_to_lv_fs_res(int err)
20 {
21 	switch (err) {
22 	case 0:
23 		return LV_FS_RES_OK;
24 	case -EIO:
25 		/*Low level hardware error*/
26 		return LV_FS_RES_HW_ERR;
27 	case -EBADF:
28 		/*Error in the file system structure */
29 		return LV_FS_RES_FS_ERR;
30 	case -ENOENT:
31 		/*Driver, file or directory is not exists*/
32 		return LV_FS_RES_NOT_EX;
33 	case -EFBIG:
34 		/*Disk full*/
35 		return LV_FS_RES_FULL;
36 	case -EACCES:
37 		/*Access denied. Check 'fs_open' modes and write protect*/
38 		return LV_FS_RES_DENIED;
39 	case -EBUSY:
40 		/*The file system now can't handle it, try later*/
41 		return LV_FS_RES_BUSY;
42 	case -ENOMEM:
43 		/*Not enough memory for an internal operation*/
44 		return LV_FS_RES_OUT_OF_MEM;
45 	case -EINVAL:
46 		/*Invalid parameter among arguments*/
47 		return LV_FS_RES_INV_PARAM;
48 	case -ENOTSUP:
49 		/*Not supported by the filesystem*/
50 		return LV_FS_RES_NOT_IMP;
51 	default:
52 		return LV_FS_RES_UNKNOWN;
53 	}
54 }
55 
lvgl_fs_open(lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)56 static void *lvgl_fs_open(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode)
57 {
58 	int err;
59 	int zmode = FS_O_CREATE;
60 	void *file;
61 
62 	/* LVGL is passing absolute paths without the root slash add it back
63 	 * by decrementing the path pointer.
64 	 */
65 	path--;
66 
67 	zmode |= (mode & LV_FS_MODE_WR) ? FS_O_WRITE : 0;
68 	zmode |= (mode & LV_FS_MODE_RD) ? FS_O_READ : 0;
69 
70 	file = lv_malloc(sizeof(struct fs_file_t));
71 	if (!file) {
72 		return NULL;
73 	}
74 
75 	fs_file_t_init((struct fs_file_t *)file);
76 
77 	err = fs_open((struct fs_file_t *)file, path, zmode);
78 	if (err) {
79 		lv_free(file);
80 		return NULL;
81 	}
82 
83 	return file;
84 }
85 
lvgl_fs_close(lv_fs_drv_t * drv,void * file)86 static lv_fs_res_t lvgl_fs_close(lv_fs_drv_t *drv, void *file)
87 {
88 	int err;
89 
90 	err = fs_close((struct fs_file_t *)file);
91 	lv_free(file);
92 	return errno_to_lv_fs_res(err);
93 }
94 
lvgl_fs_read(lv_fs_drv_t * drv,void * file,void * buf,uint32_t btr,uint32_t * br)95 static lv_fs_res_t lvgl_fs_read(lv_fs_drv_t *drv, void *file, void *buf, uint32_t btr, uint32_t *br)
96 {
97 	int err;
98 
99 	err = fs_read((struct fs_file_t *)file, buf, btr);
100 	if (err > 0) {
101 		if (br != NULL) {
102 			*br = err;
103 		}
104 		err = 0;
105 	} else if (br != NULL) {
106 		*br = 0U;
107 	}
108 	return errno_to_lv_fs_res(err);
109 }
110 
lvgl_fs_write(lv_fs_drv_t * drv,void * file,const void * buf,uint32_t btw,uint32_t * bw)111 static lv_fs_res_t lvgl_fs_write(lv_fs_drv_t *drv, void *file, const void *buf, uint32_t btw,
112 				 uint32_t *bw)
113 {
114 	int err;
115 
116 	err = fs_write((struct fs_file_t *)file, buf, btw);
117 	if (err == btw) {
118 		if (bw != NULL) {
119 			*bw = btw;
120 		}
121 		err = 0;
122 	} else if (err < 0) {
123 		if (bw != NULL) {
124 			*bw = 0U;
125 		}
126 	} else {
127 		if (bw != NULL) {
128 			*bw = err;
129 		}
130 		err = -EFBIG;
131 	}
132 	return errno_to_lv_fs_res(err);
133 }
134 
lvgl_fs_seek(lv_fs_drv_t * drv,void * file,uint32_t pos,lv_fs_whence_t whence)135 static lv_fs_res_t lvgl_fs_seek(lv_fs_drv_t *drv, void *file, uint32_t pos, lv_fs_whence_t whence)
136 {
137 	int err, fs_whence;
138 
139 	switch (whence) {
140 	case LV_FS_SEEK_END:
141 		fs_whence = FS_SEEK_END;
142 		break;
143 	case LV_FS_SEEK_CUR:
144 		fs_whence = FS_SEEK_CUR;
145 		break;
146 	case LV_FS_SEEK_SET:
147 	default:
148 		fs_whence = FS_SEEK_SET;
149 		break;
150 	}
151 
152 	err = fs_seek((struct fs_file_t *)file, pos, fs_whence);
153 	return errno_to_lv_fs_res(err);
154 }
155 
lvgl_fs_tell(lv_fs_drv_t * drv,void * file,uint32_t * pos_p)156 static lv_fs_res_t lvgl_fs_tell(lv_fs_drv_t *drv, void *file, uint32_t *pos_p)
157 {
158 	off_t pos;
159 
160 	pos = fs_tell((struct fs_file_t *)file);
161 	if (pos < 0) {
162 		return errno_to_lv_fs_res(pos);
163 	}
164 
165 	*pos_p = pos;
166 	return LV_FS_RES_OK;
167 }
168 
lvgl_fs_dir_open(lv_fs_drv_t * drv,const char * path)169 static void *lvgl_fs_dir_open(lv_fs_drv_t *drv, const char *path)
170 {
171 	void *dir;
172 	int err;
173 
174 	/* LVGL is passing absolute paths without the root slash add it back
175 	 * by decrementing the path pointer.
176 	 */
177 	path--;
178 
179 	dir = lv_malloc(sizeof(struct fs_dir_t));
180 	if (!dir) {
181 		return NULL;
182 	}
183 
184 	fs_dir_t_init((struct fs_dir_t *)dir);
185 	err = fs_opendir((struct fs_dir_t *)dir, path);
186 	if (err) {
187 		lv_free(dir);
188 		return NULL;
189 	}
190 
191 	return dir;
192 }
193 
lvgl_fs_dir_read(lv_fs_drv_t * drv,void * dir,char * fn,uint32_t fn_len)194 static lv_fs_res_t lvgl_fs_dir_read(lv_fs_drv_t *drv, void *dir, char *fn, uint32_t fn_len)
195 {
196 	/* LVGL expects a string as return parameter but the format of the
197 	 * string is not documented.
198 	 */
199 	return LV_FS_RES_NOT_IMP;
200 }
201 
lvgl_fs_dir_close(lv_fs_drv_t * drv,void * dir)202 static lv_fs_res_t lvgl_fs_dir_close(lv_fs_drv_t *drv, void *dir)
203 {
204 	int err;
205 
206 	err = fs_closedir((struct fs_dir_t *)dir);
207 	lv_free(dir);
208 	return errno_to_lv_fs_res(err);
209 }
210 
211 static lv_fs_drv_t fs_drv;
212 
lvgl_fs_init(void)213 void lvgl_fs_init(void)
214 {
215 	lv_fs_drv_init(&fs_drv);
216 
217 	/* LVGL uses letter based mount points, just pass the root slash as a
218 	 * letter. Note that LVGL will remove the drive letter, or in this case
219 	 * the root slash, from the path passed via the FS callbacks.
220 	 * Zephyr FS API assumes this slash is present so we will need to add
221 	 * it back.
222 	 */
223 	fs_drv.letter = '/';
224 	fs_drv.ready_cb = lvgl_fs_ready;
225 
226 	fs_drv.open_cb = lvgl_fs_open;
227 	fs_drv.close_cb = lvgl_fs_close;
228 	fs_drv.read_cb = lvgl_fs_read;
229 	fs_drv.write_cb = lvgl_fs_write;
230 	fs_drv.seek_cb = lvgl_fs_seek;
231 	fs_drv.tell_cb = lvgl_fs_tell;
232 
233 	fs_drv.dir_open_cb = lvgl_fs_dir_open;
234 	fs_drv.dir_read_cb = lvgl_fs_dir_read;
235 	fs_drv.dir_close_cb = lvgl_fs_dir_close;
236 
237 	lv_fs_drv_register(&fs_drv);
238 }
239