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_MEM_CUSTOM_INCLUDE
13 
lvgl_fs_ready(struct _lv_fs_drv_t * drv)14 static bool lvgl_fs_ready(struct _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(struct _lv_fs_drv_t * drv,const char * path,lv_fs_mode_t mode)56 static void *lvgl_fs_open(struct _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_MEM_CUSTOM_ALLOC(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_MEM_CUSTOM_FREE(file);
80 		return NULL;
81 	}
82 
83 	return file;
84 }
85 
lvgl_fs_close(struct _lv_fs_drv_t * drv,void * file)86 static lv_fs_res_t lvgl_fs_close(struct _lv_fs_drv_t *drv, void *file)
87 {
88 	int err;
89 
90 	err = fs_close((struct fs_file_t *)file);
91 	LV_MEM_CUSTOM_FREE(file);
92 	return errno_to_lv_fs_res(err);
93 }
94 
lvgl_fs_read(struct _lv_fs_drv_t * drv,void * file,void * buf,uint32_t btr,uint32_t * br)95 static lv_fs_res_t lvgl_fs_read(struct _lv_fs_drv_t *drv, void *file, void *buf, uint32_t btr,
96 				uint32_t *br)
97 {
98 	int err;
99 
100 	err = fs_read((struct fs_file_t *)file, buf, btr);
101 	if (err > 0) {
102 		if (br != NULL) {
103 			*br = err;
104 		}
105 		err = 0;
106 	} else if (br != NULL) {
107 		*br = 0U;
108 	}
109 	return errno_to_lv_fs_res(err);
110 }
111 
lvgl_fs_write(struct _lv_fs_drv_t * drv,void * file,const void * buf,uint32_t btw,uint32_t * bw)112 static lv_fs_res_t lvgl_fs_write(struct _lv_fs_drv_t *drv, void *file, const void *buf,
113 				 uint32_t btw, uint32_t *bw)
114 {
115 	int err;
116 
117 	err = fs_write((struct fs_file_t *)file, buf, btw);
118 	if (err == btw) {
119 		if (bw != NULL) {
120 			*bw = btw;
121 		}
122 		err = 0;
123 	} else if (err < 0) {
124 		if (bw != NULL) {
125 			*bw = 0U;
126 		}
127 	} else {
128 		if (bw != NULL) {
129 			*bw = err;
130 		}
131 		err = -EFBIG;
132 	}
133 	return errno_to_lv_fs_res(err);
134 }
135 
lvgl_fs_seek(struct _lv_fs_drv_t * drv,void * file,uint32_t pos,lv_fs_whence_t whence)136 static lv_fs_res_t lvgl_fs_seek(struct _lv_fs_drv_t *drv, void *file, uint32_t pos,
137 				lv_fs_whence_t whence)
138 {
139 	int err, fs_whence;
140 
141 	switch (whence) {
142 	case LV_FS_SEEK_END:
143 		fs_whence = FS_SEEK_END;
144 		break;
145 	case LV_FS_SEEK_CUR:
146 		fs_whence = FS_SEEK_CUR;
147 		break;
148 	case LV_FS_SEEK_SET:
149 	default:
150 		fs_whence = FS_SEEK_SET;
151 		break;
152 	}
153 
154 	err = fs_seek((struct fs_file_t *)file, pos, fs_whence);
155 	return errno_to_lv_fs_res(err);
156 }
157 
lvgl_fs_tell(struct _lv_fs_drv_t * drv,void * file,uint32_t * pos_p)158 static lv_fs_res_t lvgl_fs_tell(struct _lv_fs_drv_t *drv, void *file, uint32_t *pos_p)
159 {
160 	off_t pos;
161 
162 	pos = fs_tell((struct fs_file_t *)file);
163 	if (pos < 0) {
164 		return errno_to_lv_fs_res(pos);
165 	}
166 
167 	*pos_p = pos;
168 	return LV_FS_RES_OK;
169 }
170 
lvgl_fs_dir_open(struct _lv_fs_drv_t * drv,const char * path)171 static void *lvgl_fs_dir_open(struct _lv_fs_drv_t *drv, const char *path)
172 {
173 	void *dir;
174 	int err;
175 
176 	/* LVGL is passing absolute paths without the root slash add it back
177 	 * by decrementing the path pointer.
178 	 */
179 	path--;
180 
181 	dir = LV_MEM_CUSTOM_ALLOC(sizeof(struct fs_dir_t));
182 	if (!dir) {
183 		return NULL;
184 	}
185 
186 	fs_dir_t_init((struct fs_dir_t *)dir);
187 	err = fs_opendir((struct fs_dir_t *)dir, path);
188 	if (err) {
189 		LV_MEM_CUSTOM_FREE(dir);
190 		return NULL;
191 	}
192 
193 	return dir;
194 }
195 
lvgl_fs_dir_read(struct _lv_fs_drv_t * drv,void * dir,char * fn)196 static lv_fs_res_t lvgl_fs_dir_read(struct _lv_fs_drv_t *drv, void *dir, char *fn)
197 {
198 	/* LVGL expects a string as return parameter but the format of the
199 	 * string is not documented.
200 	 */
201 	return LV_FS_RES_NOT_IMP;
202 }
203 
lvgl_fs_dir_close(struct _lv_fs_drv_t * drv,void * dir)204 static lv_fs_res_t lvgl_fs_dir_close(struct _lv_fs_drv_t *drv, void *dir)
205 {
206 	int err;
207 
208 	err = fs_closedir((struct fs_dir_t *)dir);
209 	LV_MEM_CUSTOM_FREE(dir);
210 	return errno_to_lv_fs_res(err);
211 }
212 
213 static lv_fs_drv_t fs_drv;
214 
lvgl_fs_init(void)215 void lvgl_fs_init(void)
216 {
217 	lv_fs_drv_init(&fs_drv);
218 
219 	/* LVGL uses letter based mount points, just pass the root slash as a
220 	 * letter. Note that LVGL will remove the drive letter, or in this case
221 	 * the root slash, from the path passed via the FS callbacks.
222 	 * Zephyr FS API assumes this slash is present so we will need to add
223 	 * it back.
224 	 */
225 	fs_drv.letter = '/';
226 	fs_drv.ready_cb = lvgl_fs_ready;
227 
228 	fs_drv.open_cb = lvgl_fs_open;
229 	fs_drv.close_cb = lvgl_fs_close;
230 	fs_drv.read_cb = lvgl_fs_read;
231 	fs_drv.write_cb = lvgl_fs_write;
232 	fs_drv.seek_cb = lvgl_fs_seek;
233 	fs_drv.tell_cb = lvgl_fs_tell;
234 
235 	fs_drv.dir_open_cb = lvgl_fs_dir_open;
236 	fs_drv.dir_read_cb = lvgl_fs_dir_read;
237 	fs_drv.dir_close_cb = lvgl_fs_dir_close;
238 
239 	lv_fs_drv_register(&fs_drv);
240 }
241