From 984693a4b33ed20a7a9d66937d9a0227b910ad0c Mon Sep 17 00:00:00 2001 From: Aaron Nyholm Date: Thu, 16 Mar 2023 12:01:45 +1100 Subject: libmisc/shell: Add flashdev command --- cpukit/include/rtems/shellconfig.h | 7 + cpukit/libmisc/shell/main_flashdev.c | 591 +++++++++++++++++++++++++++++++++++ spec/build/cpukit/objshell.yml | 1 + 3 files changed, 599 insertions(+) create mode 100644 cpukit/libmisc/shell/main_flashdev.c diff --git a/cpukit/include/rtems/shellconfig.h b/cpukit/include/rtems/shellconfig.h index a013840ee7..489f281400 100644 --- a/cpukit/include/rtems/shellconfig.h +++ b/cpukit/include/rtems/shellconfig.h @@ -98,6 +98,7 @@ extern rtems_shell_cmd_t rtems_shell_MD5_Command; extern rtems_shell_cmd_t rtems_shell_RTC_Command; extern rtems_shell_cmd_t rtems_shell_SPI_Command; +extern rtems_shell_cmd_t rtems_shell_FLASHDEV_Command; extern rtems_shell_cmd_t rtems_shell_I2CDETECT_Command; extern rtems_shell_cmd_t rtems_shell_I2CGET_Command; extern rtems_shell_cmd_t rtems_shell_I2CSET_Command; @@ -556,6 +557,12 @@ extern rtems_shell_alias_t * const rtems_shell_Initial_aliases[]; &rtems_shell_SPI_Command, #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ + && !defined(CONFIGURE_SHELL_NO_COMMAND_FLASHDEV)) \ + || defined(CONFIGURE_SHELL_COMMAND_FLASHDEV) + &rtems_shell_FLASHDEV_Command, + #endif + #if (defined(CONFIGURE_SHELL_COMMANDS_ALL) \ && !defined(CONFIGURE_SHELL_NO_COMMAND_I2CDETECT)) \ || defined(CONFIGURE_SHELL_COMMAND_I2CDETECT) diff --git a/cpukit/libmisc/shell/main_flashdev.c b/cpukit/libmisc/shell/main_flashdev.c new file mode 100644 index 0000000000..495373235f --- /dev/null +++ b/cpukit/libmisc/shell/main_flashdev.c @@ -0,0 +1,591 @@ +/* + * 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 +#include +#include + +#include +#include + +#include + +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 [OPTION]\n" + " -r
Read at address for bytes\n" + " -w
Write file to address\n" + " -e
Erase at address for bytes\n" + " -t Print the flash type\n" + " -d Print the JEDEC ID of flash device\n" + " -o
Print the page information of page at address\n" + " -i 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] == '-') { + 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; + } + if (dev_path == NULL) { + printf("Please input FLASH_DEV_PATH before instruction\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"); + 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; + } + if (dev_path == NULL) { + printf("Please input FLASH_DEV_PATH before instruction\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; + } + if (dev_path == NULL) { + printf("Please input FLASH_DEV_PATH before instruction\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; + } + if (dev_path == NULL) { + printf("Please input FLASH_DEV_PATH before instruction\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, +}; diff --git a/spec/build/cpukit/objshell.yml b/spec/build/cpukit/objshell.yml index 5fd259bee3..b71abb18f9 100644 --- a/spec/build/cpukit/objshell.yml +++ b/spec/build/cpukit/objshell.yml @@ -52,6 +52,7 @@ source: - cpukit/libmisc/shell/main_echo.c - cpukit/libmisc/shell/main_edit.c - cpukit/libmisc/shell/main_exit.c +- cpukit/libmisc/shell/main_flashdev.c - cpukit/libmisc/shell/main_getenv.c - cpukit/libmisc/shell/main_halt.c - cpukit/libmisc/shell/main_help.c -- cgit v1.2.3