1 // Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef __ESP_VFS_H__
16 #define __ESP_VFS_H__
17 
18 #include <stdint.h>
19 #include <stddef.h>
20 #include <stdarg.h>
21 #include <unistd.h>
22 #include <utime.h>
23 #include "freertos/FreeRTOS.h"
24 #include "freertos/semphr.h"
25 #include "esp_err.h"
26 #include <sys/types.h>
27 #include <sys/reent.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <sys/termios.h>
31 #include <sys/poll.h>
32 #include <sys/dirent.h>
33 #include <string.h>
34 #include "sdkconfig.h"
35 
36 #ifdef __cplusplus
37 extern "C" {
38 #endif
39 
40 #ifndef _SYS_TYPES_FD_SET
41 #error "VFS should be used with FD_SETSIZE and FD_SET from sys/types.h"
42 #endif
43 
44 /**
45  * Maximum number of (global) file descriptors.
46  */
47 #define MAX_FDS         FD_SETSIZE /* for compatibility with fd_set and select() */
48 
49 /**
50  * Maximum length of path prefix (not including zero terminator)
51  */
52 #define ESP_VFS_PATH_MAX 15
53 
54 /**
55  * Default value of flags member in esp_vfs_t structure.
56  */
57 #define ESP_VFS_FLAG_DEFAULT        0
58 
59 /**
60  * Flag which indicates that FS needs extra context pointer in syscalls.
61  */
62 #define ESP_VFS_FLAG_CONTEXT_PTR    1
63 
64 /*
65  * @brief VFS identificator used for esp_vfs_register_with_id()
66  */
67 typedef int esp_vfs_id_t;
68 
69 /**
70  * @brief VFS semaphore type for select()
71  *
72  */
73 typedef struct
74 {
75     bool is_sem_local;      /*!< type of "sem" is SemaphoreHandle_t when true, defined by socket driver otherwise */
76     void *sem;              /*!< semaphore instance */
77 } esp_vfs_select_sem_t;
78 
79 /**
80  * @brief VFS definition structure
81  *
82  * This structure should be filled with pointers to corresponding
83  * FS driver functions.
84  *
85  * VFS component will translate all FDs so that the filesystem implementation
86  * sees them starting at zero. The caller sees a global FD which is prefixed
87  * with an pre-filesystem-implementation.
88  *
89  * Some FS implementations expect some state (e.g. pointer to some structure)
90  * to be passed in as a first argument. For these implementations,
91  * populate the members of this structure which have _p suffix, set
92  * flags member to ESP_VFS_FLAG_CONTEXT_PTR and provide the context pointer
93  * to esp_vfs_register function.
94  * If the implementation doesn't use this extra argument, populate the
95  * members without _p suffix and set flags member to ESP_VFS_FLAG_DEFAULT.
96  *
97  * If the FS driver doesn't provide some of the functions, set corresponding
98  * members to NULL.
99  */
100 typedef struct
101 {
102     int flags;      /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */
103     union {
104         ssize_t (*write_p)(void* p, int fd, const void * data, size_t size);                         /*!< Write with context pointer */
105         ssize_t (*write)(int fd, const void * data, size_t size);                                    /*!< Write without context pointer */
106     };
107     union {
108         off_t (*lseek_p)(void* p, int fd, off_t size, int mode);                                     /*!< Seek with context pointer */
109         off_t (*lseek)(int fd, off_t size, int mode);                                                /*!< Seek without context pointer */
110     };
111     union {
112         ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size);                               /*!< Read with context pointer */
113         ssize_t (*read)(int fd, void * dst, size_t size);                                            /*!< Read without context pointer */
114     };
115     union {
116         ssize_t (*pread_p)(void *ctx, int fd, void * dst, size_t size, off_t offset);                /*!< pread with context pointer */
117         ssize_t (*pread)(int fd, void * dst, size_t size, off_t offset);                             /*!< pread without context pointer */
118     };
119     union {
120         ssize_t (*pwrite_p)(void *ctx, int fd, const void *src, size_t size, off_t offset);          /*!< pwrite with context pointer */
121         ssize_t (*pwrite)(int fd, const void *src, size_t size, off_t offset);                       /*!< pwrite without context pointer */
122     };
123     union {
124         int (*open_p)(void* ctx, const char * path, int flags, int mode);                            /*!< open with context pointer */
125         int (*open)(const char * path, int flags, int mode);                                         /*!< open without context pointer */
126     };
127     union {
128         int (*close_p)(void* ctx, int fd);                                                           /*!< close with context pointer */
129         int (*close)(int fd);                                                                        /*!< close without context pointer */
130     };
131     union {
132         int (*fstat_p)(void* ctx, int fd, struct stat * st);                                         /*!< fstat with context pointer */
133         int (*fstat)(int fd, struct stat * st);                                                      /*!< fstat without context pointer */
134     };
135 #ifdef CONFIG_VFS_SUPPORT_DIR
136     union {
137         int (*stat_p)(void* ctx, const char * path, struct stat * st);                               /*!< stat with context pointer */
138         int (*stat)(const char * path, struct stat * st);                                            /*!< stat without context pointer */
139     };
140     union {
141         int (*link_p)(void* ctx, const char* n1, const char* n2);                                    /*!< link with context pointer */
142         int (*link)(const char* n1, const char* n2);                                                 /*!< link without context pointer */
143     };
144     union {
145         int (*unlink_p)(void* ctx, const char *path);                                                /*!< unlink with context pointer */
146         int (*unlink)(const char *path);                                                             /*!< unlink without context pointer */
147     };
148     union {
149         int (*rename_p)(void* ctx, const char *src, const char *dst);                               /*!< rename with context pointer */
150         int (*rename)(const char *src, const char *dst);                                            /*!< rename without context pointer */
151     };
152     union {
153         DIR* (*opendir_p)(void* ctx, const char* name);                                             /*!< opendir with context pointer */
154         DIR* (*opendir)(const char* name);                                                          /*!< opendir without context pointer */
155     };
156     union {
157         struct dirent* (*readdir_p)(void* ctx, DIR* pdir);                                          /*!< readdir with context pointer */
158         struct dirent* (*readdir)(DIR* pdir);                                                       /*!< readdir without context pointer */
159     };
160     union {
161         int (*readdir_r_p)(void* ctx, DIR* pdir, struct dirent* entry, struct dirent** out_dirent); /*!< readdir_r with context pointer */
162         int (*readdir_r)(DIR* pdir, struct dirent* entry, struct dirent** out_dirent);              /*!< readdir_r without context pointer */
163     };
164     union {
165         long (*telldir_p)(void* ctx, DIR* pdir);                                                    /*!< telldir with context pointer */
166         long (*telldir)(DIR* pdir);                                                                 /*!< telldir without context pointer */
167     };
168     union {
169         void (*seekdir_p)(void* ctx, DIR* pdir, long offset);                                       /*!< seekdir with context pointer */
170         void (*seekdir)(DIR* pdir, long offset);                                                    /*!< seekdir without context pointer */
171     };
172     union {
173         int (*closedir_p)(void* ctx, DIR* pdir);                                                    /*!< closedir with context pointer */
174         int (*closedir)(DIR* pdir);                                                                 /*!< closedir without context pointer */
175     };
176     union {
177         int (*mkdir_p)(void* ctx, const char* name, mode_t mode);                                   /*!< mkdir with context pointer */
178         int (*mkdir)(const char* name, mode_t mode);                                                /*!< mkdir without context pointer */
179     };
180     union {
181         int (*rmdir_p)(void* ctx, const char* name);                                                /*!< rmdir with context pointer */
182         int (*rmdir)(const char* name);                                                             /*!< rmdir without context pointer */
183     };
184 #endif // CONFIG_VFS_SUPPORT_DIR
185     union {
186         int (*fcntl_p)(void* ctx, int fd, int cmd, int arg);                                        /*!< fcntl with context pointer */
187         int (*fcntl)(int fd, int cmd, int arg);                                                     /*!< fcntl without context pointer */
188     };
189     union {
190         int (*ioctl_p)(void* ctx, int fd, int cmd, va_list args);                                   /*!< ioctl with context pointer */
191         int (*ioctl)(int fd, int cmd, va_list args);                                                /*!< ioctl without context pointer */
192     };
193     union {
194         int (*fsync_p)(void* ctx, int fd);                                                          /*!< fsync with context pointer */
195         int (*fsync)(int fd);                                                                       /*!< fsync without context pointer */
196     };
197 #ifdef CONFIG_VFS_SUPPORT_DIR
198     union {
199         int (*access_p)(void* ctx, const char *path, int amode);                                    /*!< access with context pointer */
200         int (*access)(const char *path, int amode);                                                 /*!< access without context pointer */
201     };
202     union {
203         int (*truncate_p)(void* ctx, const char *path, off_t length);                               /*!< truncate with context pointer */
204         int (*truncate)(const char *path, off_t length);                                            /*!< truncate without context pointer */
205     };
206     union {
207         int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times);                   /*!< utime with context pointer */
208         int (*utime)(const char *path, const struct utimbuf *times);                                /*!< utime without context pointer */
209     };
210 #endif // CONFIG_VFS_SUPPORT_DIR
211 #ifdef CONFIG_VFS_SUPPORT_TERMIOS
212     union {
213         int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p);       /*!< tcsetattr with context pointer */
214         int (*tcsetattr)(int fd, int optional_actions, const struct termios *p);                    /*!< tcsetattr without context pointer */
215     };
216     union {
217         int (*tcgetattr_p)(void *ctx, int fd, struct termios *p);                                   /*!< tcgetattr with context pointer */
218         int (*tcgetattr)(int fd, struct termios *p);                                                /*!< tcgetattr without context pointer */
219     };
220     union {
221         int (*tcdrain_p)(void *ctx, int fd);                                                        /*!< tcdrain with context pointer */
222         int (*tcdrain)(int fd);                                                                     /*!< tcdrain without context pointer */
223     };
224     union {
225         int (*tcflush_p)(void *ctx, int fd, int select);                                            /*!< tcflush with context pointer */
226         int (*tcflush)(int fd, int select);                                                         /*!< tcflush without context pointer */
227     };
228     union {
229         int (*tcflow_p)(void *ctx, int fd, int action);                                             /*!< tcflow with context pointer */
230         int (*tcflow)(int fd, int action);                                                          /*!< tcflow without context pointer */
231     };
232     union {
233         pid_t (*tcgetsid_p)(void *ctx, int fd);                                                     /*!< tcgetsid with context pointer */
234         pid_t (*tcgetsid)(int fd);                                                                  /*!< tcgetsid without context pointer */
235     };
236     union {
237         int (*tcsendbreak_p)(void *ctx, int fd, int duration);                                      /*!< tcsendbreak with context pointer */
238         int (*tcsendbreak)(int fd, int duration);                                                   /*!< tcsendbreak without context pointer */
239     };
240 #endif // CONFIG_VFS_SUPPORT_TERMIOS
241 #ifdef CONFIG_VFS_SUPPORT_SELECT
242     /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */
243     esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, esp_vfs_select_sem_t sem, void **end_select_args);
244     /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */
245     int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
246     /** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */
247     void (*stop_socket_select)(void *sem);
248     /** stop_socket_select which can be called from ISR; set only for the socket driver */
249     void (*stop_socket_select_isr)(void *sem, BaseType_t *woken);
250     /** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */
251     void* (*get_socket_select_semaphore)(void);
252     /** get_socket_select_semaphore returns semaphore allocated in the socket driver; set only for the socket driver */
253     esp_err_t (*end_select)(void *end_select_args);
254 #endif // CONFIG_VFS_SUPPORT_SELECT
255 } esp_vfs_t;
256 
257 
258 /**
259  * Register a virtual filesystem for given path prefix.
260  *
261  * @param base_path  file path prefix associated with the filesystem.
262  *                   Must be a zero-terminated C string, may be empty.
263  *                   If not empty, must be up to ESP_VFS_PATH_MAX
264  *                   characters long, and at least 2 characters long.
265  *                   Name must start with a "/" and must not end with "/".
266  *                   For example, "/data" or "/dev/spi" are valid.
267  *                   These VFSes would then be called to handle file paths such as
268  *                   "/data/myfile.txt" or "/dev/spi/0".
269  *                   In the special case of an empty base_path, a "fallback"
270  *                   VFS is registered. Such VFS will handle paths which are not
271  *                   matched by any other registered VFS.
272  * @param vfs  Pointer to esp_vfs_t, a structure which maps syscalls to
273  *             the filesystem driver functions. VFS component doesn't
274  *             assume ownership of this pointer.
275  * @param ctx  If vfs->flags has ESP_VFS_FLAG_CONTEXT_PTR set, a pointer
276  *             which should be passed to VFS functions. Otherwise, NULL.
277  *
278  * @return  ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
279  *          registered.
280  */
281 esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx);
282 
283 
284 /**
285  * Special case function for registering a VFS that uses a method other than
286  * open() to open new file descriptors from the interval <min_fd; max_fd).
287  *
288  * This is a special-purpose function intended for registering LWIP sockets to VFS.
289  *
290  * @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
291  * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
292  * @param min_fd The smallest file descriptor this VFS will use.
293  * @param max_fd Upper boundary for file descriptors this VFS will use (the biggest file descriptor plus one).
294  *
295  * @return  ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
296  *          registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries
297  *          are incorrect.
298  */
299 esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd);
300 
301 /**
302  * Special case function for registering a VFS that uses a method other than
303  * open() to open new file descriptors. In comparison with
304  * esp_vfs_register_fd_range, this function doesn't pre-registers an interval
305  * of file descriptors. File descriptors can be registered later, by using
306  * esp_vfs_register_fd.
307  *
308  * @param vfs Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
309  * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
310  * @param vfs_id Here will be written the VFS ID which can be passed to
311  *               esp_vfs_register_fd for registering file descriptors.
312  *
313  * @return  ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
314  *          registered, ESP_ERR_INVALID_ARG if the file descriptor boundaries
315  *          are incorrect.
316  */
317 esp_err_t esp_vfs_register_with_id(const esp_vfs_t *vfs, void *ctx, esp_vfs_id_t *vfs_id);
318 
319 /**
320  * Unregister a virtual filesystem for given path prefix
321  *
322  * @param base_path  file prefix previously used in esp_vfs_register call
323  * @return ESP_OK if successful, ESP_ERR_INVALID_STATE if VFS for given prefix
324  *         hasn't been registered
325  */
326 esp_err_t esp_vfs_unregister(const char* base_path);
327 
328 /**
329  * Special function for registering another file descriptor for a VFS registered
330  * by esp_vfs_register_with_id.
331  *
332  * @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
333  * @param fd The registered file descriptor will be written to this address.
334  *
335  * @return  ESP_OK if the registration is successful,
336  *          ESP_ERR_NO_MEM if too many file descriptors are registered,
337  *          ESP_ERR_INVALID_ARG if the arguments are incorrect.
338  */
339 esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd);
340 
341 /**
342  * Special function for unregistering a file descriptor belonging to a VFS
343  * registered by esp_vfs_register_with_id.
344  *
345  * @param vfs_id VFS identificator returned by esp_vfs_register_with_id.
346  * @param fd File descriptor which should be unregistered.
347  *
348  * @return  ESP_OK if the registration is successful,
349  *          ESP_ERR_INVALID_ARG if the arguments are incorrect.
350  */
351 esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd);
352 
353 /**
354  * These functions are to be used in newlib syscall table. They will be called by
355  * newlib when it needs to use any of the syscalls.
356  */
357 /**@{*/
358 ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size);
359 off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode);
360 ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size);
361 int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode);
362 int esp_vfs_close(struct _reent *r, int fd);
363 int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st);
364 int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st);
365 int esp_vfs_link(struct _reent *r, const char* n1, const char* n2);
366 int esp_vfs_unlink(struct _reent *r, const char *path);
367 int esp_vfs_rename(struct _reent *r, const char *src, const char *dst);
368 int esp_vfs_utime(const char *path, const struct utimbuf *times);
369 /**@}*/
370 
371 /**
372  * @brief Synchronous I/O multiplexing which implements the functionality of POSIX select() for VFS
373  * @param nfds      Specifies the range of descriptors which should be checked.
374  *                  The first nfds descriptors will be checked in each set.
375  * @param readfds   If not NULL, then points to a descriptor set that on input
376  *                  specifies which descriptors should be checked for being
377  *                  ready to read, and on output indicates which descriptors
378  *                  are ready to read.
379  * @param writefds  If not NULL, then points to a descriptor set that on input
380  *                  specifies which descriptors should be checked for being
381  *                  ready to write, and on output indicates which descriptors
382  *                  are ready to write.
383  * @param errorfds  If not NULL, then points to a descriptor set that on input
384  *                  specifies which descriptors should be checked for error
385  *                  conditions, and on output indicates which descriptors
386  *                  have error conditions.
387  * @param timeout   If not NULL, then points to timeval structure which
388  *                  specifies the time period after which the functions should
389  *                  time-out and return. If it is NULL, then the function will
390  *                  not time-out.
391  *
392  * @return      The number of descriptors set in the descriptor sets, or -1
393  *              when an error (specified by errno) have occurred.
394  */
395 int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
396 
397 /**
398  * @brief Notification from a VFS driver about a read/write/error condition
399  *
400  * This function is called when the VFS driver detects a read/write/error
401  * condition as it was requested by the previous call to start_select.
402  *
403  * @param sem semaphore structure which was passed to the driver by the start_select call
404  */
405 void esp_vfs_select_triggered(esp_vfs_select_sem_t sem);
406 
407 /**
408  * @brief Notification from a VFS driver about a read/write/error condition (ISR version)
409  *
410  * This function is called when the VFS driver detects a read/write/error
411  * condition as it was requested by the previous call to start_select.
412  *
413  * @param sem semaphore structure which was passed to the driver by the start_select call
414  * @param woken is set to pdTRUE if the function wakes up a task with higher priority
415  */
416 void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem, BaseType_t *woken);
417 
418 /**
419  *
420  * @brief Implements the VFS layer of POSIX pread()
421  *
422  * @param fd         File descriptor used for read
423  * @param dst        Pointer to the buffer where the output will be written
424  * @param size       Number of bytes to be read
425  * @param offset     Starting offset of the read
426  *
427  * @return           A positive return value indicates the number of bytes read. -1 is return on failure and errno is
428  *                   set accordingly.
429  */
430 ssize_t esp_vfs_pread(int fd, void *dst, size_t size, off_t offset);
431 
432 /**
433  *
434  * @brief Implements the VFS layer of POSIX pwrite()
435  *
436  * @param fd         File descriptor used for write
437  * @param src        Pointer to the buffer from where the output will be read
438  * @param size       Number of bytes to write
439  * @param offset     Starting offset of the write
440  *
441  * @return           A positive return value indicates the number of bytes written. -1 is return on failure and errno is
442  *                   set accordingly.
443  */
444 ssize_t esp_vfs_pwrite(int fd, const void *src, size_t size, off_t offset);
445 
446 #ifdef __cplusplus
447 } // extern "C"
448 #endif
449 
450 #endif //__ESP_VFS_H__
451