[PATCH 1/2] dev/flash: Add API for Flash devices

aaron.nyholm at unfoldedeffective.com aaron.nyholm at unfoldedeffective.com
Thu Mar 16 02:56:26 UTC 2023


From: Aaron Nyholm <aaron.nyholm at 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



More information about the devel mailing list