From: Aaron Nyholm <aaron.nyh...@southerninnovation.com> Updates #4896 --- cpukit/dev/flash/flashdev.c | 353 ++++++++++++++++++++++++++++ cpukit/include/dev/flash/flashdev.h | 95 ++++++++ spec/build/cpukit/librtemscpu.yml | 4 + 3 files changed, 452 insertions(+) create mode 100644 cpukit/dev/flash/flashdev.c create mode 100644 cpukit/include/dev/flash/flashdev.h
diff --git a/cpukit/dev/flash/flashdev.c b/cpukit/dev/flash/flashdev.c new file mode 100644 index 0000000000..a37d0e6c3d --- /dev/null +++ b/cpukit/dev/flash/flashdev.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2023, 2023 Aaron Nyholm + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include <dev/flash/flashdev.h> + +#include <rtems/imfs.h> +#include <rtems/score/assert.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int flashdev_do_init( + flashdev *flash, + void (*destroy)(flashdev *flash) +); + +void flashdev_destroy_and_free(flashdev *flash); + +void flashdev_destroy(flashdev *flash); + +static void flashdev_obtain(flashdev *flash) { + rtems_recursive_mutex_lock(&flash->mutex); +} + +static void flashdev_release(flashdev *flash) { + rtems_recursive_mutex_unlock(&flash->mutex); +} + +static ssize_t flashdev_read( + rtems_libio_t *iop, + void *buffer, + size_t count + ) +{ + flashdev *flash = IMFS_generic_get_context_by_iop(iop); + + // Check reading from valid region + off_t new_offset = iop->offset + count; + + if (iop->data1 != NULL && new_offset > ((flashdev_region*)iop->data1)->length) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + + // Read flash + off_t addr; + if (iop->data1 == NULL) { + addr = iop->offset; + } else { + addr = (iop->offset + ((flashdev_region*)iop->data1)->base); + } + flashdev_obtain(flash); + int status = (*flash->read)(flash->driver, addr, count, buffer); + flashdev_release(flash); + + // Update offset and return + if (status == 0) { + iop->offset = new_offset; + return count; + } else { + rtems_set_errno_and_return_minus_one(-status); + } +} + +static ssize_t flashdev_write( + rtems_libio_t *iop, + const void *buffer, + size_t count + ) +{ + flashdev *flash = IMFS_generic_get_context_by_iop(iop); + + // Check writing to valid region + off_t new_offset = iop->offset + count; + + if (iop->data1 != NULL && new_offset > ((flashdev_region*)iop->data1)->length) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + + // Write to flash + off_t addr; + if (iop->data1 == NULL) { + addr = iop->offset; + } else { + addr = (iop->offset + ((flashdev_region*)iop->data1)->base); + } + flashdev_obtain(flash); + int status = (*flash->write)(flash->driver, addr, count, buffer); + flashdev_release(flash); + + // Update offset and return + if (status == 0) { + iop->offset = new_offset; + return count; + } else { + rtems_set_errno_and_return_minus_one(-status); + } +} + +static int flashdev_ioctl( + rtems_libio_t *iop, + ioctl_command_t command, + void *arg + ) +{ + flashdev *flash = IMFS_generic_get_context_by_iop(iop); + int err = 0; + + flashdev_obtain(flash); + + switch (command) { + case FLASHDEV_IOCTL_OBTAIN: + flashdev_obtain(flash); + err = 0; + break; + case FLASHDEV_IOCTL_RELEASE: + flashdev_release(flash); + err = 0; + break; + case FLASHDEV_IOCTL_READID: + uint32_t *readID_out = arg; + *readID_out = (*flash->read_id)(flash); + err = 0; + break; + case FLASHDEV_IOCTL_ERASE: + erase_args *erase_args_1 = (erase_args*)arg; + // Check erasing valid region + off_t check_offset = erase_args_1->offset + erase_args_1->count; + if ((iop->data1 != NULL) && + (check_offset > ((flashdev_region*)iop->data1)->length || + (erase_args_1->offset < ((flashdev_region*)iop->data1)->base)) + ) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + // Erasing + off_t addr; + if (iop->data1 == NULL) { + addr = erase_args_1->offset; + } else { + addr = (erase_args_1->offset + ((flashdev_region*)iop->data1)->base); + } + (*flash->erase)(flash->driver, addr, erase_args_1->count); + err = 0; + break; + case FLASHDEV_IOCTL_REGION_SET: + err = 0; + flashdev_region *region_in = (flashdev_region*)arg; + if (region_in == NULL || region_in->base < 0 || region_in->length < 0) { + rtems_set_errno_and_return_minus_one(EINVAL); + } + flashdev_region *region_save = malloc(sizeof(flashdev_region)); + if (region_save == NULL) { + rtems_set_errno_and_return_minus_one(ENOMEM); + } + iop->data1 = region_save; + ((flashdev_region*)iop->data1)->base = region_in->base; + ((flashdev_region*)iop->data1)->length = region_in->length; + iop->offset = region_in->base; + break; + case FLASHDEV_IOCTL_REGION_UNSET: + err = 0; + free((flashdev_region*)iop->data1); + iop->data1 = NULL; + break; + case FLASHDEV_IOCTL_TYPE: + int *type_out = arg; + *type_out = (*flash->flash_type)(flash); + err = 0; + break; + } + + flashdev_release(flash); + + if (err == 0) { + return 0; + } else { + rtems_set_errno_and_return_minus_one(err); + } +} + + +static off_t flashdev_lseek( + rtems_libio_t *iop, + off_t offset, + int whence +) +{ + off_t tmp_offset; + + if (offset < 0) { + rtems_set_errno_and_return_minus_one( EINVAL ); + } + + switch (whence) { + case SEEK_CUR: + tmp_offset = iop->offset + offset; + break; + case SEEK_SET: + tmp_offset = offset; + break; + case SEEK_END: + default: + rtems_set_errno_and_return_minus_one( EINVAL ); + } + + if (iop->data1 != NULL && + (tmp_offset > ((flashdev_region*)iop->data1)->length)) { + rtems_set_errno_and_return_minus_one( EINVAL ); + } + + iop->offset = tmp_offset; + return iop->offset; +} + +static int flashdev_close(rtems_libio_t *iop) { + free((flashdev_region*)iop->data1); + return rtems_filesystem_default_close(iop); +} + +static int flashdev_open(rtems_libio_t *iop, const char *path, int oflag, mode_t mode) { + int ret = rtems_filesystem_default_open(iop, path, oflag, mode); + iop->data1 = NULL; + return ret; +} + + +static const rtems_filesystem_file_handlers_r flashdev_handler = { + .open_h = flashdev_open, + .close_h = flashdev_close, + .read_h = flashdev_read, + .write_h = flashdev_write, + .ioctl_h = flashdev_ioctl, + .lseek_h = flashdev_lseek, + .fstat_h = IMFS_stat, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fcntl_h = rtems_filesystem_default_fcntl, + .kqfilter_h = rtems_filesystem_default_kqfilter, + .mmap_h = rtems_filesystem_default_mmap, + .poll_h = rtems_filesystem_default_poll, + .readv_h = rtems_filesystem_default_readv, + .writev_h = rtems_filesystem_default_writev +}; + +static void flashdev_node_destroy(IMFS_jnode_t *node) { + flashdev *flash; + + flash = IMFS_generic_get_context_by_node(node); + + (*flash->destroy)(flash); + + IMFS_node_destroy_default(node); + +} + +static const IMFS_node_control flashdev_node_control = IMFS_GENERIC_INITIALIZER( + &flashdev_handler, + IMFS_node_initialize_generic, + flashdev_node_destroy +); + +int flashdev_register( + flashdev *flash, + const char *flash_path +) { + int rv; + + rv = IMFS_make_generic_node( + flash_path, + S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO, + &flashdev_node_control, + flash + ); + if (rv != 0) { + (*flash->destroy)(flash); + } + + return rv; +} + +int flashdev_do_init( + flashdev *flash, + void (*destroy)(flashdev *flash) +) { + rtems_recursive_mutex_init(&flash->mutex, "FLASHDEV Flash"); + flash->destroy = destroy; + return 0; +} + +void flashdev_destroy(flashdev *flash) { + rtems_recursive_mutex_destroy(&flash->mutex); +} + +void flashdev_destroy_and_free(flashdev *flash) { + rtems_recursive_mutex_destroy(&flash->mutex); + free(flash); +} + +int flashdev_init(flashdev *flash) { + memset(flash, 0, sizeof(*flash)); + + return flashdev_do_init(flash, flashdev_destroy); +} + +flashdev* flashdev_alloc_and_init(size_t size) { + + flashdev *flash = NULL; + + if (size >= sizeof(*flash)) { + flash = calloc(1, size); + if (flash != NULL) { + int rv; + + rv = flashdev_do_init(flash, flashdev_destroy_and_free); + if (rv != 0) { + free(flash); + return NULL; + } + } + } + + return flash; + +} diff --git a/cpukit/include/dev/flash/flashdev.h b/cpukit/include/dev/flash/flashdev.h new file mode 100644 index 0000000000..b6869629c4 --- /dev/null +++ b/cpukit/include/dev/flash/flashdev.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup FLASHDEV_Flash + * + * @brief FLASHDEV flashdev API + */ + +/* + * Copyright (C) 2023, 2023 Aaron Nyholm + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_FLASHDEV_H +#define _DEV_FLASHDEV_H + +#include <rtems/thread.h> +#include <sys/types.h> +#include <stdbool.h> + +typedef struct flashdev flashdev; + +typedef struct erase_args { + uint32_t offset; + uint32_t count; +} erase_args; + +typedef struct flashdev_region { + off_t base; + off_t length; +} flashdev_region; + +struct flashdev { + + int (*read)(void *driver, uint32_t offset, uint32_t count, void *buffer); + + int (*write)(void *driver, uint32_t offset, uint32_t count, const void *buffer); + + int (*erase)(void *driver, uint32_t offset, uint32_t count); + + uint32_t (*read_id)(flashdev *flashdev); + + int (*flash_type)(flashdev *flashdev); + + void (*destroy)(flashdev *flashdev); + + void *driver; + + rtems_recursive_mutex mutex; + +}; + +#define FLASHDEV_IOCTL_OBTAIN 0 +#define FLASHDEV_IOCTL_RELEASE 1 +#define FLASHDEV_IOCTL_READID 2 +#define FLASHDEV_IOCTL_ERASE 3 +#define FLASHDEV_IOCTL_REGION_SET 4 +#define FLASHDEV_IOCTL_REGION_UNSET 5 +#define FLASHDEV_IOCTL_TYPE 6 + +#define FLASHDEV_TYPE_NOR 0 +#define FLASHDEV_TYPE_NAND 1 + +flashdev* flashdev_alloc_and_init(size_t size); + +int flashdev_init(flashdev *flashdev); + +int flashdev_register( + flashdev *flashdev, + const char *flashdev_path +); + +#endif /* _DEV_FLASHDEV_H */ diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml index 2c969aa5f6..86c5c8ff2b 100644 --- a/spec/build/cpukit/librtemscpu.yml +++ b/spec/build/cpukit/librtemscpu.yml @@ -52,6 +52,9 @@ install: - destination: ${BSP_INCLUDEDIR}/dev/spi source: - cpukit/include/dev/spi/spi.h +- destination: ${BSP_INCLUDEDIR}/dev/flash + source: + - cpukit/include/dev/flash/flashdev.h - destination: ${BSP_INCLUDEDIR}/dev/can source: - cpukit/include/dev/can/can.h @@ -522,6 +525,7 @@ links: - role: build-dependency uid: vckey source: +- cpukit/dev/flash/flashdev.c - cpukit/dev/i2c/eeprom.c - cpukit/dev/i2c/fpga-i2c-slave.c - cpukit/dev/i2c/gpio-nxp-pca9535.c -- 2.25.1 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel