diff --git a/include/misc/fdtable.h b/include/misc/fdtable.h new file mode 100644 index 00000000000..8383bcc1678 --- /dev/null +++ b/include/misc/fdtable.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_POSIX_POSIX__FDTABLE_H_ +#define ZEPHYR_INCLUDE_POSIX_POSIX__FDTABLE_H_ + +#include +/* FIXME: For native_posix ssize_t, off_t. */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * File descriptor virtual method table. + * All operations beyond read/write go thru ioctl method. + */ +struct fd_op_vtable { + ssize_t (*read)(void *obj, void *buf, size_t sz); + ssize_t (*write)(void *obj, const void *buf, size_t sz); + int (*ioctl)(void *obj, unsigned int request, ...); +}; + +/** + * @brief Reserve file descriptor. + * + * This function allows to reserve a space for file descriptor entry in + * the underlying table, and thus allows caller to fail fast if no free + * descriptor is available. If this function succeeds, z_finalize_fd() + * or z_free_fd() must be called mandatorily. + * + * @return Allocated file descriptor, or -1 in case of error (errno is set) + */ +int z_reserve_fd(void); + +/** + * @brief Finalize creation of file descriptor. + * + * This function should be called exactly once after z_reserve_fd(), and + * should not be called in any other case. + * + * @param fd File descriptor previously returned by z_reserve_fd() + * @param obj pointer to I/O object structure + * @param vtable pointer to I/O operation implementations for the object + */ +void z_finalize_fd(int fd, void *obj, const struct fd_op_vtable *vtable); + +/** + * @brief Allocate file descriptor for underlying I/O object. + * + * This function combines operations of z_reserve_fd() and z_finalize_fd() + * in one step, and provided for convenience. + * + * @param obj pointer to I/O object structure + * @param vtable pointer to I/O operation implementations for the object + * + * @return Allocated file descriptor, or -1 in case of error (errno is set) + */ +int z_alloc_fd(void *obj, const struct fd_op_vtable *vtable); + +/** + * @brief Release reserved file descriptor. + * + * This function may be called once after z_reserve_fd(), and should + * not be called in any other case. + * + * @param fd File descriptor previously returned by z_reserve_fd() + */ +void z_free_fd(int fd); + +/** + * @brief Get underlying object pointer from file descriptor. + * + * This function is useful for functions other than read/write/ioctl + * to look up underlying I/O object by fd, optionally checking its + * type (using vtable reference). If fd refers to invalid entry, + * NULL will be returned with errno set to EBADF. If fd is valid, + * but vtable param is not NULL and doesn't match object's vtable, + * NULL is returned and errno set to err param. + * + * @param fd File descriptor previously returned by z_reserve_fd() + * @param vtable Expected object vtable or NULL + * @param err errno value to set if object vtable doesn't match + * + * @return Object pointer or NULL, with errno set + */ +void *z_get_fd_obj(int fd, const struct fd_op_vtable *vtable, int err); + +/** + * Request codes for fd_op_vtable.ioctl(). + * + * Note that these codes are internal Zephyr numbers, for internal + * Zephyr operation (and subject to change without notice, not part + * of "stable ABI"). These are however expected to co-exist with + * "well-known" POSIX/Linux ioctl numbers, and not clash with them. + */ +enum { + ZFD_IOCTL_CLOSE = 1, + ZFD_IOCTL_FSYNC, + ZFD_IOCTL_LSEEK, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_POSIX_POSIX__FDTABLE_H_ */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index c6770e64f2f..60149523c88 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,5 @@ zephyr_sources(thread_entry.c) +zephyr_sources(fdtable.c) add_subdirectory(crc) add_subdirectory_ifdef(CONFIG_JSON_LIBRARY json) if(NOT CONFIG_NATIVE_APPLICATION) diff --git a/lib/fdtable.c b/lib/fdtable.c new file mode 100644 index 00000000000..8c835b0fe4f --- /dev/null +++ b/lib/fdtable.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief File descriptor table + * + * This file provides generic file descriptor table implementation, suitable + * for any I/O object implementing POSIX I/O semantics (i.e. read/write + + * aux operations). + */ + +#include +#include +#include + +struct fd_entry { + void *obj; + const struct fd_op_vtable *vtable; +}; + +static struct fd_entry fdtable[CONFIG_POSIX_MAX_FDS]; + +static K_MUTEX_DEFINE(fdtable_lock); + +static int _find_fd_entry(void) +{ + int fd; + + for (fd = 0; fd < ARRAY_SIZE(fdtable); fd++) { + if (fdtable[fd].obj == NULL) { + return fd; + } + } + + errno = ENFILE; + return -1; +} + +static int _check_fd(int fd) +{ + if (fd < 0 || fd >= ARRAY_SIZE(fdtable) || fdtable[fd].obj == NULL) { + errno = EBADF; + return -1; + } + + return 0; +} + +void *z_get_fd_obj(int fd, const struct fd_op_vtable *vtable, int err) +{ + struct fd_entry *fd_entry; + + if (_check_fd(fd) < 0) { + return NULL; + } + + fd_entry = &fdtable[fd]; + + if (vtable != NULL && fd_entry->vtable != vtable) { + errno = err; + return NULL; + } + + return fd_entry->obj; +} + +int z_reserve_fd(void) +{ + int fd; + + (void)k_mutex_lock(&fdtable_lock, K_FOREVER); + + fd = _find_fd_entry(); + if (fd >= 0) { + /* Mark entry as used, z_finalize_fd() will fill it in. */ + fdtable[fd].obj = FD_OBJ_RESERVED; + } + + k_mutex_unlock(&fdtable_lock); + + return fd; +} + +void z_finalize_fd(int fd, void *obj, const struct fd_op_vtable *vtable) +{ + /* Assumes fd was already bounds-checked. */ + fdtable[fd].obj = obj; + fdtable[fd].vtable = vtable; +} + +void z_free_fd(int fd) +{ + /* Assumes fd was already bounds-checked. */ + fdtable[fd].obj = NULL; +} + +int z_alloc_fd(void *obj, const struct fd_op_vtable *vtable) +{ + int fd; + + fd = z_reserve_fd(); + if (fd >= 0) { + z_finalize_fd(fd, obj, vtable); + } + + return fd; +} + +#ifdef CONFIG_POSIX_API + +ssize_t read(int fd, void *buf, size_t sz) +{ + if (_check_fd(fd) < 0) { + return -1; + } + + return fdtable[fd].vtable->read(fdtable[fd].obj, buf, sz); +} + +ssize_t write(int fd, const void *buf, size_t sz) +{ + if (_check_fd(fd) < 0) { + return -1; + } + + return fdtable[fd].vtable->write(fdtable[fd].obj, buf, sz); +} + +int close(int fd) +{ + int res; + + if (_check_fd(fd) < 0) { + return -1; + } + + res = fdtable[fd].vtable->ioctl(fdtable[fd].obj, ZFD_IOCTL_CLOSE); + z_free_fd(fd); + + return res; +} + +int fsync(int fd) +{ + int res; + + if (_check_fd(fd) < 0) { + return -1; + } + + res = fdtable[fd].vtable->ioctl(fdtable[fd].obj, ZFD_IOCTL_FSYNC); + z_free_fd(fd); + + return res; +} + +off_t lseek(int fd, off_t offset, int whence) +{ + if (_check_fd(fd) < 0) { + return -1; + } + + return fdtable[fd].vtable->ioctl(fdtable[fd].obj, ZFD_IOCTL_LSEEK, + offset, whence); +} + +#endif /* CONFIG_POSIX_API */ diff --git a/lib/posix/Kconfig b/lib/posix/Kconfig index 3fd5adcb588..c63c7bbe830 100644 --- a/lib/posix/Kconfig +++ b/lib/posix/Kconfig @@ -4,6 +4,14 @@ # SPDX-License-Identifier: Apache-2.0 # +config POSIX_MAX_FDS + int "Maximum number of open file descriptors" + default 16 if POSIX_API + default 4 + help + Maximum number of open file descriptors, this includes + files, sockets, special devices, etc. + config POSIX_API bool "POSIX APIs" help @@ -75,15 +83,16 @@ if FILE_SYSTEM config POSIX_FS bool "Enable POSIX file system API support" help - This enabled POSIX style file system related APIs. + This enables POSIX style file system related APIs. if POSIX_FS config POSIX_MAX_OPEN_FILES int "Maximum number of open file descriptors" default 16 help - Mention maximum number of open file descriptors. -endif + Maximum number of open files. Note that this setting + is additionally bounded by CONFIG_POSIX_MAX_FDS. endif +endif # FILE_SYSTEM endif # POSIX_API