diff options
Diffstat (limited to 'ubi-utils/mtdinfo.c')
-rw-r--r-- | ubi-utils/mtdinfo.c | 446 |
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, ®info) == 0) { + printf(" offset: %#x size: %#x numblocks: %#x\n", + reginfo.offset, reginfo.erasesize, + reginfo.numblocks); + if (args.map) + print_region_map(mtd, fd, ®info); + } 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, ®info); + } + + 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; +} |