| /* |
| * Copyright (c) 2d3D, Inc. |
| * Written by Abraham vd Merwe <abraham@2d3d.co.za> |
| * All rights reserved. |
| * |
| * 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. |
| * 3. Neither the name of the author nor the names of other contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * 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 REGENTS 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. |
| */ |
| |
| #define PROGRAM_NAME "mtd_debug" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <mtd/mtd-user.h> |
| #include "common.h" |
| |
| /* |
| * MEMGETINFO |
| */ |
| static int getmeminfo(int fd, struct mtd_info_user *mtd) |
| { |
| return ioctl(fd, MEMGETINFO, mtd); |
| } |
| |
| /* |
| * MEMERASE |
| */ |
| static int memerase(int fd, struct erase_info_user *erase) |
| { |
| return ioctl(fd, MEMERASE, erase); |
| } |
| |
| /* |
| * MEMGETREGIONCOUNT |
| * MEMGETREGIONINFO |
| */ |
| static int getregions(int fd, struct region_info_user *regions, int *n) |
| { |
| int i, err; |
| err = ioctl(fd, MEMGETREGIONCOUNT, n); |
| if (err) |
| return err; |
| for (i = 0; i < *n; i++) { |
| regions[i].regionindex = i; |
| err = ioctl(fd, MEMGETREGIONINFO, ®ions[i]); |
| if (err) |
| return err; |
| } |
| return 0; |
| } |
| |
| int erase_flash(int fd, u_int32_t offset, u_int32_t bytes) |
| { |
| int err; |
| struct erase_info_user erase; |
| erase.start = offset; |
| erase.length = bytes; |
| err = memerase(fd, &erase); |
| if (err < 0) { |
| perror("MEMERASE"); |
| return 1; |
| } |
| fprintf(stderr, "Erased %d bytes from address 0x%.8x in flash\n", bytes, offset); |
| return 0; |
| } |
| |
| void printsize(u_int32_t x) |
| { |
| int i; |
| static const char *flags = "KMGT"; |
| printf("%u ", x); |
| for (i = 0; x >= 1024 && flags[i] != '\0'; i++) |
| x /= 1024; |
| i--; |
| if (i >= 0) |
| printf("(%u%c)", x, flags[i]); |
| } |
| |
| int flash_to_file(int fd, off_t offset, size_t len, const char *filename) |
| { |
| u_int8_t *buf = NULL; |
| int outfd, err; |
| int size = len * sizeof(u_int8_t); |
| int n = len; |
| |
| if (offset != lseek(fd, offset, SEEK_SET)) { |
| perror("lseek()"); |
| goto err0; |
| } |
| outfd = creat(filename, 0666); |
| if (outfd < 0) { |
| perror("creat()"); |
| goto err1; |
| } |
| |
| retry: |
| if ((buf = (u_int8_t *) malloc(size)) == NULL) { |
| #define BUF_SIZE (64 * 1024 * sizeof(u_int8_t)) |
| fprintf(stderr, "%s: malloc(%#x)\n", __func__, size); |
| if (size != BUF_SIZE) { |
| size = BUF_SIZE; |
| fprintf(stderr, "%s: trying buffer size %#x\n", __func__, size); |
| goto retry; |
| } |
| perror("malloc()"); |
| goto err0; |
| } |
| do { |
| if (n <= size) |
| size = n; |
| err = read(fd, buf, size); |
| if (err < 0) { |
| fprintf(stderr, "%s: read, size %#x, n %#x\n", __func__, size, n); |
| perror("read()"); |
| goto err2; |
| } |
| err = write(outfd, buf, size); |
| if (err < 0) { |
| fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, size, n); |
| perror("write()"); |
| goto err2; |
| } |
| if (err != size) { |
| fprintf(stderr, "Couldn't copy entire buffer to %s. (%d/%d bytes copied)\n", filename, err, size); |
| goto err2; |
| } |
| n -= size; |
| } while (n > 0); |
| |
| if (buf != NULL) |
| free(buf); |
| close(outfd); |
| printf("Copied %zu bytes from address 0x%.8"PRIxoff_t" in flash to %s\n", len, offset, filename); |
| return 0; |
| |
| err2: |
| close(outfd); |
| err1: |
| if (buf != NULL) |
| free(buf); |
| err0: |
| return 1; |
| } |
| |
| int file_to_flash(int fd, off_t offset, u_int32_t len, const char *filename) |
| { |
| u_int8_t *buf = NULL; |
| FILE *fp; |
| int err; |
| int size = len * sizeof(u_int8_t); |
| int n = len; |
| |
| if (offset != lseek(fd, offset, SEEK_SET)) { |
| perror("lseek()"); |
| return 1; |
| } |
| if ((fp = fopen(filename, "r")) == NULL) { |
| perror("fopen()"); |
| return 1; |
| } |
| retry: |
| if ((buf = (u_int8_t *) malloc(size)) == NULL) { |
| fprintf(stderr, "%s: malloc(%#x) failed\n", __func__, size); |
| if (size != BUF_SIZE) { |
| size = BUF_SIZE; |
| fprintf(stderr, "%s: trying buffer size %#x\n", __func__, size); |
| goto retry; |
| } |
| perror("malloc()"); |
| fclose(fp); |
| return 1; |
| } |
| do { |
| if (n <= size) |
| size = n; |
| if (fread(buf, size, 1, fp) != 1 || ferror(fp)) { |
| fprintf(stderr, "%s: fread, size %#x, n %#x\n", __func__, size, n); |
| perror("fread()"); |
| free(buf); |
| fclose(fp); |
| return 1; |
| } |
| err = write(fd, buf, size); |
| if (err < 0) { |
| fprintf(stderr, "%s: write, size %#x, n %#x\n", __func__, size, n); |
| perror("write()"); |
| free(buf); |
| fclose(fp); |
| return 1; |
| } |
| n -= size; |
| } while (n > 0); |
| |
| if (buf != NULL) |
| free(buf); |
| fclose(fp); |
| printf("Copied %d bytes from %s to address 0x%.8"PRIxoff_t" in flash\n", len, filename, offset); |
| return 0; |
| } |
| |
| int showinfo(int fd) |
| { |
| int i, err, n; |
| struct mtd_info_user mtd; |
| static struct region_info_user region[1024]; |
| |
| err = getmeminfo(fd, &mtd); |
| if (err < 0) { |
| perror("MEMGETINFO"); |
| return 1; |
| } |
| |
| err = getregions(fd, region, &n); |
| if (err < 0) { |
| perror("MEMGETREGIONCOUNT"); |
| return 1; |
| } |
| |
| printf("mtd.type = "); |
| switch (mtd.type) { |
| case MTD_ABSENT: |
| printf("MTD_ABSENT"); |
| break; |
| case MTD_RAM: |
| printf("MTD_RAM"); |
| break; |
| case MTD_ROM: |
| printf("MTD_ROM"); |
| break; |
| case MTD_NORFLASH: |
| printf("MTD_NORFLASH"); |
| break; |
| case MTD_NANDFLASH: |
| printf("MTD_NANDFLASH"); |
| break; |
| case MTD_MLCNANDFLASH: |
| printf("MTD_MLCNANDFLASH"); |
| break; |
| case MTD_DATAFLASH: |
| printf("MTD_DATAFLASH"); |
| break; |
| case MTD_UBIVOLUME: |
| printf("MTD_UBIVOLUME"); |
| default: |
| printf("(unknown type - new MTD API maybe?)"); |
| } |
| |
| printf("\nmtd.flags = "); |
| if (mtd.flags == MTD_CAP_ROM) |
| printf("MTD_CAP_ROM"); |
| else if (mtd.flags == MTD_CAP_RAM) |
| printf("MTD_CAP_RAM"); |
| else if (mtd.flags == MTD_CAP_NORFLASH) |
| printf("MTD_CAP_NORFLASH"); |
| else if (mtd.flags == MTD_CAP_NANDFLASH) |
| printf("MTD_CAP_NANDFLASH"); |
| else if (mtd.flags == MTD_WRITEABLE) |
| printf("MTD_WRITEABLE"); |
| else { |
| int first = 1; |
| static struct { |
| const char *name; |
| int value; |
| } flags[] = |
| { |
| { "MTD_WRITEABLE", MTD_WRITEABLE }, |
| { "MTD_BIT_WRITEABLE", MTD_BIT_WRITEABLE }, |
| { "MTD_NO_ERASE", MTD_NO_ERASE }, |
| { "MTD_POWERUP_LOCK", MTD_POWERUP_LOCK }, |
| { NULL, -1 } |
| }; |
| for (i = 0; flags[i].name != NULL; i++) { |
| if (mtd.flags & flags[i].value) { |
| if (first) { |
| printf("%s", flags[i].name); |
| first = 0; |
| } else { |
| printf(" | %s", flags[i].name); |
| } |
| } |
| } |
| } |
| |
| printf("\nmtd.size = "); |
| printsize(mtd.size); |
| |
| printf("\nmtd.erasesize = "); |
| printsize(mtd.erasesize); |
| |
| printf("\nmtd.writesize = "); |
| printsize(mtd.writesize); |
| |
| printf("\nmtd.oobsize = "); |
| printsize(mtd.oobsize); |
| |
| printf("\nregions = %d\n\n", n); |
| |
| for (i = 0; i < n; i++) { |
| printf("region[%d].offset = 0x%.8x\n" |
| "region[%d].erasesize = ", |
| i, region[i].offset, i); |
| printsize(region[i].erasesize); |
| printf("\nregion[%d].numblocks = %d\n" |
| "region[%d].regionindex = %d\n", |
| i, region[i].numblocks, |
| i, region[i].regionindex); |
| } |
| return 0; |
| } |
| |
| void showusage(void) |
| { |
| fprintf(stderr, "usage: %1$s info <device>\n" |
| " %1$s read <device> <offset> <len> <dest-filename>\n" |
| " %1$s write <device> <offset> <len> <source-filename>\n" |
| " %1$s erase <device> <offset> <len>\n", |
| PROGRAM_NAME); |
| exit(EXIT_FAILURE); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int err = 0, fd; |
| int open_flag; |
| |
| enum { |
| OPT_INFO, |
| OPT_READ, |
| OPT_WRITE, |
| OPT_ERASE |
| } option = OPT_INFO; |
| |
| /* parse command-line options */ |
| if (argc == 3 && !strcmp(argv[1], "info")) |
| option = OPT_INFO; |
| else if (argc == 6 && !strcmp(argv[1], "read")) |
| option = OPT_READ; |
| else if (argc == 6 && !strcmp(argv[1], "write")) |
| option = OPT_WRITE; |
| else if (argc == 5 && !strcmp(argv[1], "erase")) |
| option = OPT_ERASE; |
| else |
| showusage(); |
| |
| /* open device */ |
| open_flag = (option == OPT_INFO || option == OPT_READ) ? O_RDONLY : O_RDWR; |
| if ((fd = open(argv[2], O_SYNC | open_flag)) < 0) |
| errmsg_die("open()"); |
| |
| switch (option) { |
| case OPT_INFO: |
| showinfo(fd); |
| break; |
| case OPT_READ: |
| err = flash_to_file(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); |
| break; |
| case OPT_WRITE: |
| err = file_to_flash(fd, strtoll(argv[3], NULL, 0), strtoul(argv[4], NULL, 0), argv[5]); |
| break; |
| case OPT_ERASE: |
| err = erase_flash(fd, strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0)); |
| break; |
| } |
| |
| /* close device */ |
| if (close(fd) < 0) |
| errmsg_die("close()"); |
| |
| return err; |
| } |