/* * 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_NAME "mtdinfo" #include #include #include #include #include #include #include #include #include #include "common.h" /* The variables below are set by command line arguments */ struct args { unsigned int all:1; unsigned int ubinfo:1; unsigned int map:1; const char *node; }; static struct args args = { .ubinfo = 0, .all = 0, .node = NULL, }; static void display_help(void) { printf( "%1$s version %2$s - a tool to print MTD information.\n" "\n" "Usage: %1$s [--map | -M] [--ubi-info | -u]\n" " %1$s --all [--ubi-info | -u]\n" " %1$s [--help | --version]\n" "\n" "Options:\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" " Note: `--all' may give less info per device\n" " than, e.g., `mtdinfo /dev/mtdX'\n" "-h, --help print help message\n" "-V, --version print program version\n" "\n" "Examples:\n" " %1$s /dev/mtd0 print information MTD device /dev/mtd0\n" " %1$s /dev/mtd0 -u print information MTD device /dev/mtd0\n" " %4$*3$s and include UBI layout information\n" " %1$s -a print information about all MTD devices\n", PROGRAM_NAME, VERSION, (int)strlen(PROGRAM_NAME) + 3, ""); } static const struct option long_options[] = { { .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; key = getopt_long(argc, argv, "auMhV", long_options, NULL); if (key == -1) break; switch (key) { case 'a': args.all = 1; break; case 'u': args.ubinfo = 1; break; case 'M': args.map = 1; break; case 'h': display_help(); exit(EXIT_SUCCESS); case 'V': common_print_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.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); } return mtd.mtd_num; } 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: "); util_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 * * FIXME: We can't get region_info (via ioctl) without having the MTD * node path. This is a problem for `mtdinfo -a', for example, * since it doesn't provide any filepath information. */ if (!args.node || (!args.map && mtd->region_cnt == 0)) return; memset(®info, 0, sizeof(reginfo)); /* 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: "); util_print_bytes(mtd.eb_size, 0); printf("\n"); printf("Amount of eraseblocks: %d (", mtd.eb_cnt); util_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 || mtd.type == MTD_MLCNANDFLASH) 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 to 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; printf("\n"); for (i = mtd_info->lowest_mtd_num; i <= mtd_info->highest_mtd_num; i++) { if (!mtd_dev_present(libmtd, i)) continue; 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) return sys_errmsg("cannot get MTD information"); if (!args.all && args.node) { int mtdn; /* * A character device was specified, translate this to MTD * device number. */ mtdn = translate_dev(libmtd, args.node); if (mtdn < 0) goto out_libmtd; err = print_dev_info(libmtd, &mtd_info, mtdn); } else err = print_general_info(libmtd, &mtd_info, args.all); if (err) goto out_libmtd; libmtd_close(libmtd); return 0; out_libmtd: libmtd_close(libmtd); return -1; }