summaryrefslogtreecommitdiffstats
path: root/cpukit/libmisc/shell/main_flashdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libmisc/shell/main_flashdev.c')
-rw-r--r--cpukit/libmisc/shell/main_flashdev.c584
1 files changed, 584 insertions, 0 deletions
diff --git a/cpukit/libmisc/shell/main_flashdev.c b/cpukit/libmisc/shell/main_flashdev.c
new file mode 100644
index 0000000000..ca2454b33c
--- /dev/null
+++ b/cpukit/libmisc/shell/main_flashdev.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <errno.h>
+#include <rtems/shell.h>
+
+#include <dev/flash/flashdev.h>
+
+static int flashdev_shell_read(char *dev_path, int argc, char *argv[]);
+static int flashdev_shell_write(char *dev_path, int argc, char *argv[]);
+static int flashdev_shell_erase(char *dev_path, int argc, char *argv[]);
+static int flashdev_shell_type(char *dev_path);
+static int flashdev_shell_jedecid(char *dev_path);
+static int flashdev_shell_page_off(char *dev_path, int argc, char *argv[]);
+static int flashdev_shell_page_idx(char *dev_path, int argc, char *argv[]);
+static int flashdev_shell_pg_count(char *dev_path);
+static int flashdev_shell_wb_size(char *dev_path);
+
+static int flashdev_shell_ioctl_value(
+ char *dev_path,
+ int ioctl_call,
+ void *ret
+);
+
+static int flashdev_shell_page(
+ char *dev_path,
+ int argc,
+ char *argv[],
+ int ioctl_call
+);
+
+static const char rtems_flashdev_shell_usage [] =
+ "simple flash read / write / erase\n"
+ "\n"
+ "flashdev <FLASH_DEV_PATH> [OPTION]\n"
+ " -r <address> <bytes> Read at address for bytes\n"
+ " -w <address> <file> Write file to address\n"
+ " -e <address> <bytes> Erase at address for bytes\n"
+ " -t Print the flash type\n"
+ " -d Print the JEDEC ID of flash device\n"
+ " -o <address> Print the page information of page at address\n"
+ " -i <index> Print the page information of page at index\n"
+ " -p Print the number of pages\n"
+ " -b Print the write block size\n"
+ " -h Print this help\n";
+
+
+static int rtems_flashdev_shell_main( int argc, char *argv[] ) {
+
+ char *dev_path = NULL;
+ int i;
+
+ for (i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ /*
+ * Check that a path to flashdev has been provided before running
+ * command.
+ */
+ if (dev_path == NULL) {
+ printf("Please input FLASH_DEV_PATH before instruction\n");
+ return 1;
+ }
+ /* Run command */
+ switch (argv[i][1]) {
+ case ('r'):
+ /* Read */
+ return flashdev_shell_read(dev_path, argc, &argv[i]);
+ case ('w'):
+ /* Write */
+ return flashdev_shell_write(dev_path, argc, &argv[i]);
+ case ('e'):
+ /* Erase */
+ return flashdev_shell_erase(dev_path, argc, &argv[i]);
+ case ('t'):
+ /* Flash Type */
+ return flashdev_shell_type(dev_path);
+ case ('d'):
+ /* JEDEC Id */
+ return flashdev_shell_jedecid(dev_path);
+ case ('o'):
+ /* Page info by offset */
+ return flashdev_shell_page_off(dev_path, argc, &argv[i]);
+ case ('i'):
+ /* Page info by index */
+ return flashdev_shell_page_idx(dev_path, argc, &argv[i]);
+ case ('p'):
+ /* Page count */
+ return flashdev_shell_pg_count(dev_path);
+ case ('b'):
+ /* Write block size */
+ return flashdev_shell_wb_size(dev_path);
+ case ('h'):
+ default:
+ /* Help */
+ printf(rtems_flashdev_shell_usage);
+ break;
+ }
+ } else if (dev_path == NULL) {
+ dev_path = argv[i];
+ } else {
+ printf("Invalid argument: %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ if (argc == 1) {
+ printf(rtems_flashdev_shell_usage);
+ }
+
+ return 0;
+}
+
+int flashdev_shell_read(
+ char *dev_path,
+ int argc,
+ char *argv[]
+)
+{
+ uint32_t address;
+ uint32_t bytes;
+ int fd;
+ int status;
+ void *buffer;
+
+ /* Check arguments */
+ if (argc < 5) {
+ printf("Missing argument\n");
+ return -1;
+ }
+
+ /* Get arguments */
+ errno = 0;
+ address = (uint32_t) strtoul(argv[1], NULL, 0);
+ if (errno != 0) {
+ printf("Could not read address\n");
+ }
+ errno = 0;
+ bytes = (uint32_t) strtoul(argv[2], NULL, 0);
+ if (errno != 0) {
+ printf("Could not read address\n");
+ }
+
+ /* Open flash device */
+ fd = open(dev_path, O_RDONLY);
+ if (fd == -1) {
+ printf("Couldn't open %s\n", dev_path);
+ return -1;
+ }
+
+ /* Move to address */
+ status = lseek(fd, address, SEEK_SET);
+ if (status == -1) {
+ printf("Reading failed\n");
+ close(fd);
+ return -1;
+ }
+
+ /* Create a buffer to read into */
+ buffer = calloc((bytes + bytes%4), 1);
+ if (buffer == NULL) {
+ printf("Failed to allocate read buffer\n");
+ close(fd);
+ return -1;
+ }
+
+ /* Read into buffer */
+ status = read(fd, buffer, bytes);
+ if (status == -1) {
+ printf("Reading failed\n");
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ /* Print buffer out in 32bit blocks */
+ printf("Reading %s at 0x%08x for %d bytes\n", dev_path, address, bytes);
+ for (int i = 0; i < (bytes/4); i++) {
+ printf("%08x ", ((uint32_t*)buffer)[i]);
+ if ((i+1)%4 == 0) {
+ printf("\n");
+ }
+ }
+ printf("\n");
+
+ /* Clean up */
+ free(buffer);
+ close(fd);
+ return 0;
+}
+
+int flashdev_shell_write(
+ char *dev_path,
+ int argc,
+ char *argv[]
+)
+{
+ uint32_t address;
+ int flash;
+ int file;
+ int status;
+ int read_len;
+ off_t length;
+ void *buffer;
+ uint32_t offset;
+ char *file_path;
+
+ /* Check arguments */
+ if (argc < 5) {
+ printf("Missing argument\n");
+ return -1;
+ }
+
+ /* Get arguments */
+ errno = 0;
+ address = (uint32_t) strtoul(argv[1], NULL, 0);
+ if (errno != 0) {
+ printf("Could not read address\n");
+ }
+ errno = 0;
+ file_path = argv[2];
+
+ /* Open flash device and move to write offset */
+ flash = open(dev_path, O_WRONLY);
+ if (flash == -1) {
+ printf("Couldn't open %s\n", dev_path);
+ return -1;
+ }
+ status = lseek(flash, address, SEEK_SET);
+ if (status == -1) {
+ printf("Reading failed\n");
+ close(flash);
+ return -1;
+ }
+
+ /* Open file and get file length */
+ file = open(file_path, O_RDONLY);
+ if (file == -1) {
+ printf("Couldn't open %s\n", file_path);
+ close(flash);
+ return -1;
+ }
+
+ length = lseek(file, 0, SEEK_END);
+ if (length == -1) {
+ close(flash);
+ close(file);
+ printf("Couldn't find length of file\n");
+ return -1;
+ }
+
+ if (lseek(file, 0, SEEK_SET) == -1) {
+ close(flash);
+ close(file);
+ printf("Couldn't find length of file\n");
+ return -1;
+ }
+
+ /* Create buffer */
+ buffer = calloc(1, 0x1000);
+
+ /* Write file to flash device in 0x1000 byte chunks */
+ offset = 0;
+ while (offset != length) {
+
+ read_len = length - offset;
+ if (read_len > 0x1000) {
+ read_len = 0x1000;
+ }
+
+ status = read(file, buffer, read_len);
+ if (status == -1) {
+ free(buffer);
+ close(flash);
+ close(file);
+ printf("Can't read %s\n", file_path);
+ return -1;
+ }
+
+ status = write(flash, buffer, read_len);
+ if (status == -1) {
+ free(buffer);
+ close(flash);
+ close(file);
+ printf("Can't write %s\n", dev_path);
+ return -1;
+ }
+
+ offset = offset + read_len;
+ }
+
+ /* Clean up */
+ close(flash);
+ close(file);
+ free(buffer);
+ return 0;
+}
+
+int flashdev_shell_erase(
+ char *dev_path,
+ int argc,
+ char *argv[]
+)
+{
+ uint32_t address;
+ uint32_t bytes;
+ int fd;
+ int status;
+ rtems_flashdev_region args;
+
+ /* Check arguments */
+ if (argc < 5) {
+ printf("Missing argument\n");
+ return -1;
+ }
+
+ /* Get arguments */
+ errno = 0;
+ address = (uint32_t) strtoul(argv[1], NULL, 0);
+ if (errno != 0) {
+ printf("Could not read address\n");
+ }
+ errno = 0;
+ bytes = (uint32_t) strtoul(argv[2], NULL, 0);
+ if (errno != 0) {
+ printf("Could not read address\n");
+ }
+
+ /* Open flash device */
+ fd = open(dev_path, O_RDWR);
+ if (fd == -1) {
+ printf("Couldn't open %s\n", dev_path);
+ return -1;
+ }
+
+ printf("Erasing at %08x for %x bytes\n", address, bytes);
+
+ /* Erase flash */
+ args.offset = address;
+ args.size = bytes;
+
+ status = ioctl(fd, RTEMS_FLASHDEV_IOCTL_ERASE, &args);
+ if (status == -1) {
+ printf("Erase failed\n");
+ close(fd);
+ return -1;
+ }
+
+ /* Clean up */
+ close(fd);
+
+ return 0;
+}
+
+int flashdev_shell_type( char *dev_path )
+{
+ int type;
+ int status;
+
+ /* Get type */
+ status = flashdev_shell_ioctl_value(
+ dev_path,
+ RTEMS_FLASHDEV_IOCTL_TYPE,
+ &type
+ );
+
+ if (status) {
+ printf("Failed to get flash type\n");
+ return status;
+ }
+
+ /* Print type */
+ switch(type) {
+ case RTEMS_FLASHDEV_NOR:
+ printf("NOR flash\n");
+ break;
+ case RTEMS_FLASHDEV_NAND:
+ printf("NAND flash\n");
+ break;
+ default:
+ printf("Unknown type\n");
+ }
+
+ return 0;
+}
+
+int flashdev_shell_jedecid( char *dev_path ) {
+ uint32_t ret;
+ int status;
+
+ /* Get JEDEC Id */
+ status = flashdev_shell_ioctl_value(
+ dev_path,
+ RTEMS_FLASHDEV_IOCTL_JEDEC_ID,
+ &ret
+ );
+
+ /* Print JEDEC Id */
+ if (status) {
+ printf("Failed to get JEDEC Id\n");
+ return status;
+ } else {
+ printf("JEDEC Id: 0x%x\n", ret);
+ }
+ return 0;
+}
+
+static int flashdev_shell_page_off(
+ char *dev_path,
+ int argc,
+ char *argv[]
+)
+{
+ return flashdev_shell_page(
+ dev_path,
+ argc,
+ argv,
+ RTEMS_FLASHDEV_IOCTL_PAGEINFO_BY_OFFSET
+ );
+}
+
+static int flashdev_shell_page_idx(
+ char *dev_path,
+ int argc,
+ char *argv[]
+)
+{
+ return flashdev_shell_page(
+ dev_path,
+ argc,
+ argv,
+ RTEMS_FLASHDEV_IOCTL_PAGEINFO_BY_INDEX
+ );
+}
+
+static int flashdev_shell_pg_count( char *dev_path )
+{
+ uint32_t ret;
+ int status;
+
+ /* Get Page Count */
+ status = flashdev_shell_ioctl_value(
+ dev_path,
+ RTEMS_FLASHDEV_IOCTL_PAGE_COUNT,
+ &ret
+ );
+
+ /* Print Page Count */
+ if (status) {
+ printf("Failed to get page count\n");
+ return status;
+ } else {
+ printf("Page count: 0x%x\n", ret);
+ }
+ return 0;
+}
+
+static int flashdev_shell_wb_size( char *dev_path )
+{
+ size_t ret;
+ int status;
+
+ /* Get Write Block Size */
+ status = flashdev_shell_ioctl_value(
+ dev_path,
+ RTEMS_FLASHDEV_IOCTL_WRITE_BLOCK_SIZE,
+ &ret
+ );
+
+ /* Print Write Block Size */
+ if (status) {
+ printf("Failed to get write block size\n");
+ return status;
+ } else {
+ printf("Write block size: 0x%zx\n", ret);
+ }
+ return 0;
+}
+
+static int flashdev_shell_ioctl_value(
+ char *dev_path,
+ int ioctl_call,
+ void *ret
+)
+{
+ int fd;
+ int status;
+
+ fd = open(dev_path, O_RDONLY);
+ if (fd == -1) {
+ printf("Couldn't open %s\n", dev_path);
+ return -1;
+ }
+
+ status = ioctl(fd, ioctl_call, ret);
+ if (status == -1) {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int flashdev_shell_page(
+ char *dev_path,
+ int argc,
+ char *argv[],
+ int ioctl_call
+)
+{
+ rtems_flashdev_ioctl_page_info pg_info;
+ int fd;
+ int status;
+
+ /* Check arguments */
+ if (argc < 4) {
+ printf("Missing argument\n");
+ return -1;
+ }
+
+ /* Get arguments */
+ errno = 0;
+ pg_info.location = (off_t) strtoul(argv[1], NULL, 0);
+ if (errno != 0) {
+ printf("Could not read address\n");
+ }
+
+ /* Open flash device */
+ fd = open(dev_path, O_RDWR);
+ if (fd == -1) {
+ printf("Couldn't open %s\n", dev_path);
+ return -1;
+ }
+
+ status = ioctl(fd, ioctl_call, &pg_info);
+ if (status == -1) {
+ printf("Failed to get page info\n");
+ close(fd);
+ return -1;
+ }
+
+ printf(
+ "Page offset: 0x%jx\nPage length: 0x%zx\n",
+ pg_info.page_info.offset,
+ pg_info.page_info.size
+ );
+
+ /* Clean up */
+ close(fd);
+ return 0;
+}
+
+rtems_shell_cmd_t rtems_shell_FLASHDEV_Command = {
+ .name = "flashdev",
+ .usage = rtems_flashdev_shell_usage,
+ .topic = "misc",
+ .command = rtems_flashdev_shell_main,
+};