summaryrefslogtreecommitdiff
path: root/ubi-utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'ubi-utils/src')
-rw-r--r--ubi-utils/src/libmtd.c788
-rw-r--r--ubi-utils/src/libmtd_int.h88
-rw-r--r--ubi-utils/src/libmtd_legacy.c354
-rw-r--r--ubi-utils/src/libubi.c35
-rw-r--r--ubi-utils/src/mtdinfo.c328
-rw-r--r--ubi-utils/src/ubiformat.c103
-rw-r--r--ubi-utils/src/ubinfo.c22
7 files changed, 1567 insertions, 151 deletions
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 <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
-#include <unistd.h>
-#include <fcntl.h>
-
#include <mtd/mtd-user.h>
+
#include <libmtd.h>
+#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 <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+#include <libmtd.h>
+#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 <stdio.h>
#include <string.h>
#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
-#include <sys/ioctl.h>
#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <libubi.h>
#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 <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mtd/mtd-user.h>
+
+#include <libubigen.h>
+#include <libmtd.h>
+#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> 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 <MTD device number>] [-u] [-h] [-V] [--mtdn <MTD device number>]\n"
+"\t\t[--ubi-info] [--help] [--version]\n"
+"Usage 2: " PROGRAM_NAME " <MTD device node file 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 <libubi.h>
#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;