From 32252f2b902d88f2991d260f2982b10b2016c33b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 26 Apr 2009 09:01:12 +0300 Subject: ubi-utils: add sysfs interface support and new tool This large commit makes several things. 1. Switches libmtd to use the new sysfs interface 2. Implements new handy 'mtdinfo' utility 3. Does minore amendmends in libubi and some ubi-tools. Signed-off-by: Artem Bityutskiy --- ubi-utils/.gitignore | 1 + ubi-utils/Makefile | 10 +- ubi-utils/include/libmtd.h | 86 ++++- ubi-utils/src/libmtd.c | 788 +++++++++++++++++++++++++++++++++++++----- ubi-utils/src/libmtd_int.h | 88 +++++ ubi-utils/src/libmtd_legacy.c | 354 +++++++++++++++++++ ubi-utils/src/libubi.c | 35 +- ubi-utils/src/mtdinfo.c | 328 ++++++++++++++++++ ubi-utils/src/ubiformat.c | 103 ++++-- ubi-utils/src/ubinfo.c | 22 +- 10 files changed, 1656 insertions(+), 159 deletions(-) create mode 100644 ubi-utils/src/libmtd_int.h create mode 100644 ubi-utils/src/libmtd_legacy.c create mode 100644 ubi-utils/src/mtdinfo.c (limited to 'ubi-utils') diff --git a/ubi-utils/.gitignore b/ubi-utils/.gitignore index 4155cd1..66e766c 100644 --- a/ubi-utils/.gitignore +++ b/ubi-utils/.gitignore @@ -8,3 +8,4 @@ /ubirename /ubirmvol /ubiupdatevol +/mtdinfo diff --git a/ubi-utils/Makefile b/ubi-utils/Makefile index dcff7e3..b5388ec 100644 --- a/ubi-utils/Makefile +++ b/ubi-utils/Makefile @@ -6,12 +6,12 @@ KERNELHDR := ../include SUBDIRS = old-utils -#CFLAGS += -Werror +# CFLAGS += -Werror CPPFLAGS += -Iinclude -Isrc -I$(KERNELHDR) LIBS = libubi libmtd libubigen libiniparser libscan TARGETS = ubiupdatevol ubimkvol ubirmvol ubicrc32 ubinfo ubiattach \ - ubidetach ubinize ubiformat ubirename + ubidetach ubinize ubiformat ubirename mtdinfo VPATH = src @@ -30,13 +30,17 @@ $(BUILDDIR)/ubinize: $(addprefix $(BUILDDIR)/,\ ubinize.o common.o crc32.o libiniparser.a libubigen.a) # $(CC) $(CFLAGS) $(filter %.o, $^) -L. -liniparser -lubigen -o $@ +$(BUILDDIR)/mtdinfo: $(addprefix $(BUILDDIR)/,\ + libmtd.a libubigen.a crc32.o common.o) +# $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lubigen -o $@ + $(BUILDDIR)/ubiformat: $(addprefix $(BUILDDIR)/,\ ubiformat.o common.o crc32.o libmtd.a libscan.a libubi.a libubigen.a) # $(CC) $(CFLAGS) $(filter %.o, $^) -L. -lmtd -lscan -lubi -lubigen -o $@ $(BUILDDIR)/libubi.a: $(BUILDDIR)/libubi.o -$(BUILDDIR)/libmtd.a: $(BUILDDIR)/libmtd.o +$(BUILDDIR)/libmtd.a: $(BUILDDIR)/libmtd.o $(BUILDDIR)/libmtd_legacy.o $(BUILDDIR)/libubigen.a: $(BUILDDIR)/libubigen.o diff --git a/ubi-utils/include/libmtd.h b/ubi-utils/include/libmtd.h index 6a93e5f..8d5bc4f 100644 --- a/ubi-utils/include/libmtd.h +++ b/ubi-utils/include/libmtd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2008, 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 as published by @@ -27,9 +27,28 @@ extern "C" { #endif +/* Maximum MTD device name length */ +#define MTD_NAME_MAX 127 /* Maximum MTD device type string length */ #define MTD_TYPE_MAX 64 +/* MTD library descriptor */ +typedef void * libmtd_t; + +/** + * @dev_count: count of MTD devices in system + * @lowest_dev_num: lowest MTD device number + * @highest_dev_num: highest MTD device number + * @sysfs_supported: non-zero if sysfs is supported by MTD + */ +struct mtd_info +{ + int dev_count; + int lowest_dev_num; + int highest_dev_num; + unsigned int sysfs_supported:1; +}; + /** * struct mtd_dev_info - information about an MTD device. * @dev_num: MTD device number @@ -37,11 +56,14 @@ extern "C" { * @minor: minor number of corresponding character device * @type: flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h) * @type_str: static R/O flash type string + * @name: device name * @size: device size in bytes * @eb_cnt: count of eraseblocks * @eb_size: eraseblock size * @min_io_size: minimum input/output unit size * @subpage_size: sub-page size + * @oob_size: OOB size (zero if the device does not have OOB area) + * @region_cnt: count of additional erase regions * @writable: zero if the device is read-only * @bb_allowed: non-zero if the MTD device may have bad eraseblocks */ @@ -51,26 +73,69 @@ struct mtd_dev_info int major; int minor; int type; - const char type_str[MTD_TYPE_MAX]; + const char type_str[MTD_TYPE_MAX + 1]; + const char name[MTD_NAME_MAX + 1]; long long size; int eb_cnt; int eb_size; int min_io_size; int subpage_size; + int oob_size; + int region_cnt; unsigned int writable:1; unsigned int bb_allowed:1; }; +/** + * libmtd_open - open MTD library. + * + * This function initializes and opens the MTD library and returns MTD library + * descriptor in case of success and %NULL in case of failure. In case of + * failure, errno contains zero if MTD is not present in the system, or + * contains the error code if a real error happened. + */ +libmtd_t libmtd_open(void); + +/** + * libmtd_close - close MTD library. + * @desc: MTD library descriptor + */ +void libmtd_close(libmtd_t desc); + +/** + * mtd_get_info - get general MTD information. + * @desc: MTD library descriptor + * @info: the MTD device information is returned here + * + * This function fills the passed @info object with general MTD information and + * returns %0 in case of success and %-1 in case of failure. If MTD subsystem is + * not present in the system, errno is set to @ENODEV. + */ +int mtd_get_info(libmtd_t desc, struct mtd_info *info); + /** * mtd_get_dev_info - get information about an MTD device. + * @desc: MTD library descriptor * @node: name of the MTD device node * @mtd: the MTD device information is returned here * * This function gets information about MTD device defined by the @node device * node file and saves this information in the @mtd object. Returns %0 in case - * of success and %-1 in case of failure. + * of success and %-1 in case of failure. If MTD subsystem is not present in the + * system, or the MTD device does not exist, errno is set to @ENODEV. */ -int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd); +int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd); + +/** + * mtd_get_dev_info1 - get information about an MTD device. + * @desc: MTD library descriptor + * @dev_num: MTD device number to fetch information about + * @mtd: the MTD device information is returned here + * + * This function is identical to 'mtd_get_dev_info()' except that it accepts + * MTD device number, not MTD character device. + */ +int mtd_get_dev_info1(libmtd_t desc, int dev_num, struct mtd_dev_info *mtd); /** * mtd_erase - erase an eraseblock. @@ -78,7 +143,7 @@ int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd); * @fd: MTD device node file descriptor * @eb: eraseblock to erase * - * This function erases eraseblock @eb of MTD device decribed by @fd. Returns + * This function erases eraseblock @eb of MTD device described by @fd. Returns * %0 in case of success and %-1 in case of failure. */ int mtd_erase(const struct mtd_dev_info *mtd, int fd, int eb); @@ -137,6 +202,17 @@ int mtd_read(const struct mtd_dev_info *mtd, int fd, int eb, int offs, int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, void *buf, int len); +/** + * mtd_probe_node - test MTD node. + * @desc: MTD library descriptor + * @node: the node to test + * + * This function tests whether @node is an MTD device node and returns %1 if it + * is, and %-1 if it is not (errno is ENODEV in this case) or if an error + * occurred. + */ +int mtd_probe_node(libmtd_t desc, const char *node); + #ifdef __cplusplus } #endif diff --git a/ubi-utils/src/libmtd.c b/ubi-utils/src/libmtd.c index faa958f..046beea 100644 --- a/ubi-utils/src/libmtd.c +++ b/ubi-utils/src/libmtd.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2008 Nokia Corporation + * Copyright (c) International Business Machines Corp., 2006 + * 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 as published by @@ -20,127 +21,696 @@ * MTD library. */ +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include - #include + #include +#include "libmtd_int.h" #include "common.h" -#define PROGRAM_NAME "libmtd" -#define MTD_DEV_MAJOR 90 +/** + * mkpath - compose full path from 2 given components. + * @path: the first component + * @name: the second component + * + * This function returns the resulting path in case of success and %NULL in + * case of failure. + */ +static char *mkpath(const char *path, const char *name) +{ + char *n; + int len1 = strlen(path); + int len2 = strlen(name); + + n = malloc(len1 + len2 + 2); + if (!n) { + sys_errmsg("cannot allocate %d bytes", len1 + len2 + 2); + return NULL; + } + + memcpy(n, path, len1); + if (n[len1 - 1] != '/') + n[len1++] = '/'; + + memcpy(n + len1, name, len2 + 1); + return n; +} + +/** + * read_data - read data from a file. + * @file: the file to read from + * @buf: the buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. Note, if the file contains more then @buf_len bytes of + * date, this function fails with %EINVAL error code. + */ +static int read_data(const char *file, void *buf, int buf_len) +{ + int fd, rd, tmp, tmp1; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, buf_len); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + + /* Make sure all data is read */ + tmp1 = read(fd, &tmp, 1); + if (tmp1 == 1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (tmp1) { + errmsg("file \"%s\" contains too much data (> %d bytes)", + file, buf_len); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", file); + return -1; + } + + return rd; + +out_error: + close(fd); + return -1; +} + +/** + * read_major - read major and minor numbers from a file. + * @file: name of the file to read from + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns % in case of success, and %-1 in case of failure. + */ +static int read_major(const char *file, int *major, int *minor) +{ + int ret; + char buf[50]; + + ret = read_data(file, buf, 50); + if (ret < 0) + return ret; + + ret = sscanf(buf, "%d:%d\n", major, minor); + if (ret != 2) { + errno = EINVAL; + return errmsg("\"%s\" does not have major:minor format", file); + } + + if (*major < 0 || *minor < 0) { + errno = EINVAL; + return errmsg("bad major:minor %d:%d in \"%s\"", + *major, *minor, file); + } + + return 0; +} + +/** + * dev_get_major - get major and minor numbers of an MTD device. + * @lib: libmtd descriptor + * @dev_num: MTD device number + * @major: major number is returned here + * @minor: minor number is returned here + * + * This function returns zero in case of success and %-1 in case of failure. + */ +static int dev_get_major(struct libmtd *lib, int dev_num, int *major, int *minor) +{ + char file[strlen(lib->mtd_dev) + 50]; + + sprintf(file, lib->mtd_dev, dev_num); + return read_major(file, major, minor); +} -int mtd_get_dev_info(const char *node, struct mtd_dev_info *mtd) +/** + * dev_read_data - read data from an MTD device's sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD device number + * @buf: buffer to read to + * @buf_len: buffer length + * + * This function returns number of read bytes in case of success and %-1 in + * case of failure. + */ +static int dev_read_data(const char *patt, int dev_num, void *buf, int buf_len) +{ + char file[strlen(patt) + 100]; + + sprintf(file, patt, dev_num); + return read_data(file, buf, buf_len); +} + +/** + * read_hex_ll - read a hex 'long long' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function reads file @file and interprets its contents as hexadecimal + * 'long long' integer. If this is not true, it fails with %EINVAL error code. + * Returns %0 in case of success and %-1 in case of failure. + */ +static int read_hex_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, 50); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (rd == 50) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + + if (sscanf(buf, "%llx\n", value) != 1) { + errmsg("cannot read integer from \"%s\"\n", file); + errno = EINVAL; + goto out_error; + } + + if (*value < 0) { + errmsg("negative value %lld in \"%s\"", *value, file); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) + return sys_errmsg("close failed on \"%s\"", file); + + return 0; + +out_error: + close(fd); + return -1; +} + +/** + * read_pos_ll - read a positive 'long long' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function reads file @file and interprets its contents as a positive + * 'long long' integer. If this is not true, it fails with %EINVAL error code. + * Returns %0 in case of success and %-1 in case of failure. + */ +static int read_pos_ll(const char *file, long long *value) +{ + int fd, rd; + char buf[50]; + + fd = open(file, O_RDONLY); + if (fd == -1) + return -1; + + rd = read(fd, buf, 50); + if (rd == -1) { + sys_errmsg("cannot read \"%s\"", file); + goto out_error; + } + if (rd == 50) { + errmsg("contents of \"%s\" is too long", file); + errno = EINVAL; + goto out_error; + } + + if (sscanf(buf, "%lld\n", value) != 1) { + errmsg("cannot read integer from \"%s\"\n", file); + errno = EINVAL; + goto out_error; + } + + if (*value < 0) { + errmsg("negative value %lld in \"%s\"", *value, file); + errno = EINVAL; + goto out_error; + } + + if (close(fd)) + return sys_errmsg("close failed on \"%s\"", file); + + return 0; + +out_error: + close(fd); + return -1; +} + +/** + * read_hex_int - read an 'int' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function is the same as 'read_pos_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_hex_int(const char *file, int *value) +{ + long long res; + + if (read_hex_ll(file, &res)) + return -1; + + /* Make sure the value has correct range */ + if (res > INT_MAX || res < INT_MIN) { + errmsg("value %lld read from file \"%s\" is out of range", + res, file); + errno = EINVAL; + return -1; + } + + *value = res; + return 0; +} + +/** + * read_pos_int - read a positive 'int' value from a file. + * @file: the file to read from + * @value: the result is stored here + * + * This function is the same as 'read_pos_ll()', but it reads an 'int' + * value, not 'long long'. + */ +static int read_pos_int(const char *file, int *value) +{ + long long res; + + if (read_pos_ll(file, &res)) + return -1; + + /* Make sure the value is not too big */ + if (res > INT_MAX) { + errmsg("value %lld read from file \"%s\" is out of range", + res, file); + errno = EINVAL; + return -1; + } + + *value = res; + return 0; +} + +/** + * dev_read_hex_int - read an hex 'int' value from an MTD device sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_hex_int(const char *patt, int dev_num, int *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_hex_int(file, value); +} + +/** + * dev_read_pos_int - read a positive 'int' value from an MTD device sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_pos_int(const char *patt, int dev_num, int *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_pos_int(file, value); +} + +/** + * dev_read_pos_ll - read a positive 'long long' value from an MTD device sysfs file. + * @patt: file pattern to read from + * @dev_num: MTD device number + * @value: the result is stored here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_read_pos_ll(const char *patt, int dev_num, long long *value) +{ + char file[strlen(patt) + 50]; + + sprintf(file, patt, dev_num); + return read_pos_ll(file, value); +} + +/** + * type_str2int - convert MTD device type to integer. + * @str: MTD device type string to convert + * + * This function converts MTD device type string @str, read from sysfs, into an + * integer. + */ +static int type_str2int(const char *str) +{ + if (!strcmp(str, "nand")) + return MTD_NANDFLASH; + if (!strcmp(str, "nor")) + return MTD_NORFLASH; + if (!strcmp(str, "rom")) + return MTD_ROM; + if (!strcmp(str, "absent")) + return MTD_ABSENT; + if (!strcmp(str, "dataflash")) + return MTD_DATAFLASH; + if (!strcmp(str, "ram")) + return MTD_RAM; + if (!strcmp(str, "ubi")) + return MTD_UBIVOLUME; + return -1; +} + +/** + * dev_node2num - find UBI device number by its character device node. + * @lib: MTD library descriptor + * @node: name of the MTD device node + * @dev_num: MTD device number is returned here + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int dev_node2num(struct libmtd *lib, const char *node, int *dev_num) { struct stat st; - struct mtd_info_user ui; - int fd, ret; - loff_t offs = 0; + int i, major, minor; + struct mtd_info info; if (stat(node, &st)) - return sys_errmsg("cannot open \"%s\"", node); + return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { + errmsg("\"%s\" is not a character device", node); errno = EINVAL; - return errmsg("\"%s\" is not a character device", node); + return -1; } - mtd->major = major(st.st_rdev); - mtd->minor = minor(st.st_rdev); + major = major(st.st_rdev); + minor = minor(st.st_rdev); - if (mtd->major != MTD_DEV_MAJOR) { - errno = EINVAL; - return errmsg("\"%s\" has major number %d, MTD devices have " - "major %d", node, mtd->major, MTD_DEV_MAJOR); + if (mtd_get_info((libmtd_t *)lib, &info)) + return -1; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + if (!errno) + break; + return -1; + } + + if (major1 == major && minor1 == minor) { + errno = 0; + *dev_num = i; + return 0; + } } - mtd->dev_num = mtd->minor / 2; - mtd->writable = !(mtd->minor & 1); + errno = ENODEV; + return -1; +} - fd = open(node, O_RDWR); - if (fd == -1) - return sys_errmsg("cannot open \"%s\"", node); +libmtd_t libmtd_open(void) +{ + int fd; + struct libmtd *lib; + + lib = calloc(1, sizeof(struct libmtd)); + if (!lib) + return NULL; + + lib->sysfs_mtd = mkpath("/sys", SYSFS_MTD); + if (!lib->sysfs_mtd) + goto out_error; + + lib->mtd = mkpath(lib->sysfs_mtd, MTD_NAME_PATT); + if (!lib->mtd) + goto out_error; + + /* Make sure MTD is recent enough and supports sysfs */ + fd = open(lib->sysfs_mtd, O_RDONLY); + if (fd == -1) { + free(lib->mtd); + free(lib->sysfs_mtd); + if (errno != ENOENT || legacy_libmtd_open()) { + free(lib); + return NULL; + } + lib->mtd_name = lib->mtd = lib->sysfs_mtd = NULL; + return lib; + } - if (ioctl(fd, MEMGETINFO, &ui)) { - sys_errmsg("MEMGETINFO ioctl request failed"); - goto out_close; + if (close(fd)) { + sys_errmsg("close failed on \"%s\"", lib->sysfs_mtd); + goto out_error; } - ret = ioctl(fd, MEMGETBADBLOCK, &offs); - if (ret == -1) { - if (errno != EOPNOTSUPP) { - sys_errmsg("MEMGETBADBLOCK ioctl failed"); - goto out_close; + lib->mtd_name = mkpath(lib->mtd, MTD_NAME); + if (!lib->mtd_name) + goto out_error; + + lib->mtd_dev = mkpath(lib->mtd, MTD_DEV); + if (!lib->mtd_dev) + goto out_error; + + lib->mtd_type = mkpath(lib->mtd, MTD_TYPE); + if (!lib->mtd_type) + goto out_error; + + lib->mtd_eb_size = mkpath(lib->mtd, MTD_EB_SIZE); + if (!lib->mtd_eb_size) + goto out_error; + + lib->mtd_size = mkpath(lib->mtd, MTD_SIZE); + if (!lib->mtd_size) + goto out_error; + + lib->mtd_min_io_size = mkpath(lib->mtd, MTD_MIN_IO_SIZE); + if (!lib->mtd_min_io_size) + goto out_error; + + lib->mtd_subpage_size = mkpath(lib->mtd, MTD_SUBPAGE_SIZE); + if (!lib->mtd_subpage_size) + goto out_error; + + lib->mtd_oob_size = mkpath(lib->mtd, MTD_OOB_SIZE); + if (!lib->mtd_oob_size) + goto out_error; + + lib->mtd_region_cnt = mkpath(lib->mtd, MTD_REGION_CNT); + if (!lib->mtd_region_cnt) + goto out_error; + + lib->mtd_flags = mkpath(lib->mtd, MTD_FLAGS); + if (!lib->mtd_flags) + goto out_error; + + lib->sysfs_supported = 1; + return lib; + +out_error: + libmtd_close((libmtd_t)lib); + return NULL; +} + +void libmtd_close(libmtd_t desc) +{ + struct libmtd *lib = (struct libmtd *)desc; + + free(lib->mtd_flags); + free(lib->mtd_region_cnt); + free(lib->mtd_oob_size); + free(lib->mtd_subpage_size); + free(lib->mtd_min_io_size); + free(lib->mtd_size); + free(lib->mtd_eb_size); + free(lib->mtd_type); + free(lib->mtd_dev); + free(lib->mtd_name); + free(lib->mtd); + free(lib->sysfs_mtd); + free(lib); +} + +int mtd_get_info(libmtd_t desc, struct mtd_info *info) +{ + DIR *sysfs_mtd; + struct dirent *dirent; + struct libmtd *lib = (struct libmtd *)desc; + + memset(info, '\0', sizeof(struct mtd_info)); + + if (!lib->sysfs_supported) + return legacy_mtd_get_info(info); + + info->sysfs_supported = 1; + + /* + * We have to scan the MTD sysfs directory to identify how many MTD + * devices are present. + */ + sysfs_mtd = opendir(lib->sysfs_mtd); + if (!sysfs_mtd) { + if (errno == ENOENT) { + errno = ENODEV; + return -1; } - errno = 0; - mtd->bb_allowed = 0; - } else - mtd->bb_allowed = 1; - - mtd->type = ui.type; - mtd->size = ui.size; - mtd->eb_size = ui.erasesize; - mtd->min_io_size = ui.writesize; - - if (mtd->min_io_size <= 0) { - errmsg("mtd%d (%s) has insane min. I/O unit size %d", - mtd->dev_num, node, mtd->min_io_size); - goto out_close; + return sys_errmsg("cannot open \"%s\"", lib->sysfs_mtd); } - if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { - errmsg("mtd%d (%s) has insane eraseblock size %d", - mtd->dev_num, node, mtd->eb_size); - goto out_close; + + info->lowest_dev_num = INT_MAX; + while (1) { + int dev_num, ret; + char tmp_buf[256]; + + errno = 0; + dirent = readdir(sysfs_mtd); + if (!dirent) + break; + + if (strlen(dirent->d_name) >= 255) { + errmsg("invalid entry in %s: \"%s\"", + lib->sysfs_mtd, dirent->d_name); + errno = EINVAL; + goto out_close; + } + + ret = sscanf(dirent->d_name, MTD_NAME_PATT"%s", + &dev_num, tmp_buf); + if (ret == 1) { + info->dev_count += 1; + if (dev_num > info->highest_dev_num) + info->highest_dev_num = dev_num; + if (dev_num < info->lowest_dev_num) + info->lowest_dev_num = dev_num; + } } - if (mtd->size <= 0 || mtd->size < mtd->eb_size) { - errmsg("mtd%d (%s) has insane size %lld", - mtd->dev_num, node, mtd->size); + + if (!dirent && errno) { + sys_errmsg("readdir failed on \"%s\"", lib->sysfs_mtd); goto out_close; } - mtd->eb_cnt = mtd->size / mtd->eb_size; - switch(mtd->type) { - case MTD_ABSENT: - errmsg("mtd%d (%s) is removable and is not present", - mtd->dev_num, node); - goto out_close; - case MTD_RAM: - strcpy(mtd->type_str, "RAM-based"); - break; - case MTD_ROM: - strcpy(mtd->type_str, "ROM"); - break; - case MTD_NORFLASH: - strcpy(mtd->type_str, "NOR"); - break; - case MTD_NANDFLASH: - strcpy(mtd->type_str, "NAND"); - break; - case MTD_DATAFLASH: - strcpy(mtd->type_str, "DataFlash"); - break; - case MTD_UBIVOLUME: - strcpy(mtd->type_str, "UBI-emulated MTD"); - break; - default: - strcpy(mtd->type_str, "Unknown flash type"); - break; - } - - if (ui.flags & MTD_WRITEABLE) - mtd->writable = 1; + if (closedir(sysfs_mtd)) + return sys_errmsg("closedir failed on \"%s\"", lib->sysfs_mtd); + + if (info->lowest_dev_num == INT_MAX) + info->lowest_dev_num = 0; - close(fd); return 0; out_close: - close(fd); + closedir(sysfs_mtd); return -1; } +int mtd_get_dev_info1(libmtd_t desc, int dev_num, struct mtd_dev_info *mtd) +{ + int ret; + struct stat st; + struct libmtd *lib = (struct libmtd *)desc; + + memset(mtd, '\0', sizeof(struct mtd_dev_info)); + mtd->dev_num = dev_num; + + if (!lib->sysfs_supported) + return legacy_get_dev_info1(dev_num, mtd); + else { + char file[strlen(lib->mtd) + 10]; + + sprintf(file, lib->mtd, dev_num); + if (stat(file, &st)) { + if (errno == ENOENT) + errno = ENODEV; + return -1; + } + } + + if (dev_get_major(lib, dev_num, &mtd->major, &mtd->minor)) + return -1; + + ret = dev_read_data(lib->mtd_name, dev_num, &mtd->name, + MTD_NAME_MAX); + if (ret < 0) + return -1; + ((char *)mtd->name)[ret - 1] = '\0'; + + ret = dev_read_data(lib->mtd_type, dev_num, &mtd->type_str, + MTD_TYPE_MAX); + if (ret < 0) + return -1; + ((char *)mtd->type_str)[ret - 1] = '\0'; + + if (dev_read_pos_int(lib->mtd_eb_size, dev_num, &mtd->eb_size)) + return -1; + if (dev_read_pos_ll(lib->mtd_size, dev_num, &mtd->size)) + return -1; + if (dev_read_pos_int(lib->mtd_min_io_size, dev_num, &mtd->min_io_size)) + return -1; + if (dev_read_pos_int(lib->mtd_subpage_size, dev_num, &mtd->subpage_size)) + return -1; + if (dev_read_pos_int(lib->mtd_oob_size, dev_num, &mtd->oob_size)) + return -1; + if (dev_read_pos_int(lib->mtd_region_cnt, dev_num, &mtd->region_cnt)) + return -1; + if (dev_read_hex_int(lib->mtd_flags, dev_num, &ret)) + return -1; + mtd->writable = !!(ret & MTD_WRITEABLE); + + mtd->eb_cnt = mtd->size / mtd->eb_size; + mtd->type = type_str2int(mtd->type_str); + mtd->bb_allowed = !!(mtd->type == MTD_NANDFLASH); + + return 0; +} + +int mtd_get_dev_info(libmtd_t desc, const char *node, struct mtd_dev_info *mtd) +{ + int dev_num; + struct libmtd *lib = (struct libmtd *)desc; + + if (!lib->sysfs_supported) + return legacy_get_dev_info(node, mtd); + + if (dev_node2num(lib, node, &dev_num)) + return -1; + + return mtd_get_dev_info1(desc, dev_num, mtd); +} + int mtd_erase(const struct mtd_dev_info *mtd, int fd, int eb) { struct erase_info_user ei; @@ -252,7 +822,7 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, errno = EINVAL; return -1; } -#if 0 + if (offs % mtd->subpage_size) { errmsg("write offset %d is not aligned to mtd%d min. I/O size %d", offs, mtd->dev_num, mtd->subpage_size); @@ -265,7 +835,6 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, errno = EINVAL; return -1; } -#endif /* Seek to the beginning of the eraseblock */ seek = (off_t)eb * mtd->eb_size + offs; @@ -280,3 +849,48 @@ int mtd_write(const struct mtd_dev_info *mtd, int fd, int eb, int offs, return 0; } + +int mtd_probe_node(libmtd_t desc, const char *node) +{ + struct stat st; + struct mtd_info info; + int i, major, minor; + struct libmtd *lib = (struct libmtd *)desc; + + if (stat(node, &st)) + return sys_errmsg("cannot get information about \"%s\"", node); + + if (!S_ISCHR(st.st_mode)) { + errmsg("\"%s\" is not a character device", node); + errno = EINVAL; + return -1; + } + + major = major(st.st_rdev); + minor = minor(st.st_rdev); + + if (mtd_get_info((libmtd_t *)lib, &info)) + return -1; + + if (!lib->sysfs_supported) + return 0; + + for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) { + int major1, minor1, ret; + + ret = dev_get_major(lib, i, &major1, &minor1); + if (ret) { + if (errno == ENOENT) + continue; + if (!errno) + break; + return -1; + } + + if (major1 == major && minor1 == minor) + return 1; + } + + errno = 0; + return -1; +} diff --git a/ubi-utils/src/libmtd_int.h b/ubi-utils/src/libmtd_int.h new file mode 100644 index 0000000..7de4b42 --- /dev/null +++ b/ubi-utils/src/libmtd_int.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * MTD library. + */ + +#ifndef __LIBMTD_INT_H__ +#define __LIBMTD_INT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROGRAM_NAME "libmtd" + +#define SYSFS_MTD "class/mtd" +#define MTD_NAME_PATT "mtd%d" +#define MTD_DEV "dev" +#define MTD_NAME "name" +#define MTD_TYPE "type" +#define MTD_EB_SIZE "erasesize" +#define MTD_SIZE "size" +#define MTD_MIN_IO_SIZE "writesize" +#define MTD_SUBPAGE_SIZE "subpagesize" +#define MTD_OOB_SIZE "oobsize" +#define MTD_REGION_CNT "numeraseregions" +#define MTD_FLAGS "flags" + +/** + * libmtd - MTD library description data structure. + * @sysfs_mtd: MTD directory in sysfs + * @mtd: MTD device sysfs directory pattern + * @mtd_dev: MTD device major/minor numbers file pattern + * @mtd_name: MTD device name file pattern + * @mtd_type: MTD device type file pattern + * @mtd_eb_size: MTD device eraseblock size file pattern + * @mtd_size: MTD device size file pattern + * @mtd_min_io_size: minimum I/O unit size file pattern + * @mtd_subpage_size: sub-page size file pattern + * @mtd_oob_size: MTD device OOB size file pattern + * @mtd_region_cnt: count of additional erase regions file pattern + * @mtd_flags: MTD device flags file pattern + * @sysfs_supported: non-zero if sysfs is supported by MTD + */ +struct libmtd +{ + char *sysfs_mtd; + char *mtd; + char *mtd_dev; + char *mtd_name; + char *mtd_type; + char *mtd_eb_size; + char *mtd_size; + char *mtd_min_io_size; + char *mtd_subpage_size; + char *mtd_oob_size; + char *mtd_region_cnt; + char *mtd_flags; + unsigned int sysfs_supported:1; +}; + +int legacy_libmtd_open(void); +int legacy_mtd_get_info(struct mtd_info *info); +int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd); +int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd); + +#ifdef __cplusplus +} +#endif + +#endif /* !__LIBMTD_INT_H__ */ diff --git a/ubi-utils/src/libmtd_legacy.c b/ubi-utils/src/libmtd_legacy.c new file mode 100644 index 0000000..cd474b1 --- /dev/null +++ b/ubi-utils/src/libmtd_legacy.c @@ -0,0 +1,354 @@ +/* + * 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Artem Bityutskiy + * + * This file is part of the MTD library. Implements pre-2.6.30 kernels support, + * where MTD did not have sysfs interface. The main limitation of the old + * kernels was that the sub-page size was not exported to user-space, so it was + * not possible to get sub-page size. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libmtd_int.h" +#include "common.h" + +#define MTD_PROC_FILE "/proc/mtd" +#define MTD_DEV_PATT "/dev/mtd%d" +#define MTD_DEV_MAJOR 90 + +#define PROC_MTD_FIRST "dev: size erasesize name\n" +#define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1) +#define PROC_MTD_MAX_LEN 4096 +#define PROC_MTD_PATT "mtd%d: %llx %x" + +/** + * struct proc_parse_info - /proc/mtd parsing information. + * @dev_num: MTD device number + * @size: device size + * @eb_size: eraseblock size + * @name: device name + * @buf: contents of /proc/mtd + * @data_size: how much data was read into @buf + * @pos: next string in @buf to parse + */ +struct proc_parse_info +{ + int dev_num; + long long size; + char name[MTD_NAME_MAX + 1]; + int eb_size; + char *buf; + int data_size; + char *next; +}; + +static int proc_parse_start(struct proc_parse_info *pi) +{ + int fd, ret; + + fd = open(MTD_PROC_FILE, O_RDONLY); + if (fd == -1) + return -1; + + pi->buf = malloc(PROC_MTD_MAX_LEN); + if (!pi->buf) { + sys_errmsg("cannot allocate %d bytes of memory", + PROC_MTD_MAX_LEN); + goto out_close; + } + + ret = read(fd, pi->buf, PROC_MTD_MAX_LEN); + if (ret == -1) { + sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE); + goto out_free; + } + + if (ret < PROC_MTD_FIRST_LEN || + memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) { + errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE, + PROC_MTD_FIRST); + goto out_free; + } + + pi->data_size = ret; + pi->next = pi->buf + PROC_MTD_FIRST_LEN; + + close(fd); + return 0; + +out_free: + free(pi->buf); +out_close: + close(fd); + return -1; +} + +static int proc_parse_next(struct proc_parse_info *pi) +{ + int ret, len, pos = pi->next - pi->buf; + char *p, *p1; + + if (pos >= pi->data_size) { + free(pi->buf); + return 0; + } + + ret = sscanf(pi->next, PROC_MTD_PATT, &pi->dev_num, &pi->size, + &pi->eb_size); + if (ret != 3) + return errmsg("\"%s\" pattern not found", PROC_MTD_PATT); + + p = memchr(pi->next, '\"', pi->data_size - pos); + if (!p) + return errmsg("opening \" not fount"); + p += 1; + pos = p - pi->buf; + if (pos >= pi->data_size) + return errmsg("opening \" not fount"); + + p1 = memchr(p, '\"', pi->data_size - pos); + if (!p1) + return errmsg("closing \" not fount"); + pos = p1 - pi->buf; + if (pos >= pi->data_size) + return errmsg("closing \" not fount"); + + len = p1 - p; + if (len > MTD_NAME_MAX) + return errmsg("too long mtd%d device name", pi->dev_num); + + memcpy(pi->name, p, len); + pi->name[len] = '\0'; + + if (p1[1] != '\n') + return errmsg("opening \"\n\" not fount"); + pi->next = p1 + 2; + return 1; +} + +/** + * legacy_libmtd_open - legacy version of 'libmtd_open()'. + * + * This function is just checks that MTD is present in the system. Returns + * zero in case of success and %-1 in case of failure. In case of failure, + * errno contains zero if MTD is not present in the system, or contains the + * error code if a real error happened. This is similar to the 'libmtd_open()' + * return conventions. + */ +int legacy_libmtd_open(void) +{ + int fd; + + fd = open(MTD_PROC_FILE, O_RDONLY); + if (fd == -1) { + if (errno == ENOENT) + errno = 0; + return -1; + } + + close(fd); + return 0; +} + +/** + * legacy_mtd_get_info - legacy version of 'mtd_get_info()'. + * @info: the MTD device information is returned here + * + * This function is similar to 'mtd_get_info()' and has the same conventions. + */ +int legacy_mtd_get_info(struct mtd_info *info) +{ + int ret; + struct proc_parse_info pi; + + ret = proc_parse_start(&pi); + if (ret) + return -1; + + info->lowest_dev_num = INT_MAX; + while (proc_parse_next(&pi)) { + info->dev_count += 1; + if (pi.dev_num > info->highest_dev_num) + info->highest_dev_num = pi.dev_num; + if (pi.dev_num < info->lowest_dev_num) + info->lowest_dev_num = pi.dev_num; + } + + return 0; +} + +/** + * legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'. + * @node: name of the MTD device node + * @mtd: the MTD device information is returned here + * + * This function is similar to 'mtd_get_dev_info()' and has the same + * conventions. + */ +int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd) +{ + struct stat st; + struct mtd_info_user ui; + int fd, ret; + loff_t offs = 0; + struct proc_parse_info pi; + + if (stat(node, &st)) + return sys_errmsg("cannot open \"%s\"", node); + + if (!S_ISCHR(st.st_mode)) { + errno = EINVAL; + return errmsg("\"%s\" is not a character device", node); + } + + memset(mtd, '\0', sizeof(struct mtd_dev_info)); + mtd->major = major(st.st_rdev); + mtd->minor = minor(st.st_rdev); + + if (mtd->major != MTD_DEV_MAJOR) { + errno = EINVAL; + return errmsg("\"%s\" has major number %d, MTD devices have " + "major %d", node, mtd->major, MTD_DEV_MAJOR); + } + + mtd->dev_num = mtd->minor / 2; + + fd = open(node, O_RDWR); + if (fd == -1) + return sys_errmsg("cannot open \"%s\"", node); + + if (ioctl(fd, MEMGETINFO, &ui)) { + sys_errmsg("MEMGETINFO ioctl request failed"); + goto out_close; + } + + ret = ioctl(fd, MEMGETBADBLOCK, &offs); + if (ret == -1) { + if (errno != EOPNOTSUPP) { + sys_errmsg("MEMGETBADBLOCK ioctl failed"); + goto out_close; + } + errno = 0; + mtd->bb_allowed = 0; + } else + mtd->bb_allowed = 1; + + mtd->type = ui.type; + mtd->size = ui.size; + mtd->eb_size = ui.erasesize; + mtd->min_io_size = ui.writesize; + + if (mtd->min_io_size <= 0) { + errmsg("mtd%d (%s) has insane min. I/O unit size %d", + mtd->dev_num, node, mtd->min_io_size); + goto out_close; + } + if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { + errmsg("mtd%d (%s) has insane eraseblock size %d", + mtd->dev_num, node, mtd->eb_size); + goto out_close; + } + if (mtd->size <= 0 || mtd->size < mtd->eb_size) { + errmsg("mtd%d (%s) has insane size %lld", + mtd->dev_num, node, mtd->size); + goto out_close; + } + mtd->eb_cnt = mtd->size / mtd->eb_size; + + switch(mtd->type) { + case MTD_ABSENT: + errmsg("mtd%d (%s) is removable and is not present", + mtd->dev_num, node); + goto out_close; + case MTD_RAM: + strcpy((char *)mtd->type_str, "ram"); + break; + case MTD_ROM: + strcpy((char *)mtd->type_str, "rom"); + break; + case MTD_NORFLASH: + strcpy((char *)mtd->type_str, "nor"); + break; + case MTD_NANDFLASH: + strcpy((char *)mtd->type_str, "nand"); + break; + case MTD_DATAFLASH: + strcpy((char *)mtd->type_str, "dataflash"); + break; + case MTD_UBIVOLUME: + strcpy((char *)mtd->type_str, "ubi"); + break; + default: + goto out_close; + } + + if (ui.flags & MTD_WRITEABLE) + mtd->writable = 1; + mtd->subpage_size = mtd->min_io_size; + + close(fd); + + /* + * Unfortunately, the device name is not available via ioctl, and + * we have to parse /proc/mtd to get it. + */ + ret = proc_parse_start(&pi); + if (ret) + return -1; + + while (proc_parse_next(&pi)) { + if (pi.dev_num == mtd->dev_num) { + strcpy((char *)mtd->name, pi.name); + return 0; + } + } + + errmsg("mtd%d not found in \"%s\"", mtd->dev_num, MTD_PROC_FILE); + errno = ENOENT; + return -1; + +out_close: + close(fd); + return -1; +} + +/** + * legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'. + * @node: name of the MTD device node + * @mtd: the MTD device information is returned here + * + * This function is similar to 'mtd_get_dev_info1()' and has the same + * conventions. + */ +int legacy_get_dev_info1(int dev_num, struct mtd_dev_info *mtd) +{ + char node[sizeof(MTD_DEV_PATT) + 20]; + + sprintf(node, MTD_DEV_PATT, dev_num); + return legacy_get_dev_info(node, mtd); +} diff --git a/ubi-utils/src/libubi.c b/ubi-utils/src/libubi.c index 5c8ce9e..5b22e21 100644 --- a/ubi-utils/src/libubi.c +++ b/ubi-utils/src/libubi.c @@ -24,12 +24,12 @@ #include #include #include -#include -#include #include #include -#include #include +#include +#include +#include #include #include "libubi_int.h" #include "common.h" @@ -94,7 +94,6 @@ static int read_positive_ll(const char *file, long long *value) } if (sscanf(buf, "%lld\n", value) != 1) { - /* This must be a UBI bug */ errmsg("cannot read integer from \"%s\"\n", file); errno = EINVAL; goto out_error; @@ -227,7 +226,7 @@ static int read_major(const char *file, int *major, int *minor) /** * dev_read_int - read a positive 'int' value from an UBI device sysfs file. * @patt: file pattern to read from - * @dev_num: UBI device number + * @dev_num: UBI device number * @value: the result is stored here * * This function returns %0 in case of success and %-1 in case of failure. @@ -422,9 +421,9 @@ static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num, * dev_node2num - find UBI device number by its character device node. * @lib: UBI library descriptor * @node: UBI character device node name + * @dev_num: UBI device number is returned here * - * This function returns positive UBI device number in case of success and %-1 - * in case of failure. + * This function returns %0 in case of success and %-1 in case of failure. */ static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) { @@ -433,8 +432,7 @@ static int dev_node2num(struct libubi *lib, const char *node, int *dev_num) int i, major, minor; if (stat(node, &st)) - return sys_errmsg("cannot get information about \"%s\"", - node); + return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errno = EINVAL; @@ -749,8 +747,7 @@ int ubi_probe_node(libubi_t desc, const char *node) char file[strlen(lib->ubi_vol) + 100]; if (stat(node, &st)) - return sys_errmsg("cannot get information about \"%s\"", - node); + return sys_errmsg("cannot get information about \"%s\"", node); if (!S_ISCHR(st.st_mode)) { errmsg("\"%s\" is not a character device", node); @@ -837,9 +834,10 @@ int ubi_get_info(libubi_t desc, struct ubi_info *info) if (!dirent) break; - if (strlen(dirent->d_name) > 256) { + if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_ubi, dirent->d_name); + errno = EINVAL; goto out_close; } @@ -896,6 +894,7 @@ int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req) strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1); r.name_len = n; + desc = desc; fd = open(node, O_RDONLY); if (fd == -1) return sys_errmsg("cannot open \"%s\"", node); @@ -1036,10 +1035,8 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) memset(info, '\0', sizeof(struct ubi_dev_info)); info->dev_num = dev_num; - if (!dev_present(lib, dev_num)) { - errno = ENODEV; + if (!dev_present(lib, dev_num)) return -1; - } sysfs_ubi = opendir(lib->sysfs_ubi); if (!sysfs_ubi) @@ -1056,7 +1053,7 @@ int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info) if (!dirent) break; - if (strlen(dirent->d_name) > 256) { + if (strlen(dirent->d_name) >= 255) { errmsg("invalid entry in %s: \"%s\"", lib->sysfs_ubi, dirent->d_name); goto out_close; @@ -1120,7 +1117,8 @@ int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info) err = ubi_probe_node(desc, node); if (err != 1) { - errno = ENODEV; + if (err == 2) + errno = ENODEV; return -1; } @@ -1196,7 +1194,8 @@ int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info) err = ubi_probe_node(desc, node); if (err != 2) { - errno = ENODEV; + if (err == 1) + errno = ENODEV; return -1; } diff --git a/ubi-utils/src/mtdinfo.c b/ubi-utils/src/mtdinfo.c new file mode 100644 index 0000000..849d165 --- /dev/null +++ b/ubi-utils/src/mtdinfo.c @@ -0,0 +1,328 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "common.h" + +#define PROGRAM_VERSION "1.0" +#define PROGRAM_NAME "mtdinfo" + +/* The variables below are set by command line arguments */ +struct args { + int mtdn; + unsigned int all:1; + unsigned int ubinfo: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 to get information about\n" +"-u, --ubi-info print what would UBI layout be if it was put\n" +" on this MTD device\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 ] [-u] [-h] [-V] [--mtdn ]\n" +"\t\t[--ubi-info] [--help] [--version]\n" +"Usage 2: " PROGRAM_NAME " [-u] [-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 = "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; + char *endp; + + key = getopt_long(argc, argv, "am:uhV", 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 = strtoul(optarg, &endp, 0); + if (*endp != '\0' || endp == optarg || args.mtdn < 0) + return errmsg("bad MTD device number: \"%s\"", optarg); + + break; + + case 'h': + fprintf(stderr, "%s\n\n", doc); + fprintf(stderr, "%s\n\n", usage); + fprintf(stderr, "%s\n", optionsstr); + exit(EXIT_SUCCESS); + + case 'V': + fprintf(stderr, "%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; + } + + 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.dev_num; + return 0; +} + +static int print_dev_info(libmtd_t libmtd, const struct mtd_info *mtd_info, int mtdn) +{ + int err; + struct mtd_dev_info mtd; + struct ubigen_info ui; + + 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.dev_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) + goto out; + + if (!mtd_info->sysfs_supported) { + errmsg("cannot provide UBI info, becasue sub-page size is " + "not known"); + goto out; + } + + ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, + 0, 1); + 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); + +out: + 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->dev_count); + if (mtd_info->dev_count == 0) + return 0; + + printf("Present MTD devices: "); + for (i = mtd_info->lowest_dev_num; + i <= mtd_info->highest_dev_num; i++) { + err = mtd_get_dev_info1(libmtd, i, &mtd); + if (err == -1) { + if (errno == ENODEV) + continue; + + printf("\n"); + return sys_errmsg("libmtd failed get MTD device %d " + "information", i); + } + + if (!first) + printf(", mtd%d", i); + else { + printf("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_dev_num; + i <= mtd_info->highest_dev_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; +} diff --git a/ubi-utils/src/ubiformat.c b/ubi-utils/src/ubiformat.c index 1c201a6..b9d3f79 100644 --- a/ubi-utils/src/ubiformat.c +++ b/ubi-utils/src/ubiformat.c @@ -45,7 +45,7 @@ #include "crc32.h" #include "common.h" -#define PROGRAM_VERSION "1.2" +#define PROGRAM_VERSION "1.3" #define PROGRAM_NAME "ubiformat" /* The variables below are set by command line arguments */ @@ -55,6 +55,7 @@ struct args { unsigned int verbose:1; unsigned int override_ec:1; unsigned int novtbl:1; + unsigned int manual_subpage; int subpage_size; int vid_hdr_offs; int ubi_ver; @@ -633,9 +634,9 @@ static int format(const struct mtd_dev_info *mtd, const struct ubigen_info *ui, write_size, eb); if (errno != EIO) { - if (args.subpage_size != mtd->min_io_size) - normsg("may be %d is incorrect?", - args.subpage_size); + if (!args.subpage_size != mtd->min_io_size) + normsg("may be sub-page size is " + "incorrect?"); goto out_free; } @@ -682,59 +683,91 @@ out_free: int main(int argc, char * const argv[]) { int err, verbose; + libmtd_t libmtd; + struct mtd_info mtd_info; struct mtd_dev_info mtd; libubi_t libubi; struct ubigen_info ui; struct ubi_scan_info *si; + libmtd = libmtd_open(); + if (!libmtd) + return errmsg("MTD subsystem is not present"); + err = parse_opt(argc, argv); if (err) - return -1; + goto out_close_mtd; - err = mtd_get_dev_info(args.node, &mtd); - if (err) - return errmsg("cannot get information about \"%s\"", args.node); + err = mtd_get_info(libmtd, &mtd_info); + if (err) { + if (errno == ENODEV) + errmsg("MTD is not present"); + sys_errmsg("cannot get MTD information"); + goto out_close_mtd; + } - args.node_fd = open(args.node, O_RDWR); - if (args.node_fd == -1) - return sys_errmsg("cannot open \"%s\"", args.node); + err = mtd_get_dev_info(libmtd, args.node, &mtd); + if (err) { + sys_errmsg("cannot get information about \"%s\"", args.node); + goto out_close_mtd; + } + + if (!mtd_info.sysfs_supported) { + /* + * Linux kernels older than 2.6.30 did not support sysfs + * interface, and it is impossible to find out sub-page + * size in these kernels. This is why users should + * provide -s option. + */ + if (args.subpage_size == 0) { + warnmsg("your MTD system is old and it is impossible " + "to detect sub-page size. Use -s to get rid " + "of this warning"); + normsg("assume sub-page to be %d", mtd.subpage_size); + } else { + mtd.subpage_size = args.subpage_size; + args.manual_subpage = 1; + } + } else if (args.subpage_size && args.subpage_size != mtd.subpage_size) { + mtd.subpage_size = args.subpage_size; + args.manual_subpage = 1; + } - if (args.subpage_size == 0) - args.subpage_size = mtd.min_io_size; - else { + if (args.manual_subpage) { + /* Do some sanity check */ if (args.subpage_size > mtd.min_io_size) { errmsg("sub-page cannot be larger than min. I/O unit"); - goto out; + goto out_close; } if (mtd.min_io_size % args.subpage_size) { - errmsg("min. I/O unit size should be multiple of sub-page size"); - goto out; + errmsg("min. I/O unit size should be multiple of " + "sub-page size"); + goto out_close; } } + args.node_fd = open(args.node, O_RDWR); + if (args.node_fd == -1) { + sys_errmsg("cannot open \"%s\"", args.node); + goto out_close_mtd; + } + /* Validate VID header offset if it was specified */ if (args.vid_hdr_offs != 0) { if (args.vid_hdr_offs % 8) { errmsg("VID header offset has to be multiple of min. I/O unit size"); - goto out; + goto out_close; } if (args.vid_hdr_offs + (int)UBI_VID_HDR_SIZE > mtd.eb_size) { errmsg("bad VID header offset"); - goto out; + goto out_close; } } - /* - * Because of MTD interface limitations 'mtd_get_dev_info()' cannot get - * sub-page so we force the user to pass it via the command line. Let's - * hope the user passed us something sane. - */ - mtd.subpage_size = args.subpage_size; - if (!mtd.writable) { errmsg("mtd%d (%s) is a read-only device", mtd.dev_num, args.node); - goto out; + goto out_close; } /* Make sure this MTD device is not attached to UBI */ @@ -747,14 +780,14 @@ int main(int argc, char * const argv[]) if (!err) { errmsg("please, first detach mtd%d (%s) from ubi%d", mtd.dev_num, args.node, ubi_dev_num); - goto out; + goto out_close; } } if (!args.quiet) { normsg_cont("mtd%d (%s), size ", mtd.dev_num, mtd.type_str); ubiutils_print_bytes(mtd.size, 1); - printf(", %d eraseblocks of ", mtd.eb_size); + printf(", %d eraseblocks of ", mtd.eb_cnt); ubiutils_print_bytes(mtd.eb_size, 1); printf(", min. I/O size %d bytes\n", mtd.min_io_size); } @@ -768,7 +801,7 @@ int main(int argc, char * const argv[]) err = ubi_scan(&mtd, args.node_fd, &si, verbose); if (err) { errmsg("failed to scan mtd%d (%s)", mtd.dev_num, args.node); - goto out; + goto out_close; } if (si->good_cnt == 0) { @@ -777,7 +810,8 @@ int main(int argc, char * const argv[]) } if (si->good_cnt < 2 && (!args.novtbl || args.image)) { - errmsg("too few non-bad eraseblocks (%d) on mtd%d", si->good_cnt, mtd.dev_num); + errmsg("too few non-bad eraseblocks (%d) on mtd%d", + si->good_cnt, mtd.dev_num); goto out_free; } @@ -842,7 +876,7 @@ int main(int argc, char * const argv[]) if (!args.quiet && args.override_ec) normsg("use erase counter %lld for all eraseblocks", args.ec); - ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, args.subpage_size, + ubigen_info_init(&ui, mtd.eb_size, mtd.min_io_size, mtd.subpage_size, args.vid_hdr_offs, args.ubi_ver); if (si->vid_hdr_offs != -1 && ui.vid_hdr_offs != si->vid_hdr_offs) { @@ -883,11 +917,14 @@ int main(int argc, char * const argv[]) ubi_scan_free(si); close(args.node_fd); + libmtd_close(libmtd); return 0; out_free: ubi_scan_free(si); -out: +out_close: close(args.node_fd); +out_close_mtd: + libmtd_close(libmtd); return -1; } diff --git a/ubi-utils/src/ubinfo.c b/ubi-utils/src/ubinfo.c index 036ed6c..8c5a1a9 100644 --- a/ubi-utils/src/ubinfo.c +++ b/ubi-utils/src/ubinfo.c @@ -30,7 +30,7 @@ #include #include "common.h" -#define PROGRAM_VERSION "1.0" +#define PROGRAM_VERSION "1.1" #define PROGRAM_NAME "ubinfo" /* The variables below are set by command line arguments */ @@ -131,7 +131,7 @@ static int parse_opt(int argc, char * const argv[]) if (optind == argc - 1) args.node = argv[optind]; else if (optind < argc) - return errmsg("more then one UBI devices specified (use -h for help)"); + return errmsg("more then one UBI device specified (use -h for help)"); return 0; } @@ -216,9 +216,11 @@ static int print_dev_info(libubi_t libubi, int dev_num, int all) if (err) return sys_errmsg("cannot get information about UBI device %d", dev_num); - printf("ubi%d:\n", dev_info.dev_num); + printf("ubi%d\n", dev_info.dev_num); printf("Volumes count: %d\n", dev_info.vol_count); - printf("Logical eraseblock size: %d\n", dev_info.leb_size); + printf("Logical eraseblock size: "); + ubiutils_print_bytes(dev_info.leb_size, 0); + printf("\n"); printf("Total amount of logical eraseblocks: %d (", dev_info.total_lebs); ubiutils_print_bytes(dev_info.total_bytes, 0); @@ -232,7 +234,8 @@ static int print_dev_info(libubi_t libubi, int dev_num, int all) printf("Count of bad physical eraseblocks: %d\n", dev_info.bad_count); printf("Count of reserved physical eraseblocks: %d\n", dev_info.bad_rsvd); printf("Current maximum erase counter value: %lld\n", dev_info.max_ec); - printf("Minimum input/output unit size: %d bytes\n", dev_info.min_io_size); + printf("Minimum input/output unit size: %d %s\n", + dev_info.min_io_size, dev_info.min_io_size > 1 ? "bytes" : "byte"); printf("Character device major/minor: %d:%d\n", dev_info.major, dev_info.minor); @@ -317,6 +320,7 @@ static int print_general_info(libubi_t libubi, int all) if (errno == ENOENT) continue; + printf("\n"); return sys_errmsg("libubi failed to probe UBI device %d", i); } @@ -339,15 +343,7 @@ static int print_general_info(libubi_t libubi, int all) i <= ubi_info.highest_dev_num; i++) { if(!first) printf("\n===================================\n\n"); - err = ubi_get_dev_info1(libubi, i, &dev_info); - if (err == -1) { - if (errno == ENOENT) - continue; - - return sys_errmsg("libubi failed to probe UBI device %d", i); - } first = 0; - err = print_dev_info(libubi, i, all); if (err) return err; -- cgit v1.2.3