aboutsummaryrefslogtreecommitdiff
path: root/ubi-utils/mtdinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/mtdinfo.c')
-rw-r--r--ubi-utils/mtdinfo.c446
1 files changed, 446 insertions, 0 deletions
diff --git a/ubi-utils/mtdinfo.c b/ubi-utils/mtdinfo.c
new file mode 100644
index 0000000..bfd7e6d
--- /dev/null
+++ b/ubi-utils/mtdinfo.c
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2009 Nokia Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * An utility to get MTD information.
+ *
+ * Author: Artem Bityutskiy
+ */
+
+#define PROGRAM_VERSION "1.1"
+#define PROGRAM_NAME "mtdinfo"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mtd/mtd-user.h>
+
+#include <libubigen.h>
+#include <libmtd.h>
+#include "common.h"
+#include "ubiutils-common.h"
+
+/* The variables below are set by command line arguments */
+struct args {
+ int mtdn;
+ unsigned int all:1;
+ unsigned int ubinfo:1;
+ unsigned int map:1;
+ const char *node;
+};
+
+static struct args args = {
+ .mtdn = -1,
+ .ubinfo = 0,
+ .all = 0,
+ .node = NULL,
+};
+
+static const char doc[] = PROGRAM_NAME " version " PROGRAM_VERSION
+ " - a tool to print MTD information.";
+
+static const char optionsstr[] =
+"-m, --mtdn=<MTD device number> MTD device number to get information about\n"
+" (deprecated option, will be removed, do not use)\n"
+"-u, --ubi-info print what would UBI layout be if it was put\n"
+" on this MTD device\n"
+"-M, --map print eraseblock map\n"
+"-a, --all print information about all MTD devices\n"
+"-h, --help print help message\n"
+"-V, --version print program version";
+
+static const char usage[] =
+"Usage 1: " PROGRAM_NAME " [-m <MTD device number>] [-u] [-M] [-h] [-V] [--mtdn <MTD device number>]\n"
+"\t\t[--ubi-info] [--help] [--version]\n"
+"Usage 2: " PROGRAM_NAME " <MTD device node file name> [-u] [-M] [-h] [-V] [--ubi-info] [--help]\n"
+"\t\t[--version]\n"
+"Example 1: " PROGRAM_NAME " - (no arguments) print general MTD information\n"
+"Example 2: " PROGRAM_NAME " -m 1 - print information about MTD device number 1\n"
+"Example 3: " PROGRAM_NAME " /dev/mtd0 - print information MTD device /dev/mtd0\n"
+"Example 4: " PROGRAM_NAME " /dev/mtd0 -u - print information MTD device /dev/mtd0\n"
+"\t\t\t\tand include UBI layout information\n"
+"Example 5: " PROGRAM_NAME " -a - print information about all MTD devices\n"
+"\t\t\tand include UBI layout information\n";
+
+static const struct option long_options[] = {
+ { .name = "mtdn", .has_arg = 1, .flag = NULL, .val = 'm' },
+ { .name = "ubi-info", .has_arg = 0, .flag = NULL, .val = 'u' },
+ { .name = "map", .has_arg = 0, .flag = NULL, .val = 'M' },
+ { .name = "all", .has_arg = 0, .flag = NULL, .val = 'a' },
+ { .name = "help", .has_arg = 0, .flag = NULL, .val = 'h' },
+ { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+ { NULL, 0, NULL, 0},
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+ while (1) {
+ int key, error = 0;
+
+ key = getopt_long(argc, argv, "am:uMhV", long_options, NULL);
+ if (key == -1)
+ break;
+
+ switch (key) {
+ case 'a':
+ args.all = 1;
+ break;
+
+ case 'u':
+ args.ubinfo = 1;
+ break;
+
+ case 'm':
+ args.mtdn = simple_strtoul(optarg, &error);
+ if (error || args.mtdn < 0)
+ return errmsg("bad MTD device number: \"%s\"", optarg);
+ warnmsg("-m/--mtdn is depecated, will be removed in mtd-utils-1.4.6");
+ break;
+
+ case 'M':
+ args.map = 1;
+ break;
+
+ case 'h':
+ printf("%s\n\n", doc);
+ printf("%s\n\n", usage);
+ printf("%s\n", optionsstr);
+ exit(EXIT_SUCCESS);
+
+ case 'V':
+ printf("%s\n", PROGRAM_VERSION);
+ exit(EXIT_SUCCESS);
+
+ case ':':
+ return errmsg("parameter is missing");
+
+ default:
+ fprintf(stderr, "Use -h for help\n");
+ return -1;
+ }
+ }
+
+ if (optind == argc - 1)
+ args.node = argv[optind];
+ else if (optind < argc)
+ return errmsg("more then one MTD device specified (use -h for help)");
+
+ if (args.all && (args.node || args.mtdn != -1)) {
+ args.mtdn = -1;
+ args.node = NULL;
+ }
+
+ if (args.map && !args.node)
+ return errmsg("-M requires MTD device node name");
+
+ return 0;
+}
+
+static int translate_dev(libmtd_t libmtd, const char *node)
+{
+ int err;
+ struct mtd_dev_info mtd;
+
+ err = mtd_get_dev_info(libmtd, node, &mtd);
+ if (err) {
+ if (errno == ENODEV)
+ return errmsg("\"%s\" does not correspond to any "
+ "existing MTD device", node);
+ return sys_errmsg("cannot get information about MTD "
+ "device \"%s\"", node);
+ }
+
+ args.mtdn = mtd.mtd_num;
+ return 0;
+}
+
+static void print_ubi_info(const struct mtd_info *mtd_info,
+ const struct mtd_dev_info *mtd)
+{
+ struct ubigen_info ui;
+
+ if (!mtd_info->sysfs_supported) {
+ errmsg("cannot provide UBI info, becasue sub-page size is "
+ "not known");
+ return;
+ }
+
+ ubigen_info_init(&ui, mtd->eb_size, mtd->min_io_size, mtd->subpage_size,
+ 0, 1, 0);
+ printf("Default UBI VID header offset: %d\n", ui.vid_hdr_offs);
+ printf("Default UBI data offset: %d\n", ui.data_offs);
+ printf("Default UBI LEB size: ");
+ ubiutils_print_bytes(ui.leb_size, 0);
+ printf("\n");
+ printf("Maximum UBI volumes count: %d\n", ui.max_volumes);
+}
+
+static void print_region_map(const struct mtd_dev_info *mtd, int fd,
+ const region_info_t *reginfo)
+{
+ unsigned long start;
+ int i, width;
+ int ret_locked, errno_locked, ret_bad, errno_bad;
+
+ printf("Eraseblock map:\n");
+
+ /* Figure out the number of spaces to pad w/out libm */
+ for (i = 1, width = 0; i < reginfo->numblocks; i *= 10, ++width)
+ continue;
+
+ /* If we don't have a fd to query, just show the bare map */
+ if (fd == -1) {
+ ret_locked = ret_bad = -1;
+ errno_locked = errno_bad = ENODEV;
+ } else
+ ret_locked = ret_bad = errno_locked = errno_bad = 0;
+
+ for (i = 0; i < reginfo->numblocks; ++i) {
+ start = reginfo->offset + i * reginfo->erasesize;
+ printf(" %*i: %08lx ", width, i, start);
+
+ if (ret_locked != -1) {
+ ret_locked = mtd_is_locked(mtd, fd, i);
+ if (ret_locked == 1)
+ printf("RO ");
+ else
+ errno_locked = errno;
+ }
+ if (ret_locked != 1)
+ printf(" ");
+
+ if (ret_bad != -1) {
+ ret_bad = mtd_is_bad(mtd, fd, i);
+ if (ret_bad == 1)
+ printf("BAD ");
+ else
+ errno_bad = errno;
+ }
+ if (ret_bad != 1)
+ printf(" ");
+
+ if (((i + 1) % 4) == 0)
+ printf("\n");
+ }
+ if (i % 4)
+ printf("\n");
+
+ if (ret_locked == -1 && errno_locked != EOPNOTSUPP) {
+ errno = errno_locked;
+ sys_errmsg("could not read locked block info");
+ }
+
+ if (mtd->bb_allowed && ret_bad == -1 && errno_bad != EOPNOTSUPP) {
+ errno = errno_bad;
+ sys_errmsg("could not read bad block info");
+ }
+}
+
+static void print_region_info(const struct mtd_dev_info *mtd)
+{
+ region_info_t reginfo;
+ int r, fd;
+
+ /* If we don't have any region info, just return */
+ if (!args.map && mtd->region_cnt == 0)
+ return;
+
+ /* First open the device so we can query it */
+ fd = open(args.node, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ sys_errmsg("couldn't open MTD dev: %s", args.node);
+ if (mtd->region_cnt)
+ return;
+ }
+
+ /* Walk all the regions and show the map for them */
+ if (mtd->region_cnt) {
+ for (r = 0; r < mtd->region_cnt; ++r) {
+ printf("Eraseblock region %i: ", r);
+ if (mtd_regioninfo(fd, r, &reginfo) == 0) {
+ printf(" offset: %#x size: %#x numblocks: %#x\n",
+ reginfo.offset, reginfo.erasesize,
+ reginfo.numblocks);
+ if (args.map)
+ print_region_map(mtd, fd, &reginfo);
+ } else
+ printf(" info is unavailable\n");
+ }
+ } else {
+ reginfo.offset = 0;
+ reginfo.erasesize = mtd->eb_size;
+ reginfo.numblocks = mtd->eb_cnt;
+ reginfo.regionindex = 0;
+ print_region_map(mtd, fd, &reginfo);
+ }
+
+ if (fd != -1)
+ close(fd);
+}
+
+static int print_dev_info(libmtd_t libmtd, const struct mtd_info *mtd_info, int mtdn)
+{
+ int err;
+ struct mtd_dev_info mtd;
+
+ err = mtd_get_dev_info1(libmtd, mtdn, &mtd);
+ if (err) {
+ if (errno == ENODEV)
+ return errmsg("mtd%d does not correspond to any "
+ "existing MTD device", mtdn);
+ return sys_errmsg("cannot get information about MTD device %d",
+ mtdn);
+ }
+
+ printf("mtd%d\n", mtd.mtd_num);
+ printf("Name: %s\n", mtd.name);
+ printf("Type: %s\n", mtd.type_str);
+ printf("Eraseblock size: ");
+ ubiutils_print_bytes(mtd.eb_size, 0);
+ printf("\n");
+ printf("Amount of eraseblocks: %d (", mtd.eb_cnt);
+ ubiutils_print_bytes(mtd.size, 0);
+ printf(")\n");
+ printf("Minimum input/output unit size: %d %s\n",
+ mtd.min_io_size, mtd.min_io_size > 1 ? "bytes" : "byte");
+ if (mtd_info->sysfs_supported)
+ printf("Sub-page size: %d %s\n",
+ mtd.subpage_size,
+ mtd.subpage_size > 1 ? "bytes" : "byte");
+ else if (mtd.type == MTD_NANDFLASH)
+ printf("Sub-page size: unknown\n");
+
+ if (mtd.oob_size > 0)
+ printf("OOB size: %d bytes\n",
+ mtd.oob_size);
+ if (mtd.region_cnt > 0)
+ printf("Additional erase regions: %d\n", mtd.oob_size);
+ if (mtd_info->sysfs_supported)
+ printf("Character device major/minor: %d:%d\n",
+ mtd.major, mtd.minor);
+ printf("Bad blocks are allowed: %s\n",
+ mtd.bb_allowed ? "true" : "false");
+ printf("Device is writable: %s\n",
+ mtd.writable ? "true" : "false");
+
+ if (args.ubinfo)
+ print_ubi_info(mtd_info, &mtd);
+
+ print_region_info(&mtd);
+
+ printf("\n");
+ return 0;
+}
+
+static int print_general_info(libmtd_t libmtd, const struct mtd_info *mtd_info,
+ int all)
+{
+ int i, err, first = 1;
+ struct mtd_dev_info mtd;
+
+ printf("Count of MTD devices: %d\n", mtd_info->mtd_dev_cnt);
+ if (mtd_info->mtd_dev_cnt == 0)
+ return 0;
+
+ for (i = mtd_info->lowest_mtd_num;
+ i <= mtd_info->highest_mtd_num; i++) {
+ err = mtd_get_dev_info1(libmtd, i, &mtd);
+ if (err == -1) {
+ if (errno == ENODEV)
+ continue;
+ return sys_errmsg("libmtd failed get MTD device %d "
+ "information", i);
+ }
+
+ if (!first)
+ printf(", mtd%d", i);
+ else {
+ printf("Present MTD devices: mtd%d", i);
+ first = 0;
+ }
+ }
+ printf("\n");
+ printf("Sysfs interface supported: %s\n",
+ mtd_info->sysfs_supported ? "yes" : "no");
+
+ if (!all)
+ return 0;
+
+ first = 1;
+ printf("\n");
+
+ for (i = mtd_info->lowest_mtd_num;
+ i <= mtd_info->highest_mtd_num; i++) {
+ err = print_dev_info(libmtd, mtd_info, i);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int main(int argc, char * const argv[])
+{
+ int err;
+ libmtd_t libmtd;
+ struct mtd_info mtd_info;
+
+ err = parse_opt(argc, argv);
+ if (err)
+ return -1;
+
+ libmtd = libmtd_open();
+ if (libmtd == NULL) {
+ if (errno == 0)
+ return errmsg("MTD is not present in the system");
+ return sys_errmsg("cannot open libmtd");
+ }
+
+ err = mtd_get_info(libmtd, &mtd_info);
+ if (err) {
+ if (errno == ENODEV)
+ return errmsg("MTD is not present");
+ return sys_errmsg("cannot get MTD information");
+ }
+
+ if (args.node) {
+ /*
+ * A character device was specified, translate this to MTD
+ * device number.
+ */
+ err = translate_dev(libmtd, args.node);
+ if (err)
+ goto out_libmtd;
+ }
+
+ if (args.mtdn == -1)
+ err = print_general_info(libmtd, &mtd_info, args.all);
+ else
+ err = print_dev_info(libmtd, &mtd_info, args.mtdn);
+ if (err)
+ goto out_libmtd;
+
+ libmtd_close(libmtd);
+ return 0;
+
+out_libmtd:
+ libmtd_close(libmtd);
+ return -1;
+}