]> www.infradead.org Git - mtd-utils.git/commitdiff
ubi-utils: return old tools
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Wed, 23 Jan 2008 17:42:44 +0000 (19:42 +0200)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Wed, 23 Jan 2008 18:01:03 +0000 (20:01 +0200)
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
65 files changed:
ubi-utils/old-tools/Makefile [new file with mode: 0644]
ubi-utils/old-tools/README [moved from ubi-utils/README with 92% similarity]
ubi-utils/old-tools/inc/libubi.h [new file with mode: 0644]
ubi-utils/old-tools/scripts/Makefile [new file with mode: 0644]
ubi-utils/old-tools/scripts/README [new file with mode: 0644]
ubi-utils/old-tools/scripts/bin2nand2bin_test.sh [new file with mode: 0644]
ubi-utils/old-tools/scripts/f128_nand_sample.cfg [new file with mode: 0644]
ubi-utils/old-tools/scripts/f64_nor_sample.cfg [new file with mode: 0644]
ubi-utils/old-tools/scripts/inject_biterror.pl [new file with mode: 0644]
ubi-utils/old-tools/scripts/jffs2_test.sh [new file with mode: 0755]
ubi-utils/old-tools/scripts/mkdevs.pl [new file with mode: 0755]
ubi-utils/old-tools/scripts/mkpfi [new file with mode: 0644]
ubi-utils/old-tools/scripts/pdd.txt [new file with mode: 0644]
ubi-utils/old-tools/scripts/run_all.sh [new file with mode: 0755]
ubi-utils/old-tools/scripts/test.cfg [new file with mode: 0644]
ubi-utils/old-tools/scripts/ubi_test.sh [new file with mode: 0755]
ubi-utils/old-tools/scripts/ubi_tools_test.sh [new file with mode: 0755]
ubi-utils/old-tools/scripts/ubicrc32.pl [new file with mode: 0644]
ubi-utils/old-tools/scripts/unubi_test.sh [new file with mode: 0644]
ubi-utils/old-tools/src/bin2nand.c [new file with mode: 0644]
ubi-utils/old-tools/src/bootenv.c [new file with mode: 0644]
ubi-utils/old-tools/src/bootenv.h [new file with mode: 0644]
ubi-utils/old-tools/src/common.c [new file with mode: 0644]
ubi-utils/old-tools/src/common.h [new file with mode: 0644]
ubi-utils/old-tools/src/config.h [new file with mode: 0644]
ubi-utils/old-tools/src/crc32.c [new file with mode: 0644]
ubi-utils/old-tools/src/crc32.h [new file with mode: 0644]
ubi-utils/old-tools/src/eb_chain.c [new file with mode: 0644]
ubi-utils/old-tools/src/ecclayouts.h [new file with mode: 0644]
ubi-utils/old-tools/src/error.c [new file with mode: 0644]
ubi-utils/old-tools/src/error.h [new file with mode: 0644]
ubi-utils/old-tools/src/example_ubi.h [new file with mode: 0644]
ubi-utils/old-tools/src/hashmap.c [new file with mode: 0644]
ubi-utils/old-tools/src/hashmap.h [new file with mode: 0644]
ubi-utils/old-tools/src/libpfiflash.c [new file with mode: 0644]
ubi-utils/old-tools/src/libubi.c [new file with mode: 0644]
ubi-utils/old-tools/src/libubi_int.h [new file with mode: 0644]
ubi-utils/old-tools/src/libubigen.c [new file with mode: 0644]
ubi-utils/old-tools/src/libubimirror.c [new file with mode: 0644]
ubi-utils/old-tools/src/list.c [new file with mode: 0644]
ubi-utils/old-tools/src/list.h [new file with mode: 0644]
ubi-utils/old-tools/src/mkbootenv.c [new file with mode: 0644]
ubi-utils/old-tools/src/nand2bin.c [new file with mode: 0644]
ubi-utils/old-tools/src/nandcorr.c [new file with mode: 0644]
ubi-utils/old-tools/src/nandecc.c [new file with mode: 0644]
ubi-utils/old-tools/src/nandecc.h [new file with mode: 0644]
ubi-utils/old-tools/src/pddcustomize.c [new file with mode: 0644]
ubi-utils/old-tools/src/peb.c [new file with mode: 0644]
ubi-utils/old-tools/src/peb.h [new file with mode: 0644]
ubi-utils/old-tools/src/pfi.c [new file with mode: 0644]
ubi-utils/old-tools/src/pfi.h [new file with mode: 0644]
ubi-utils/old-tools/src/pfi2bin.c [new file with mode: 0644]
ubi-utils/old-tools/src/pfiflash.c [new file with mode: 0644]
ubi-utils/old-tools/src/pfiflash.h [new file with mode: 0644]
ubi-utils/old-tools/src/pfiflash_error.h [new file with mode: 0644]
ubi-utils/old-tools/src/reader.c [new file with mode: 0644]
ubi-utils/old-tools/src/reader.h [new file with mode: 0644]
ubi-utils/old-tools/src/ubigen.c [new file with mode: 0644]
ubi-utils/old-tools/src/ubigen.h [new file with mode: 0644]
ubi-utils/old-tools/src/ubimirror.c [new file with mode: 0644]
ubi-utils/old-tools/src/ubimirror.h [new file with mode: 0644]
ubi-utils/old-tools/src/ubiupdate.c [new file with mode: 0644]
ubi-utils/old-tools/src/unubi.c [new file with mode: 0644]
ubi-utils/old-tools/src/unubi_analyze.c [new file with mode: 0644]
ubi-utils/old-tools/src/unubi_analyze.h [new file with mode: 0644]

diff --git a/ubi-utils/old-tools/Makefile b/ubi-utils/old-tools/Makefile
new file mode 100644 (file)
index 0000000..d4c908b
--- /dev/null
@@ -0,0 +1,99 @@
+#
+# Makefile for ubi-utils
+#
+
+OPTFLAGS := -O2 -Wall
+KERNELHDR := ../../include
+DESTDIR := /usr/local
+SBINDIR=/usr/sbin
+MANDIR=/usr/man
+INCLUDEDIR=/usr/include
+
+CC := $(CROSS)gcc
+CFLAGS := -I./inc -I./src -I$(KERNELHDR) $(OPTFLAGS) -Werror \
+       -Wwrite-strings -W -std=gnu99 -DPACKAGE_VERSION=\"1.0\"
+
+PERLPROGS = mkpfi ubicrc32.pl
+TARGETS = pfiflash pddcustomize ubimirror \
+       bin2nand nand2bin ubigen mkbootenv unubi pfi2bin
+
+vpath   %.c ./src
+
+%: %.o
+       $(CC) $(LDFLAGS) -g -o $@ $^
+
+%.o: %.c
+       $(CC) $(CFLAGS) -g -c -o $@ $< -g -Wp,-MD,.$(shell basename $<).dep
+
+all: $(TARGETS) libubi.a
+
+IGNORE=${wildcard .*.c.dep}
+-include ${IGNORE}
+
+clean:
+       rm -rf *.o $(TARGETS) .*.c.dep libubi.a
+
+libubi.a: libubi.o
+       ar cr $@ $^
+
+ubidetach: ubidetach.o common.o libubi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubiattach: ubiattach.o common.o libubi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubinfo: ubinfo.o common.o libubi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubiupdate: ubiupdate.o common.o libubi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubimkvol: ubimkvol.o common.o libubi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubirmvol: ubirmvol.o common.o libubi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+pddcustomize: pddcustomize.o error.o libubimirror.o bootenv.o hashmap.o \
+               libubi.o crc32.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+pfiflash: pfiflash.o libpfiflash.o list.o reader.o error.o libubimirror.o \
+               bootenv.o hashmap.o pfi.o libubi.o crc32.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubimirror: ubimirror.o error.o libubimirror.o bootenv.o hashmap.o \
+               libubi.o crc32.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+nand2bin: nand2bin.o nandecc.o nandcorr.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+bin2nand: bin2nand.o error.o nandecc.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubigen: ubigen.o libubigen.o crc32.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+mkbootenv: mkbootenv.o bootenv.o hashmap.o error.o crc32.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+unubi: unubi.o crc32.o unubi_analyze.o eb_chain.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+pfi2bin: pfi2bin.o peb.o error.o list.o crc32.o libubigen.o bootenv.o \
+               hashmap.o reader.o pfi.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+ubicrc32: ubicrc32.o crc32.o
+       $(CC) $(LDFLAGS) -o $@ $^
+
+install: ${TARGETS}
+       mkdir -p ${DESTDIR}/${SBINDIR}
+       install -m0755 ${TARGETS} ${DESTDIR}/${SBINDIR}/
+       (cd perl && install ${PERLPROGS} ${DESTDIR}/${SBINDIR}/)
+
+uninstall:
+       for file in ${TARGETS} ${PERLPROGS}; do \
+               $(RM) ${DESTDIR}/${SBINDIR}/$$file; \
+       done
similarity index 92%
rename from ubi-utils/README
rename to ubi-utils/old-tools/README
index 5c7f9db77d4f239ae864a8808bbad80783911b3e..39ed0e961af6969a88840b9c8c4a6071686e9977 100644 (file)
@@ -1,3 +1,15 @@
+This directory contains old UBI tools which I cannot maintain
+because they are too messy and vague for me and the original authors
+do not seem to have much time for them. Some of the utilities are
+just not of general interest because they are oriented to specific
+tasks of the guys from IBM.
+
+But the "unubi" utility must be very useful but it fails when I
+try to feed an image to it, so it should be looked at and fixed,
+then moved to the main "main" place.
+
+Artem Bityutskiy
+
 README
 ======
 
@@ -28,9 +40,6 @@ ubigen          - tool to create binary UBI images e.g. for a
 nandimg         - tool to add OOB data to binary images intended
                   for NAND flash systems
 ubilib          - UBI library
-ubimkvol        - UBI volume creation utility
-ubirmvol        - UBI volume removal utility
-ubiupdatevol    - UBI volume update utility
 
 
 The following text is from original UBI announcement
diff --git a/ubi-utils/old-tools/inc/libubi.h b/ubi-utils/old-tools/inc/libubi.h
new file mode 100644 (file)
index 0000000..0cdb67c
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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 B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_H__
+#define __LIBUBI_H__
+
+#include <stdint.h>
+#include <mtd/ubi-user.h>
+#include <ctype.h>
+#include <mtd/ubi-header.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* UBI version libubi is made for */
+#define LIBUBI_UBI_VERSION 1
+
+/* UBI library descriptor */
+typedef void * libubi_t;
+
+/**
+ * struct ubi_attach_request - MTD device attachement request.
+ * @dev_num: number to assigne to the newly created UBI device
+ *           (%UBI_DEV_NUM_AUTO should be used to automatically assign the
+ *           number)
+ * @mtd_num: MTD device number to attach
+ * @vid_hdr_offset: VID header offset (%0 means default offset and this is what
+ *                  most of the users want)
+ */
+struct ubi_attach_request
+{
+       int dev_num;
+       int mtd_num;
+       int vid_hdr_offset;
+};
+
+/**
+ * struct ubi_mkvol_request - volume creation request.
+ * @vol_id: ID to assign to the new volume (%UBI_VOL_NUM_AUTO should be used to
+ *          automatically assign ID)
+ * @alignment: volume alignment
+ * @bytes: volume size in bytes
+ * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @name: volume name
+ */
+struct ubi_mkvol_request
+{
+       int vol_id;
+       int alignment;
+       long long bytes;
+       int vol_type;
+       const char *name;
+};
+
+/**
+ * struct ubi_info - general UBI information.
+ * @dev_count: count of UBI devices in system
+ * @lowest_dev_num: lowest UBI device number
+ * @highest_dev_num: highest UBI device number
+ * @version: UBI version
+ * @ctrl_major: major number of the UBI control device
+ * @ctrl_minor: minor number of the UBI control device
+ */
+struct ubi_info
+{
+       int dev_count;
+       int lowest_dev_num;
+       int highest_dev_num;
+       int version;
+       int ctrl_major;
+       int ctrl_minor;
+};
+
+/**
+ * struct ubi_dev_info - UBI device information.
+ * @vol_count: count of volumes on this UBI device
+ * @lowest_vol_num: lowest volume number
+ * @highest_vol_num: highest volume number
+ * @major: major number of corresponding character device
+ * @minor: minor number of corresponding character device
+ * @total_lebs: total number of logical eraseblocks on this UBI device
+ * @avail_lebs: how many logical eraseblocks are not used and available for new
+ *             volumes
+ * @total_bytes: @total_lebs * @leb_size
+ * @avail_bytes: @avail_lebs * @leb_size
+ * @bad_count: count of bad physical eraseblocks
+ * @leb_size: logical eraseblock size
+ * @max_ec: current highest erase counter value
+ * @bad_rsvd: how many physical eraseblocks of the underlying flash device are
+ *            reserved for bad eraseblocks handling
+ * @max_vol_count: maximum possible number of volumes on this UBI device
+ * @min_io_size: minimum input/output unit size of the UBI device
+ */
+struct ubi_dev_info
+{
+       int dev_num;
+       int vol_count;
+       int lowest_vol_num;
+       int highest_vol_num;
+       int major;
+       int minor;
+       int total_lebs;
+       int avail_lebs;
+       long long total_bytes;
+       long long avail_bytes;
+       int bad_count;
+       int leb_size;
+       long long max_ec;
+       int bad_rsvd;
+       int max_vol_count;
+       int min_io_size;
+};
+
+/**
+ * struct ubi_vol_info - UBI volume information.
+ * @dev_num: UBI device number the volume resides on
+ * @vol_id: ID of this volume
+ * @major: major number of corresponding volume character device
+ * @minor: minor number of corresponding volume character device
+ * @dev_major: major number of corresponding UBI device character device
+ * @dev_minor: minor number of corresponding UBI device character device
+ * @type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
+ * @alignment: alignemnt of this volume
+ * @data_bytes: how many data bytes are stored on this volume (equivalent to
+ *              @rsvd_bytes for dynamic volumes)
+ * @rsvd_bytes: how many bytes are reserved for this volume
+ * @rsvd_lebs: how many logical eraseblocks are reserved for this volume
+ * @leb_size: logical eraseblock size of this volume (may be less then
+ *           device's logical eraseblock size due to alignment)
+ * @corrupted: non-zero if the volume is corrupted
+ * @name: volume name (null-terminated)
+ */
+struct ubi_vol_info
+{
+       int dev_num;
+       int vol_id;
+       int major;
+       int minor;
+       int dev_major;
+       int dev_minor;
+       int type;
+       int alignment;
+       long long data_bytes;
+       long long rsvd_bytes;
+       int rsvd_lebs;
+       int leb_size;
+       int corrupted;
+       char name[UBI_VOL_NAME_MAX + 1];
+};
+
+/**
+ * libubi_open - open UBI library.
+ * This function initializes and opens the UBI library and returns UBI library
+ * descriptor in case of success and %NULL in case of failure.
+ */
+libubi_t libubi_open(void);
+
+/**
+ * libubi_close - close UBI library.
+ * @desc UBI library descriptor
+ */
+void libubi_close(libubi_t desc);
+
+/**
+ * ubi_get_info - get general UBI information.
+ * @desc: UBI library descriptor
+ * @info: pointer to the &struct ubi_info object to fill
+ *
+ * This function fills the passed @info object with general UBI information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_info(libubi_t desc, struct ubi_info *info);
+
+/**
+ * ubi_attach_mtd - attach MTD device to UBI.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI control character device node
+ * @req: MTD attach request.
+ *
+ * This function creates a new UBI device by attaching an MTD device as
+ * described by @req. Returns %0 in case of success and %-1 in case of failure.
+ * The newly created UBI device number is returned in @req->dev_num.
+ */
+int ubi_attach_mtd(libubi_t desc, const char *node,
+                  struct ubi_attach_request *req);
+
+/**
+ * ubi_detach_mtd - detach an MTD device.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI control character device node
+ * @mtd_num: MTD device number to detach
+ *
+ * This function detaches MTD device number @mtd_num from UBI, which means the
+ * corresponding UBI device is removed. Returns zero in case of success and %-1
+ * in case of failure.
+ */
+int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num);
+
+/**
+ * ubi_remove_dev - remove an UBI device.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI control character device node
+ * @ubi_dev: UBI device number to remove
+ *
+ * This function removes UBI device number @ubi_dev and returns zero in case of
+ * success and %-1 in case of failure.
+ */
+int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev);
+
+/**
+ * ubi_mkvol - create an UBI volume.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to create a volume at
+ * @req: UBI volume creation request
+ *
+ * This function creates a UBI volume as described at @req and returns %0 in
+ * case of success and %-1 in case of failure. The assigned volume ID is
+ * returned in @req->vol_id.
+ */
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req);
+
+/**
+ * ubi_rmvol - remove a UBI volume.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to remove a volume from
+ * @vol_id: ID of the volume to remove
+ *
+ * This function removes volume @vol_id from UBI device @node and returns %0 in
+ * case of success and %-1 in case of failure.
+ */
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id);
+
+/**
+ * ubi_rsvol - re-size UBI volume.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device owning the volume which should be
+ *        re-sized
+ * @vol_id: volume ID to re-size
+ * @bytes: new volume size in bytes
+ *
+ * This function returns %0 in case of success and %-1 in case of error.
+ */
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes);
+
+/**
+ * ubi_node_type - test UBI node type.
+ * @desc: UBI library descriptor
+ * @node: the node to test
+ *
+ * This function tests whether @node is a UBI device or volume node and returns
+ * %1 if this is an UBI device node, %2 if this is a volume node, and %-1 if
+ * this is not an UBI node or if an error occurred (the latter is indicated by
+ * a non-zero errno).
+ */
+int ubi_node_type(libubi_t desc, const char *node);
+
+/**
+ * ubi_get_dev_info - get UBI device information.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI character device to fetch information about
+ * @info: pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function fills the passed @info object with UBI device information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_dev_info(libubi_t desc, const char *node,
+                    struct ubi_dev_info *info);
+
+/**
+ * ubi_get_dev_info1 - get UBI device information.
+ * @desc: UBI library descriptor
+ * @dev_num: UBI device number to fetch information about
+ * @info: pointer to the &struct ubi_dev_info object to fill
+ *
+ * This function is identical to 'ubi_get_dev_info()' except that it accepts UBI
+ * device number, not UBI character device.
+ */
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info);
+
+/**
+ * ubi_get_vol_info - get UBI volume information.
+ * @desc: UBI library descriptor
+ * @node: name of the UBI volume character device to fetch information about
+ * @info: pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function fills the passed @info object with UBI volume information and
+ * returns %0 in case of success and %-1 in case of failure.
+ */
+int ubi_get_vol_info(libubi_t desc, const char *node,
+                    struct ubi_vol_info *info);
+
+/**
+ * ubi_get_vol_info1 - get UBI volume information.
+ * @desc: UBI library descriptor
+ * @dev_num: UBI device number
+ * @vol_id: ID of the UBI volume to fetch information about
+ * @info: pointer to the &struct ubi_vol_info object to fill
+ *
+ * This function is identical to 'ubi_get_vol_info()' except that it accepts UBI
+ * volume number, not UBI volume character device.
+ */
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+                     struct ubi_vol_info *info);
+
+/**
+ * ubi_update_start - start UBI volume update.
+ * @desc: UBI library descriptor
+ * @fd: volume character devie file descriptor
+ * @bytes: how many bytes will be written to the volume
+ *
+ * This function initiates UBI volume update and returns %0 in case of success
+ * and %-1 in case of error.
+ */
+int ubi_update_start(libubi_t desc, int fd, long long bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_H__ */
diff --git a/ubi-utils/old-tools/scripts/Makefile b/ubi-utils/old-tools/scripts/Makefile
new file mode 100644 (file)
index 0000000..b8e3c96
--- /dev/null
@@ -0,0 +1,91 @@
+#
+# Makefile
+#
+# Testcase for UBI pfi update.
+#
+# Author:      Frank Haverkamp <haverkam@de.ibm.com>
+#
+
+card           = test
+mkpfi_cfg      = test.cfg
+
+#
+# Some default values you might want to overwrite. Try it if you need
+# it and add more if needed. Note that no real sanity checking is done
+# on those values. If you do it wrong your card has no valid PDD data.
+#
+
+PATH := $(PATH):/opt/ppc/usr/bin:../perl:..
+
+dd             = dd
+sed            = sed
+bin2nand       = bin2nand
+ubigen         = ubigen
+mkpfi          = mkpfi -v
+pfi2bin                = pfi2bin -v
+
+vmlinux_bin    ?= test_vmlinux.bin
+rootfs_bin     ?= test_rootfs.bin
+spl_bin                ?= test_u-boot.bin
+pdd_txt                ?= pdd.txt
+
+flashtype      ?= nand
+pagesize       ?= 2048
+
+compl          ?= $(card)_complete
+compl_pfi      ?= $(compl).pfi
+compl_img      ?= $(compl).img
+
+compl_nand2048_mif=$(compl).$(flashtype)$(pagesize).mif
+compl_nand2048_img=$(compl).$(flashtype)$(pagesize).img
+
+all: help
+
+help:
+       @echo "Testcases for the UBI/NAND manufacturing tool-chain"
+       @echo "---------------------------------------------------------------"
+       @echo "  make mif_test          - Generate a mif (manufacturing "
+       @echo "                           image file)."
+       @echo "  make bin2nand2bin_test - Create binary with ECC information"
+       @echo "                           in the OOB area. Test ECC generation"
+       @echo "                           and correction."
+
+mif_test: $(compl_pfi) $(compl_nand2048_mif)
+
+bin2nand2bin_test:
+       chmod a+x ./bin2nand2bin_test.sh
+       chmod a+x ./inject_biterror.pl
+       ./bin2nand2bin_test.sh
+
+$(compl_pfi): $(vmlinux_bin) $(rootfs_bin) $(spl_bin)
+       $(mkpfi) -c $(mkpfi_cfg)
+
+# Binary data and out of band data (OOB)
+#
+$(compl_nand2048_mif): $(compl_img)
+       $(bin2nand) -p $(pagesize) -o $(compl_nand2048_mif) $<
+
+# Binary data only
+#
+$(compl_img): $(compl_pfi)
+       $(pfi2bin) -j $(pdd_txt) -o $@ $<
+
+#
+# Default data
+#
+# If the binary data is not available in the current working directory
+# we try to create symlinks to our test data.
+#
+$(vmlinux_bin) $(rootfs_bin) $(spl_bin):
+       @echo
+       @echo "No $@ found, will use defaults !"
+       @echo
+       @echo "OR press CTRL-C to provide your own $@" &&       \
+       sleep 1 &&                                              \
+       $(dd) if=/dev/urandom of=$@ bs=1M count=1
+
+clean:
+       $(RM) *.pfi *~ testblock* oob.bin
+
+distclean: clean
+       $(RM) *.bin *.mif *.oob *.img
diff --git a/ubi-utils/old-tools/scripts/README b/ubi-utils/old-tools/scripts/README
new file mode 100644 (file)
index 0000000..01c7453
--- /dev/null
@@ -0,0 +1,14 @@
+README
+======
+
+This procedure creates a test pfi which should be flashed to our
+system with pfiflash. The testcase should read the data back and 
+compare with the original.
+
+We should try not forget to run these tests before we release 
+a new version of UBI.
+
+Frank
+
+Please guys, clean up this and move the tests to mtd-utils/tests/
+Artem.
diff --git a/ubi-utils/old-tools/scripts/bin2nand2bin_test.sh b/ubi-utils/old-tools/scripts/bin2nand2bin_test.sh
new file mode 100644 (file)
index 0000000..51f048c
--- /dev/null
@@ -0,0 +1,216 @@
+#!/bin/sh
+#
+# Version: 1.1
+# Author:  Frank Haverkamp <haver@vnet.ibm.com>
+#
+# Testcase for nand2bin and bin2nand. Generate testdata and inject
+# biterrors. Convert data back and compare with original data.
+#
+# Conversion:
+#    bin -> bin2nand -> mif -> nand2bin -> img
+#
+
+inject_biterror=./inject_biterror.pl
+pagesize=2048
+oobsize=64
+
+# Create test data
+dd if=/dev/urandom of=testblock.bin bs=131072 count=1
+
+for layout in IBM MTD ; do
+    echo "*** Simple test with $layout layout ..."
+
+    echo -n "Convert bin to mif ... "
+    bin2nand -l$layout --pagesize=${pagesize} -o testblock.mif testblock.bin
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    echo -n "Convert mif to bin ... "
+    nand2bin -l$layout --pagesize=${pagesize} -o testblock.img testblock.mif
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    echo -n "Comparing data ... "
+    diff testblock.bin testblock.img
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+done
+
+echo "*** Test conversion without bitflips ..."
+
+echo -n "Convert bin to mif ... "
+bin2nand --pagesize=${pagesize} -o testblock.mif testblock.bin
+if [ $? -ne "0" ]; then
+    echo "failed!"
+    exit 1
+else
+    echo "ok"
+fi
+
+echo -n "Convert mif to bin ... "
+nand2bin --pagesize=${pagesize} -o testblock.img testblock.mif
+if [ $? -ne "0" ]; then
+    echo "failed!"
+    exit 1
+else
+    echo "ok"
+fi
+
+echo -n "Comparing data ... "
+diff testblock.bin testblock.img
+if [ $? -ne "0" ]; then
+    echo "failed!"
+    exit 1
+else
+    echo "ok"
+fi
+
+echo "*** Test conversion with uncorrectable ECC erors ..."
+echo -n "Inject biterror at offset $ioffs ... "
+${inject_biterror} --offset=0 --bitmask=0x81 \
+    --input=testblock.mif \
+    --output=testblock_bitflip.mif
+if [ $? -ne "0" ]; then
+    echo "failed!"
+    exit 1
+else
+    echo "ok"
+fi
+
+echo "Convert mif to bin ... "
+rm testblock.img
+nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \
+    testblock_bitflip.mif
+if [ $? -ne "0" ]; then
+    echo "failed!"
+    exit 1
+else
+    echo "ok"
+fi
+
+echo -n "Comparing data, must fail due to uncorrectable ECC ... "
+diff testblock.bin testblock.img
+if [ $? -ne "0" ]; then
+    echo "ok" # Must fail!
+else
+    echo "failed!"
+    exit 1
+fi
+
+echo "*** Test bitflips in data ... "
+for offs in `seq 0 255` ; do
+
+    cp testblock.mif testblock_bitflip.mif
+
+    for xoffs in 0 256 512 768 ; do
+       let ioffs=$offs+$xoffs
+
+       cp testblock_bitflip.mif testblock_bitflip_tmp.mif
+       echo -n "Inject biterror at offset $ioffs ... "
+       ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \
+           --input=testblock_bitflip_tmp.mif \
+           --output=testblock_bitflip.mif
+       if [ $? -ne "0" ]; then
+           echo "failed!"
+           exit 1
+       else
+           echo "ok"
+       fi
+    done
+
+    echo "Convert mif to bin ... "
+    rm testblock.img
+    nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \
+       testblock_bitflip.mif
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    echo -n "Comparing data ... "
+    diff testblock.bin testblock.img
+    if [ $? -ne "0" ]; then
+       hexdump testblock.bin > testblock.bin.txt
+       hexdump testblock.img > testblock.img.txt
+       echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare"
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    # Without correction
+    echo "Convert mif to bin ... "
+    rm testblock.img
+    nand2bin --pagesize=${pagesize} -o testblock.img \
+       testblock_bitflip.mif
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    echo -n "Comparing data must differ, correction is disabled ... "
+    diff testblock.bin testblock.img
+    if [ $? -ne "0" ]; then
+       echo "ok" # must fail
+    else
+       echo "failed!"
+       exit 1
+    fi
+done
+
+echo "*** Test bitflips in OOB data ... "
+for offs in `seq 0 $oobsize` ; do
+
+    let ioffs=$pagesize+$offs
+
+    echo -n "Inject biterror at offset $ioffs ... "
+    ${inject_biterror} --offset=${ioffs} --bitmask=0x01 \
+       --input=testblock.mif \
+       --output=testblock_bitflip.mif
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    echo "Convert mif to bin ... "
+    rm testblock.img
+    nand2bin --correct-ecc --pagesize=${pagesize} -o testblock.img \
+       testblock_bitflip.mif
+    if [ $? -ne "0" ]; then
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+
+    echo -n "Comparing data ... "
+    diff testblock.bin testblock.img
+    if [ $? -ne "0" ]; then
+       hexdump testblock.bin > testblock.bin.txt
+       hexdump testblock.img > testblock.img.txt
+       echo "Use tkdiff testblock.bin.txt testblock.img.txt to compare"
+       echo "failed!"
+       exit 1
+    else
+       echo "ok"
+    fi
+done
diff --git a/ubi-utils/old-tools/scripts/f128_nand_sample.cfg b/ubi-utils/old-tools/scripts/f128_nand_sample.cfg
new file mode 100644 (file)
index 0000000..bb62600
--- /dev/null
@@ -0,0 +1,38 @@
+[targets]
+complete=ipl,spl,bootenv,kernel,rootfs
+bootcode=spl,bootenv
+
+# Build sections
+[ipl]
+image=ipl.bin
+raw_starts=0x00000000
+raw_total_size=128kiB
+
+[spl]
+image=u-boot.bin
+ubi_ids=2,3
+ubi_size=2MiB
+ubi_type=static
+ubi_names=spl_0,spl_1
+
+[bootenv]
+bootenv_file=bootenv_complete.txt
+ubi_ids=4,5
+ubi_size=128kiB
+ubi_type=static
+ubi_names=bootenv_0,bootenv_1
+
+[kernel]
+image=vmlinux.bin
+ubi_ids=6,7
+ubi_size=6MiB
+ubi_type=static
+ubi_names=kernel_0,kernel_1
+
+[rootfs]
+image=rootfs.bin
+ubi_ids=8,9
+ubi_alignment=2kiB
+ubi_size=16MiB
+ubi_type=dynamic
+ubi_names=rootfs_0,rootfs_1
diff --git a/ubi-utils/old-tools/scripts/f64_nor_sample.cfg b/ubi-utils/old-tools/scripts/f64_nor_sample.cfg
new file mode 100644 (file)
index 0000000..889d4c2
--- /dev/null
@@ -0,0 +1,39 @@
+[targets]
+complete=ipl,spl,bootenv,kernel,rootfs
+bootcode=spl,bootenv
+rootfs=rootfs
+
+# Build sections
+[ipl]
+image=ipl.bin
+raw_starts=0x02FE0000, 0x03FE0000
+raw_total_size=128kiB
+
+[spl]
+image=u-boot.bin
+ubi_ids=2,3
+ubi_size=2MiB
+ubi_type=static
+ubi_names=spl_0,spl_1
+
+[bootenv]
+bootenv_file=bootenv_complete.txt
+ubi_ids=4,5
+ubi_size=128kiB
+ubi_type=static
+ubi_names=bootenv_0,bootenv_1
+
+[kernel]
+image=vmlinux.bin
+ubi_ids=6,7
+ubi_size=6MiB
+ubi_type=static
+ubi_names=kernel_0,kernel_1
+
+[rootfs]
+image=rootfs.bin
+ubi_ids=8,9
+ubi_alignment=2kiB
+ubi_size=16128kiB
+ubi_type=dynamic
+ubi_names=rootfs_0,rootfs_1
diff --git a/ubi-utils/old-tools/scripts/inject_biterror.pl b/ubi-utils/old-tools/scripts/inject_biterror.pl
new file mode 100644 (file)
index 0000000..b4a862a
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/perl -w
+#
+# 2007 Frank Haverkamp <haver@vnet.ibm.com>
+#
+# Program for bit-error injection. I am sure that perl experts do it
+# in 1 line. Please let me know how it is done right ;-).
+#
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+
+my $i;
+my $help;
+my $result;
+my $offset = 0;
+my $bitmask = 0x01;
+my $in = "input.mif";
+my $out = "output.mif";
+
+$result = GetOptions ("offset=i"  => \$offset,    # numeric
+                     "bitmask=o" => \$bitmask,   # numeric
+                     "input=s"   => \$in,        # string
+                     "output=s"  => \$out,       # string
+                     "help|?"    => \$help) or pod2usage(2);
+
+pod2usage(1) if $help;
+
+my $buf;
+
+open(my $in_fh, "<", $in)
+  or die "Cannot open file $in: $!";
+binmode $in_fh;
+
+open(my $out_fh, ">", $out) or
+  die "Cannot open file $out: $!";
+binmode $out_fh;
+
+$i = 0;
+while (sysread($in_fh, $buf, 1)) {
+
+       $buf = pack('C', unpack('C', $buf) ^ $bitmask) if ($i == $offset);
+       syswrite($out_fh, $buf, 1) or
+         die "Cannot write to offset $offset: $!";
+       $i++;
+}
+
+close $in_fh;
+close $out_fh;
+
+__END__
+
+=head1 NAME
+
+inject_biterrors.pl
+
+=head1 SYNOPSIS
+
+inject_biterror.pl [options]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--offset>=I<offset>
+
+Byte-offset where bit-error should be injected.
+
+=item B<--bitmask>=I<bitmask>
+
+Bit-mask where to inject errors in the byte.
+
+=item B<--input>=I<input-file>
+
+Input file.
+
+=item B<--output>=I<output-file>
+
+Output file.
+
+=back
+
+=head1 DESCRIPTION
+
+B<inject_biterrors.pl> will read the given input file and inject
+biterrors at the I<offset> specified. The location of the biterrors
+are defined by the I<bitmask> parameter.
+
+=cut
diff --git a/ubi-utils/old-tools/scripts/jffs2_test.sh b/ubi-utils/old-tools/scripts/jffs2_test.sh
new file mode 100755 (executable)
index 0000000..0cc9f0c
--- /dev/null
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Testcase for JFFS2 verification. We do not want to see any
+# kernel errors occuring when this is executed.
+#
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed ()
+{
+    echo "FAILED"
+}
+
+passed ()
+{
+    echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+    echo "SUCCESS"
+    exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+    echo "FAILED"
+    exit 1
+}
+
+echo "***********************************************************************"
+echo "*        jffs2 testing ...                                            *"
+echo "***********************************************************************"
+
+ulimit -c unlimited
+
+for i in `seq 5000`; do
+    echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+    dd if=/dev/urandom of=test.bin bs=$i count=1;
+    if [ $? -ne "0" ] ; then
+        exit_failure
+    fi
+    passed
+
+    echo "Copy to different file ... "
+    dd if=test.bin of=new.bin bs=$i count=1;
+    if [ $? -ne "0" ] ; then
+        exit_failure
+    fi
+    passed
+
+    echo "Comparing files ... "
+    cmp test.bin new.bin
+    dd if=test.bin of=new.bin bs=$i count=1;
+    if [ $? -ne "0" ] ; then
+        exit_failure
+    fi
+    passed
+done
+
+for i in `seq 5000`; do
+    echo "Testing $i byte (dd if=/dev/urandom of=foo bs=$i count=1) ... "
+    dd if=/dev/urandom of=foo bs=$i count=1;
+    if [ $? -ne "0" ] ; then
+        exit_failure
+    fi
+    passed
+done
+
+for i in `seq 5000`; do 
+    echo "Testing $i byte (dd if=/dev/zero of=foo bs=$i count=1) ... "
+    dd if=/dev/zero of=foo bs=$i count=1;
+    if [ $? -ne "0" ] ; then
+        exit_failure
+    fi
+    passed
+done
+
+echo "***********************************************************************"
+echo "*               Congratulations, no errors found!                     *"
+echo "*              Have fun with your cool JFFS2 using system!            *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/old-tools/scripts/mkdevs.pl b/ubi-utils/old-tools/scripts/mkdevs.pl
new file mode 100755 (executable)
index 0000000..f0fd464
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl -w
+
+#
+# Author: Artem B. Bityutskiy <dedekind@oktetlabs.ru>
+#
+# A small scrip which creates UBI device nodes in /dev. UBI allocates
+# major number dynamically, so the script looks at /proc/devices to find
+# out UBI's major number.
+#
+
+
+my $proc = '/proc/devices';
+my $regexp = '(\d+) (ubi\d+)$';
+
+
+open FILE, "<", $proc or die "Cannot open $proc file: $!\n";
+my @file = <FILE>;
+close FILE;
+
+foreach (@file) {
+       next if not m/$regexp/g;
+       print "found $2\n";
+
+       system("rm -rf /dev/$2");
+       system("mknod /dev/$2 c $1 0");
+
+       for (my $i = 0; $i < 128; $i += 1) {
+               system("rm -rf /dev/$2_$i");
+               my $j = $i + 1;
+               system("mknod /dev/$2_$i c $1 $j");
+       }
+}
diff --git a/ubi-utils/old-tools/scripts/mkpfi b/ubi-utils/old-tools/scripts/mkpfi
new file mode 100644 (file)
index 0000000..2cce587
--- /dev/null
@@ -0,0 +1,723 @@
+#!/usr/bin/perl
+#
+# Copyright (c) International Business Machines Corp., 2006
+#
+# 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.
+#
+
+#
+# mkpfi
+#
+# This perl program is assembles PFI files from a config file.
+#
+# Author: Oliver Lohmann (oliloh@de.ibm.com)
+#
+use warnings;
+use strict;
+use lib "/usr/lib/perl5"; # Please change this path as you need it, or
+                         # make a proposal how this could be done
+                         # nicer.
+use Getopt::Long;
+use Pod::Usage;
+use Config::IniFiles;
+use File::Temp;
+
+# ----------------------------------------------------------------------------
+# Versions
+our $version : unique = "0.1";
+our $pfi_version : unique = "0x1";
+
+# ----------------------------------------------------------------------------
+# Globals
+my $verbose = 0;
+my $cfg;
+
+my %opts = ();
+my %files = (config => "");
+my @tmp_files;
+
+my %tools = (ubicrc32 => "ubicrc32");
+
+# ----------------------------------------------------------------------------
+# Processing the input sections
+#
+# The idea is to combine each section entry with a function
+# in order to allow some kind of preprocessing for the values
+# before they are written into the PFI file.
+# This is especially useful to be more verbose and
+# user-friendly in the layout file.
+#
+# All key-function hashes are applied after the general
+# validation of the configuration file.
+# If any mandatory key is missing in a section the user
+# will be informed and the PFI creation process is aborted.
+#
+# Default keys will be checked for their presence inside the config
+# file. If they are missing, they will be generated with appr. values.
+
+# Mandatory keys for UBI volumes.
+my %ubi_keys = ("ubi_ids"       => \&check_id_list,
+               "ubi_size"      => \&replace_num,
+               "ubi_type"      => \&replace_type,
+               "ubi_names"     => \&remove_spaces,
+               "ubi_alignment" => \&replace_num);
+
+# Mandatory keys for RAW sections.
+my %raw_keys = ("raw_starts"     => \&expand_starts,
+               "raw_total_size" => \&replace_num);
+
+# Common default keys for documentation and control purposes.
+my %common_keys = ("flags" => \&replace_num,
+                  "label" => \&do_nothing);
+
+# Define any defaults here. Values which maintained in this default
+# region need not to be specified by the user explicitly.
+my %def_ubi_keys      = ("ubi_alignment" => [\&set_default, "0x1"]);
+my %def_raw_keys      = ();
+my %def_common_keys   = ("flags"        => [\&set_default, "0x0"],
+                        "label"         => [\&generate_label, ""]);
+
+# ----------------------------------------------------------------------------
+# Input keys, actually the path to the input data.
+
+my %input_keys = ("image" => \&do_nothing);
+
+# Placeholder keys allow the replacement via a special
+# purpose function. E.g. the bootenv_file key will be used
+# to generate bootenv binary data from an text file and
+# replace the bootenv_file key with an image key to handle it
+# in the same way in the further creation process.
+my %input_placeholder_keys = ("bootenv_file" => \&create_bootenv_image);
+
+# ----------------------------------------------------------------------------
+# Helper
+
+# @brief Get current time string.
+sub get_date {
+       my $tmp = scalar localtime;
+       $tmp =~ s/ /_/g;
+       return $tmp;
+}
+
+# @brief Print an info message to stdout.
+sub INFO($) {
+       my $str = shift;
+
+       if (!$verbose) {
+               return;
+       }
+
+       print STDOUT $str;
+}
+
+# @brief Print an error message to stderr.
+sub ERR($) {
+       my $str = shift;
+       print STDERR $str;
+}
+
+# @brief Print a warning message to stderr.
+sub WARN($) {
+       my $str = shift;
+       print STDERR $str;
+}
+
+sub parse_command_line($) {
+       my $opt = shift;
+       my $result = GetOptions( "help"      => \$$opt{'help'},
+                                "man"       => \$$opt{'man'},
+                                "config=s"  => \$$opt{'config'},
+                                "verbose"   => \$$opt{'verbose'},
+                              ) or pod2usage(2);
+       pod2usage(1) if defined ($$opt{help});
+       pod2usage(-verbose => 2) if defined ($$opt{man});
+
+       $verbose = $$opt{verbose} if defined $$opt{verbose};
+
+       if (!defined $$opt{config}) {
+               ERR("[ ERROR: No config file specified. Aborting...\n");
+               exit 1;
+       }
+
+}
+
+# @brief Check if all needed tools are in PATH.
+sub check_tools {
+       my $err = 0;
+       my $key;
+
+       foreach $key (keys %tools) {
+               if (`which $tools{$key}` eq "") {
+                       ERR("\n") if ($err == 0);
+                       ERR("! Please add the tool \'$tools{$key}\' " .
+                               "to your path!\n");
+                       $err = 1;
+               }
+       }
+       die "[ ERROR: Did not find all needed tools!\n" if $err;
+}
+
+sub open_cfg_file($) {
+       my $fname = shift;
+       my $res = new Config::IniFiles( -file => $fname );
+
+       die "[ ERROR: Cannot load your config file!\n" if (!defined $res);
+       return $res;
+}
+
+sub set_default($$$$) {
+       my ($cfg, $section, $parameter, $def_value) = @_;
+       $cfg->newval($section, $parameter, $def_value);
+       return;
+}
+
+sub generate_label($$$$) {
+       my ($cfg, $section, $parameter, $def_value) = @_;
+       my $new_label = $def_value . $section;
+       $new_label .= "_" . get_date;
+       $cfg->newval($section, $parameter, $new_label);
+       return;
+}
+
+# @brief   Converts any num to a unified hex string, i.e the resulting value
+#         always starts with "0x" and is aligned to 8 hexdigits.
+# @return  Returns 0 on success, otherwise an error occured.
+#
+sub any_num_to_hex($$) {
+       my $val = shift;
+       my $res = shift;
+
+       # M(iB)
+       if ($val =~ m/([0-9]+)[Mm][i]?[Bb]?/g) {
+               $$res = sprintf("0x%08x", $1 * 1024 * 1024);
+       }
+       # k(iB)
+       elsif ($val =~ m/([0-9]+)[kK][i]?[Bb]?/g) {
+               $$res = sprintf("0x%08x", $1 * 1024);
+       }
+       # hex
+       elsif ($val =~ m/0x?([0-9a-fA-F]+)/g) {
+               $$res = sprintf("0x%08x", hex $1);
+       }
+       # decimal
+       elsif ($val =~ m/^([0-9]+)$/g) {
+               $$res = sprintf("0x%08x", $1);
+       }
+       else {
+               $$res = "";
+               return -1;
+       }
+
+       return 0;
+}
+
+sub remove_spaces($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       my ($start, @starts, @new_starts);
+       my $val = $cfg->val($section, $parameter);
+       my $res;
+
+       $val =~ s/ //g; # spaces
+       $cfg->newval($section, $parameter, $val);
+}
+
+sub expand_starts($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       my ($start, @starts, @new_starts);
+       my $val = $cfg->val($section, $parameter);
+       my $res;
+
+       $val =~ s/ //g; # spaces
+       @starts = split(/,/, $val);
+
+       foreach $start (@starts) {
+               if (any_num_to_hex($start, \$res) != 0) {
+                       ERR("[ ERROR: [$section]\n");
+                       ERR("[        Expecting a list of numeric " .
+                           "values for parameter: $parameter\n");
+                       exit 1;
+               }
+               push (@new_starts, $res);
+       }
+       $res = join(',', @starts);
+
+       $cfg->newval($section, $parameter, $res);
+}
+
+sub check_id_list($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       my $val = $cfg->val($section, $parameter);
+       my $res;
+
+       if (!($val =~ m/^[0-9]+[,0-9]*/)) {
+               ERR("[ ERROR: Syntax error in 'ubi_ids' in " .
+                   "section '$section': $val\n");
+                       ERR("[ Aborting... ");
+                       exit 1;
+       }
+}
+
+sub replace_type($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       my $val = $cfg->val($section, $parameter);
+       my $res;
+
+       $res = lc($val);
+       grep {$res eq $_} ('static', 'dynamic')
+           or die "[ ERROR: Unknown UBI Volume Type in " .
+           "section '$section': $val\n";
+
+       $cfg->newval($section, $parameter, $res);
+}
+
+
+sub replace_num($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       my $val = $cfg->val($section, $parameter);
+       my $res = "";
+
+       if (any_num_to_hex($val, \$res) != 0) {
+               ERR("[ ERROR: [$section]\n");
+               ERR("[        Expecting a numeric value " .
+                   "for parameter: $parameter\n");
+               exit 1;
+       }
+       $cfg->newval($section, $parameter, $res);
+}
+
+sub do_nothing($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       return;
+}
+
+sub bootenv_sanity_check($) {
+       my $env = shift;        # hash array containing bootenv
+       my %pdd = ();
+
+       defined($$env{'pdd'}) or return "'pdd' not defined";
+       foreach (split /,/, $$env{'pdd'}) {
+               defined($$env{$_}) or return "undefined '$_' in pdd";
+               $pdd{$_} = 1;
+       }
+
+       defined $$env{'pdd_preserve'} or
+               return "";
+       foreach (split /,/, $$env{'pdd_preserve'}) {
+               defined($pdd{$_})
+                       or return "pdd_preserve field '$_' not in pdd";
+       }
+       return "";
+}
+
+sub create_bootenv_image($$$) {
+       my ($cfg, $section, $parameter) = @_;
+       my $txt_fn = $cfg->val($section, "bootenv_file");
+       my $in;
+
+       my %value = ();
+       my @key = ();
+
+       open $in, "<", $txt_fn
+               or die "[ ERROR: can't open bootenv file '$txt_fn'.\n";
+       while (<$in>) {
+               next if (/^\s*(\#.*)?$/); # Skip comments/whitespace.
+
+               if (/^(\S+?)\+\=(.*)$/) {
+                       defined($value{$1}) or
+                               die "$txt_fn:$.: error: appending to" .
+                                       " non-existent '$1'\n";
+                       $value{$1} .= $2;
+               } elsif (/^(\S+?)\=(.*)$/) {
+                       not defined($value{$1}) or
+                               die "$txt_fn:$.: error: trying to" .
+                                       " redefine '$1'\n";
+                       push @key, $1;
+                       $value{$1} = $2;
+               } else {
+                       die "$txt_fn:$.: error: unrecognized syntax\n";
+               }
+       }
+       close $in;
+
+       $_ = &bootenv_sanity_check(\%value)
+               and die "$txt_fn: error: $_\n";
+
+       my $tmp_file = new File::Temp();
+       push (@tmp_files, $tmp_file);
+
+       foreach (@key) {
+               print $tmp_file "$_=", $value{$_}, "\0";
+       }
+       close $tmp_file;
+
+       $cfg->newval($section, "image", $tmp_file-> filename);
+}
+
+sub process_keys($$$) {
+       my ($cfg, $section, $keys) = @_;
+       my @parameters = $cfg->Parameters($section);
+       my $i;
+
+       for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) {
+               if (defined($$keys{$parameters[$i]})) {
+                       $$keys{$parameters[$i]}->($cfg, $section,
+                                       $parameters[$i]);
+               }
+       }
+
+}
+
+sub is_in_keylist($$) {
+       my ($key, $keys) = @_;
+       my $i;
+
+       for ($i = 0; $i < scalar(@$keys); $i++) {
+               if ($$keys[$i] eq $key) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+sub check_default_keys($$$) {
+       my ($cfg, $section, $keys) = @_;
+       my @parameters = $cfg->Parameters($section);
+       my $key;
+
+       foreach $key (keys %$keys) {
+               if (!is_in_keylist($key, \@parameters)) {
+                       $$keys{$key}[0]->
+                               ($cfg, $section, $key, $$keys{$key}[1]);
+               }
+       }
+
+}
+
+
+
+sub check_keys($$$) {
+       my ($cfg, $section, $keys) = @_;
+       my @parameters = $cfg->Parameters($section);
+       my ($i, $key, $err);
+
+       $err = 0;
+       for ($i = 0 ; $i < scalar(@$keys) ; $i++ ) {
+               if (!is_in_keylist($$keys[$i], \@parameters)) {
+                       ERR("[ ERROR: [$section]\n") if $err == 0;
+                       $err = 1;
+                       ERR("[        Missing key '$$keys[$i]'\n");
+               }
+       }
+
+       if ($err) {
+               ERR("[ Aborting...\n");
+               exit 1;
+       }
+}
+
+sub push_pfi_data($$$$$) {
+       my ($cfg, $section, $pfi_infos, $keys, $mode) = @_;
+       my ($tmp, $i, $hdr);
+
+       my %pfi_info = ();
+       $pfi_info{'mode'} = $mode;
+       $pfi_info{'image'} = $cfg->val($section, "image");
+
+       # Build the PFI header
+       $hdr  = sprintf("PFI!\n");
+       $hdr .= sprintf("version=0x%08x\n", hex $pfi_version);
+       $hdr .= sprintf("mode=$mode\n");
+
+       # calculate the size of the binary data part
+       $tmp = -s $cfg->val($section, "image");
+       if (!defined $tmp) {
+               ERR("[ ERROR: [$section]\n");
+               ERR("[        Missing input image: "
+                               . $cfg->val($section, "image") . "\n");
+               exit 1;
+       }
+       # Check for the image to fit into the given space
+       my $quota;
+       if ($mode eq 'raw') {
+               $quota = oct $cfg->val($section, "raw_total_size");
+       } elsif ($mode eq 'ubi') {
+               $quota = oct $cfg->val($section, "ubi_size");
+       }
+       $tmp <= $quota
+               or die "[ERROR: image file too big: " .
+               $cfg->val($section, "image") . "\n";
+       $pfi_info{'size'} = $tmp;
+
+       $hdr .= sprintf("size=0x%08x\n", $tmp);
+
+       my $img_file = $cfg->val($section, "image");
+       my $crc32 = `$tools{'ubicrc32'} $img_file 2>&1`;
+       if (any_num_to_hex($crc32, \$tmp) != 0) {
+               die "[ ERROR: $tools{'ubicrc32'} returned with errors";
+       }
+       $hdr .= sprintf("crc=$tmp\n");
+
+
+       # Process all remaining keys
+       for ($i = 0; $i < scalar (@$keys); $i++) {
+               if ($$keys[$i] eq "image") { # special case image input file
+                       if (! -e ($tmp = $cfg->val($section, "image"))) {
+                               ERR("[ ERROR: [$section]\n");
+                               ERR("[        Cannot find input file $tmp\n");
+                               exit 1;
+                       }
+                       next;
+               }
+               $hdr .= sprintf("%s=%s\n", $$keys[$i],
+                               $cfg->val($section, $$keys[$i]));
+       }
+
+       $hdr .= sprintf("\n"); # end marker for PFI-header
+
+       $pfi_info{'header'} = $hdr;
+
+       # store in the header list
+       push @$pfi_infos, \%pfi_info;
+}
+
+sub process_section($$$$$$) {
+       my ($cfg, $section, $pfi_infos, $custom_keys,
+                       $def_custom_keys, $mode) = @_;
+       my @keys = (keys %common_keys, keys %$custom_keys);
+       my @complete_keys = (@keys, keys %input_keys);
+
+       # set defaults if necessary
+       check_default_keys($cfg, $section, $def_custom_keys);
+       check_default_keys($cfg, $section, \%def_common_keys);
+
+       # check for placeholders...
+       process_keys($cfg, $section, \%input_placeholder_keys);
+
+       # VALIDATE layout.cfg entries
+       check_keys($cfg, $section, \@complete_keys);
+
+       # execute linked functions (if any)
+       process_keys($cfg, $section, \%common_keys);
+       process_keys($cfg, $section, $custom_keys);
+
+       push_pfi_data($cfg, $section, $pfi_infos, \@keys, $mode);
+}
+
+sub get_section_info($$) {
+       my ($cfg, $section) = @_;
+       my @parameters = $cfg->Parameters($section);
+       my ($ubi, $raw, $i, @res);
+
+       $ubi = $raw = 0;
+       for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) {
+               if ($parameters[$i] =~ m/ubi_/gi) {
+                       $ubi = 1;
+                       @res = (\%ubi_keys, \%def_ubi_keys, "ubi");
+               }
+               if ($parameters[$i] =~ m/raw_/gi) {
+                       $raw = 1;
+                       @res = (\%raw_keys, \%def_raw_keys, "raw");
+               }
+       }
+
+       if (($ubi + $raw) != 1) { # double definition in section
+               ERR("[ ERROR: Layout error in section '$section'\n");
+               exit 1;
+       }
+
+       return @res;
+}
+
+sub mk_target_list($$) {
+       my $val = shift;
+       my $tmp = shift;
+       my $complete = 0;
+
+       if ($val =~ m/\((.*)\)/g) {
+               $val = $1;
+               $complete = 1;
+       }
+       $val =~ s/ //g; # spaces
+
+       @$tmp = split(/,/, $val);
+
+       return $complete;
+}
+
+sub copy_bytes($$$) {
+       my ($in, $out, $to_copy) = @_;
+
+       while ($to_copy) {
+               my $buf;
+               my $bufsize = 1024*1024;
+
+               $bufsize < $to_copy or $bufsize = $to_copy;
+               read($in, $buf, $bufsize) == $bufsize
+                       or die "[ ERROR: Image file shrunk during operation\n";
+               print $out $buf;
+               $to_copy -= $bufsize;
+       }
+}
+
+sub write_target($$) {
+       my ($pfi_infos, $target) = @_;
+       my ($pfi_info);
+
+       INFO("[ Writting target pfi file: '$target.pfi'...\n");
+       if (-e "$target.pfi") {
+               WARN("! Replaced old pfi...\n");
+               `rm -f $target.pfi`;
+       }
+       open(FILE, ">", "$target.pfi")
+               or die "[ ERROR: Cannot create output file: $target.pfi\n";
+       binmode(FILE);
+
+       # @FIXME sort by mode (first raw, then ubi)
+       # Currently this ordering is based on a string comparism. :-)
+       @$pfi_infos = sort {(lc $$a{'mode'}) cmp (lc $$b{'mode'})} @$pfi_infos;
+
+       # Print all headers first
+       foreach $pfi_info (@$pfi_infos) {
+               print FILE $$pfi_info{'header'};
+
+       }
+       # Print the linked data sections
+       print FILE "DATA\n";
+       foreach $pfi_info (@$pfi_infos) {
+               open(IMAGE, "<", $$pfi_info{'image'})
+                               or die "[ ERROR: Cannot open input image: " .
+                               "$$pfi_info{'image'}" . "\n";
+               binmode(IMAGE);
+               &copy_bytes(\*IMAGE, \*FILE, $$pfi_info{'size'});
+               close(IMAGE) or die "[ ERROR: Cannot close input image: " .
+                               "$$pfi_info{'image'}" . "\n";
+       }
+       close(FILE) or die "[ ERROR: Cannot close output file: $target.pfi\n";
+}
+
+sub process_config($) {
+       my $cfg = shift;
+       my @sections = $cfg->Sections;
+       my ($i, $j, $keylist, $def_keylist, $mode, $tmp,
+                       @tlist, $complete,@pfi_infos);
+
+       my @parameters = $cfg->Parameters("targets") or
+               die "[ ERROR: Config file has no 'targets' section!\n";
+
+       for ($i = 0 ; $i < scalar(@parameters) ; $i++ ) {
+               INFO("[ Processing target '$parameters[$i]'...\n");
+               @pfi_infos = ();
+
+               # get a list of subtargets
+               $complete = mk_target_list($cfg->val("targets",
+                                       $parameters[$i]), \@tlist);
+               # build all subtargets
+               for ($j = 0 ; $j < scalar(@tlist) ; $j++ ) {
+                       ($keylist, $def_keylist, $mode)
+                               = get_section_info($cfg, $tlist[$j]);
+                       process_section($cfg, $tlist[$j],
+                                       \@pfi_infos,
+                                       $keylist, $def_keylist, $mode);
+               }
+
+               write_target(\@pfi_infos, $parameters[$i]);
+       }
+
+       INFO("[ Success.\n");
+
+
+}
+
+sub clear_files() {
+       # @FIXME:
+       # Works implicitly and Fedora seems to have removed
+       # the cleanup call. Thus for now, inactive.
+       # File::Temp::cleanup();
+}
+
+require 5.008_000;             # Tested with version 5.8.0.
+select STDOUT; $| = 1;         # make STDOUT output unbuffered
+select STDERR; $| = 1;         # make STDERR output unbuffered
+
+parse_command_line(\%opts);
+check_tools;
+$cfg = open_cfg_file($opts{config});
+process_config($cfg);
+clear_files;
+
+__END__
+
+
+=head1 NAME
+
+mkpfi - Using GetOpt::Long, Pod::Usage, Config::IniFiles
+
+
+=head1 SYNOPSIS
+
+mkpfi  [OPTIONS ...]
+
+
+       OPTION
+
+       [--config] [--help] [--man]
+
+
+=head1 ABSTRACT
+
+Perl script for generating pdd pfi files from given config files.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--help>
+
+Print out brief help message.
+
+=item B<--usage>
+
+Print usage.
+
+=item B<--config>
+
+Config input file.
+
+=item B<--man>
+
+Print manual page, same as 'perldoc mkpfi'.
+
+=item B<--verbose>
+
+Be verbose!
+
+=back
+
+=head1 BUGS
+
+Report via MTD mailing list
+
+
+=head1 SEE ALSO
+
+http://www.linux-mtd.infradead.org/
+
+
+=head1 AUTHOR
+
+Oliver Lohmann (oliloh@de.ibm.com)
+
+=cut
diff --git a/ubi-utils/old-tools/scripts/pdd.txt b/ubi-utils/old-tools/scripts/pdd.txt
new file mode 100644 (file)
index 0000000..a3ad915
--- /dev/null
@@ -0,0 +1,16 @@
+pdd=flash_type,flash_size,flash_eraseblock_size,flash_page_size,card_serialnumber,card_type,ethaddr,eth1addr,eth0,eth1,total,card_hardwarelevel
+pdd_preserve=ethaddr,eth1addr,card_serialnumber
+# To be personalized
+ethaddr=00:04:34:56:78:9A
+eth1addr=00:04:34:56:78:9B
+card_serialnumber=SN0
+# Static for this card type
+total=102M
+card_type=nand_driven_testcard
+card_hardwarelevel=0
+eth0=bcm5222,eth0,0
+eth1=bcm5222,eth0,1
+flash_type=NAND
+flash_size=0x08000000
+flash_eraseblock_size=0x00020000
+flash_page_size=0x00000800
diff --git a/ubi-utils/old-tools/scripts/run_all.sh b/ubi-utils/old-tools/scripts/run_all.sh
new file mode 100755 (executable)
index 0000000..040bcbd
--- /dev/null
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+exit_success ()
+{
+       echo "UBI Utils Test Scripts - SUCCESS!"
+       exit 0
+}
+
+exit_failure ()
+{
+       echo $1
+       echo "UBI Utils Test Scripts - FAILED!"
+       exit 1
+}
+
+echo UBI Utils Test Scripts
+
+devno=$1
+logfile=temp-test-log.txt
+
+if test -z "$devno";
+then
+       echo "Usage is $0 <mtd device number>"
+       exit 1
+fi
+
+cwd=`pwd` || exit_failure "pwd failed"
+
+log="${cwd}/${logfile}"
+
+PATH=$PATH:$cwd:..
+
+cat /dev/null > $log || exit_failure "Failed to create $log"
+
+echo "Setting up for jffs2_test.sh" | tee -a $log
+
+avail=`cat /sys/class/ubi/ubi${devno}/avail_eraseblocks`
+size=`cat /sys/class/ubi/ubi${devno}/eraseblock_size`
+
+bytes=`expr $avail \* $size`
+
+ubimkvol -d$devno -s$bytes -n0 -Njtstvol || exit_failure "ubimkvol failed"
+
+mkdir -p /mnt/test_file_system || exit_failure "mkdir failed"
+
+mtd=`cat /proc/mtd | grep jtstvol | cut -d: -f1`
+
+if test -z "$mtd";
+then
+       exit_failure "mtd device not found"
+fi
+
+mount -t jffs2 $mtd /mnt/test_file_system || exit_failure "mount failed"
+
+cd /mnt/test_file_system || exit_failure "cd failed"
+
+echo Running jffs2_test.sh | tee -a $log
+
+jffs2_test.sh >> $log 2>&1 || exit_failure "jffs2_test.sh failed"
+
+rm -f *
+
+cd $cwd || exit_failure "cd failed"
+
+umount /mnt/test_file_system || exit_failure "umount failed"
+
+ubirmvol -d$devno -n0 || exit_failure "ubirmvol failed"
+
+major=`cat /sys/class/ubi/ubi${devno}/dev | cut -d: -f1`
+
+for minor in `seq 0 32`; do
+       if test ! -e /dev/ubi${devno}_$minor ;
+       then
+               mknod /dev/ubi${devno}_$minor c $major $(($minor + 1))
+       fi
+done
+
+rm -f testdata.bin readdata.bin
+
+echo Running ubi_jffs2_test.sh | tee -a $log
+
+ubi_jffs2_test.sh >> $log 2>&1 || exit_failure "ubi_jffs2_test.sh failed"
+
+echo Running ubi_test.sh | tee -a $log
+
+ubi_test.sh >> $log 2>&1 || exit_failure "ubi_test.sh failed"
+
+for minor in `seq 0 32`; do
+       if test -e /sys/class/ubi/ubi${devno}/$minor;
+       then
+               ubirmvol -d$devno -n$minor || exit_failure "ubirmvol failed"
+       fi
+done
+
+echo Running ubi_tools_test.sh | tee -a $log
+
+ubi_tools_test.sh >> $log 2>&1 || exit_failure "ubi_tools_test failed"
+
+rm -f $log
+
+exit_success
diff --git a/ubi-utils/old-tools/scripts/test.cfg b/ubi-utils/old-tools/scripts/test.cfg
new file mode 100644 (file)
index 0000000..0b5ec48
--- /dev/null
@@ -0,0 +1,23 @@
+[targets]
+test_complete=spl,kernel,rootfs
+
+[spl]
+image=test_u-boot.bin
+ubi_ids=10,11
+ubi_size=1MiB 
+ubi_type=static
+ubi_names=test_spl_0,test_spl_1
+
+[kernel]
+image=test_vmlinux.bin
+ubi_ids=12,13
+ubi_size=2MiB 
+ubi_type=static
+ubi_names=test_kernel_0,test_kernel_1
+
+[rootfs]
+image=test_rootfs.bin
+ubi_ids=14,15
+ubi_size=2MiB 
+ubi_type=dynamic
+ubi_names=test_rootfs_0,test_rootfs_1
diff --git a/ubi-utils/old-tools/scripts/ubi_test.sh b/ubi-utils/old-tools/scripts/ubi_test.sh
new file mode 100755 (executable)
index 0000000..73e4b19
--- /dev/null
@@ -0,0 +1,328 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read test script
+#
+# Written in shell language to reduce dependencies to more sophisticated 
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+# 1.1 Use ubiupdatevol instead of ubiwritevol
+#
+
+VERSION="1.1"
+
+export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+UBIMKVOL=ubimkvol
+UBIRMVOL=ubirmvol
+UBIUPDATEVOL=ubiupdatevol
+
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+SIZE_512K=524288
+SIZE_1M=1310720
+
+SELF=$0
+MINVOL=10
+MAXVOL=12
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed () 
+{
+    echo "FAILED"
+}
+
+passed ()
+{
+    echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+    echo "SUCCESS"
+    exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+    echo "FAILED"
+    exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+    echo -n "*** Fixing the sysfs issue with the /dev nodes ... "
+
+    minor=0
+    major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+    rm -rf /dev/ubi0
+    mknod /dev/ubi0 c $major 0
+
+    for minor in `seq 0 $MAXVOL`; do
+       ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+        rm -rf /dev/ubi0_$minor
+        mknod /dev/ubi0_$minor c $major $(($minor + 1))
+    done
+    passed
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+#                 to delete it.
+# @id:     volume id
+#
+delete_volume ()
+{
+    volume=$1
+
+    ### FIXME broken sysfs!!!!
+    if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then
+
+       echo -n "*** Truncate volume if it exists ... "
+       $UBIUPDATEVOL -d0 -n$volume -t
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+
+       echo -n "*** Delete volume if it exists ... "
+       $UBIRMVOL -d0 -n$volume
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+    fi
+}
+
+mkvol_rmvol_test ()
+{
+    type=$1
+
+### Test if volume delete on non-existing volumes fails nicely
+
+    for i in `seq $MINVOL $MAXVOL`; do
+       echo "*** Delete if exist or not $i ... "
+
+       delete_volume $i
+       passed
+    done
+
+### Now deleting volumes must fail
+
+    for i in `seq $MINVOL $MAXVOL`; do
+       echo "*** Trying to delete non existing UBI Volume $i ... "
+
+       $UBIRMVOL -d0 -n$i
+       if [ $? -eq "0" ] ; then
+           exit_failure
+       fi
+       passed
+    done
+
+### Test if volume creation works ok
+
+    for i in `seq $MINVOL $MAXVOL`; do
+       echo "*** Creating UBI Volume $i ... "
+       echo "    $UBIMKVOL -d0 -n$i -t$type -NNEW$i -s $SIZE_512K"
+
+       $UBIMKVOL -d0 -n$i -t$type -N"NEW$i" -s $SIZE_512K
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+    done
+
+### Now deleting volumes must be ok
+
+    for i in `seq $MINVOL $MAXVOL`; do
+       echo "*** Trying to delete UBI Volume $i ... "
+
+       $UBIRMVOL -d0 -n$i
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+    done
+
+### Now allocate too large volume
+
+    echo -n "*** Try to create too large volume"
+    $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s 800000000
+    if [ $? -eq "0" ] ; then
+       exit_failure
+    fi
+    passed
+}
+
+# writevol_test - Tests volume creation and writing data to it.
+#
+# @size:    Size of random data to write
+# @type:    Volume type static or dynamic
+#
+writevol_test ()
+{
+    size=$1
+    type=$2
+
+    echo "*** Write volume test with size $size"
+
+### Make sure that volume exist, delete existing volume, create new
+
+    delete_volume $MINVOL
+
+    echo -n "*** Try to create volume ... "
+    $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M
+    if [ $? -ne "0" ] ; then
+       exit_failure
+    fi
+    passed
+    
+### Try to create same volume again
+    echo -n "*** Try to create some volume again, this must fail ... "
+    $UBIMKVOL -d0 -n$MINVOL -t$type -N"NEW$MINVOL" -s $SIZE_1M
+    if [ $? -eq "0" ] ; then
+       exit_failure
+    fi
+    passed
+    
+### Now create test data, write it, read it, compare it
+    echo -n "*** Create test data ... "
+    dd if=/dev/urandom of=testdata.bin bs=$size count=1
+    if [ $? -ne "0" ] ; then
+       exit_failure
+    fi
+    passed
+
+    echo "*** Now writing data to volume ... "
+    # sleep 5
+    ls -l testdata.bin
+    echo "    $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin"
+    $UBIUPDATEVOL -d0 -n$MINVOL testdata.bin
+    if [ $? -ne "0" ] ; then
+       exit_failure
+    fi
+    passed
+
+    if [ $type = "static" ] ; then
+       echo "*** Download data with cat ... "
+       cat /dev/ubi0_$MINVOL > readdata.bin
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+    else
+       echo "*** Download data with dd bs=1 ... "
+       dd if=/dev/ubi0_$MINVOL of=readdata.bin bs=$size count=1
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+
+       # Size 1 does not work with this test ...
+       #
+       #echo "*** Download data with dd bs=$size ... "
+       #dd if=/dev/ubi0_$MINVOL of=readdata2.bin bs=$size count=1
+       #if [ $? -ne "0" ] ; then
+       #    exit_failure
+       #fi
+       #passed
+
+       #echo -n "*** Comparing data (1) ... "
+       #cmp readdata.bin readdata2.bin
+       #if [ $? -ne "0" ] ; then
+       #    exit_failure
+       #fi
+       #passed
+    fi
+
+    echo -n "*** Comparing data ... "
+    cmp readdata.bin testdata.bin
+    if [ $? -ne "0" ] ; then
+       exit_failure
+    fi
+    passed
+}
+
+echo "***********************************************************************"
+echo "*           UBI Testing starts now ...                                *"
+echo "*                                 Good luck!                          *"
+echo "***********************************************************************"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+    echo "No UBI found in /proc/devices! I am broken!"
+    exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+    echo "Running on example hardware"
+    mount -o remount,rw / /
+    sleep 1
+    fix_sysfs_issue
+else
+    echo "Running on Artems hardware"
+fi
+
+echo "***********************************************************************"
+echo "*        mkvol/rmvol testing for static volumes ...                   *"
+echo "***********************************************************************"
+
+mkvol_rmvol_test static
+
+echo "***********************************************************************"
+echo "*        mkvol/rmvol testing for dynamic volumes ...                  *"
+echo "***********************************************************************"
+
+mkvol_rmvol_test dynamic
+
+echo "***********************************************************************"
+echo "*                write to static volumes ...                          *"
+echo "***********************************************************************"
+
+# 10 Erase blocks = (128 KiB - 64 * 2) * 10
+#                 = 1309440 bytes
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+for size in 262144 131073 131072 2048 1 4096 12800 31313  ; do
+    writevol_test $size static
+done
+
+echo "***********************************************************************"
+echo "*                write to dynamic volumes ...                         *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+for size in 131073 131072 2048 1 4096 12800 31313 262144 ; do
+    writevol_test $size dynamic
+done
+
+echo "***********************************************************************"
+echo "*               Congratulations, no errors found!                     *"
+echo "*              Have fun with your cool UBI system!                    *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/old-tools/scripts/ubi_tools_test.sh b/ubi-utils/old-tools/scripts/ubi_tools_test.sh
new file mode 100755 (executable)
index 0000000..7f121f1
--- /dev/null
@@ -0,0 +1,252 @@
+#!/bin/sh
+#
+# UBI Volume creation/deletion/write/read test script.
+# Uses our flash update tools and the associated toolchain for flash
+# image creation.
+#
+# Written in shell language to reduce dependencies to more sophisticated 
+# interpreters, which may not be available on some stupid platforms.
+#
+# Author: Frank Haverkamp <haver@vnet.ibm.com>
+#
+# 1.0 Initial version
+#
+
+VERSION="1.0"
+
+export PATH=$PATH:~/bin:/usr/local/bin:/home/dedekind/work/prj/ubi/tools/flashutils/bin/
+
+UBIMKVOL=ubimkvol
+UBIRMVOL=ubirmvol
+UBIWRITEVOL=ubiupdatevol
+PFIFLASH=pfiflash
+CMP=cmp
+
+MAXVOL=32
+
+test_pfi=test_complete.pfi
+real_pfi=example_complete.pfi
+
+# 128 KiB 131072
+# 256 KiB 262144
+# 512 KiB 524288
+
+#
+# To have a standardized output I define the following function to be
+# used when a test was ok or when it failed.
+#
+failed () 
+{
+    echo "FAILED"
+}
+
+passed ()
+{
+    echo "PASSED"
+}
+
+#
+# Print sucess message. Consider to exit with zero as return code.
+#
+exit_success ()
+{
+    echo "SUCCESS"
+    exit 0
+}
+
+#
+# Print failure message. Consider to exit with non zero return code.
+#
+exit_failure ()
+{
+    echo "FAILED"
+    exit 1
+}
+
+###############################################################################
+#
+# START
+#
+###############################################################################
+
+fix_sysfs_issue ()
+{
+    echo -n "*** Fixing the sysfs issue with the /dev nodes ... "
+
+    minor=0
+    major=`grep ubi0 /proc/devices | sed -e 's/\(.*\) ubi0/\1/'`
+
+    rm -rf /dev/ubi0
+    mknod /dev/ubi0 c $major 0
+
+    for minor in `seq 0 $MAXVOL`; do
+       ### echo " mknod /dev/ubi0_$minor c $major $(($minor + 1))"
+        rm -rf /dev/ubi0_$minor
+        mknod /dev/ubi0_$minor c $major $(($minor + 1))
+    done
+    passed
+}
+
+# delete_volume - Delete a volume. If it does not exist, do not try
+#                 to delete it.
+# @id:     volume id
+#
+delete_volume ()
+{
+    volume=$1
+
+    ### FIXME broken sysfs!!!!
+    if [ -e /sys/class/ubi/$volume -o -e /sys/class/ubi/ubi0/$volume -o -e /sys/class/ubi/ubi0_$volume ]; then
+
+       echo -n "*** Truncate volume if it exists ... "
+       $UBIWRITEVOL -d0 -n$volume -t
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+
+       echo -n "*** Delete volume if it exists ... "
+       $UBIRMVOL -d0 -n$volume
+       if [ $? -ne "0" ] ; then
+           exit_failure
+       fi
+       passed
+    fi
+}
+
+echo "***********************************************************************"
+echo "*           UBI Tools Testing starts now ...                          *"
+echo "*                                 Good luck!                          *"
+echo "***********************************************************************"
+
+# Set to zero if not running on example hardware
+grep ubi /proc/devices > /dev/null
+if [ $? -ne "0" ]; then
+    echo "No UBI found in /proc/devices! I am broken!"
+    exit_failure
+fi
+
+# Set to zero if not running on example hardware
+grep 1142 /proc/cpuinfo > /dev/null
+if [ $? -eq "0" ]; then
+    echo "Running on example hardware"
+    mount -o remount,rw / /
+    sleep 1
+    fix_sysfs_issue
+else
+    echo "Running on other hardware"
+fi
+
+### Test basic stuff
+pfiflash_basic ()
+{
+    echo "Calling pfiflash with test-data ... "
+    echo "    $PFIFLASH $test_pfi"
+    $PFIFLASH $test_pfi
+    if [ $? -ne "0" ]; then
+       echo "Uhhh something went wrong!"
+       exit_failure
+    fi
+    passed
+    
+    echo "Testing if data is correct 10 and 11 ... "
+    $CMP /dev/ubi0_10 /dev/ubi0_11
+    if [ $? -ne "0" ]; then
+       echo "Mirrored volumes not equal!"
+       exit_failure
+    fi
+    passed
+    
+    echo "Comparing against original data ... "
+    $CMP /dev/ubi0_10 test_u-boot.bin
+    if [ $? -ne "0" ]; then
+       echo "Compared volume not equal!"
+       exit_failure
+    fi
+    passed
+    
+    echo "Testing if data is correct 12 and 13 ... "
+    $CMP /dev/ubi0_12 /dev/ubi0_13
+    if [ $? -ne "0" ]; then
+       echo "Mirrored volumes not equal!"
+       exit_failure
+    fi
+    passed
+    
+    echo "Comparing against original data ... "
+    $CMP /dev/ubi0_12 test_vmlinux.bin
+    if [ $? -ne "0" ]; then
+       echo "Compared volume not equal!"
+       exit_failure
+    fi
+    passed
+    
+    echo "Testing if data is correct 14 and 15 ... "
+    $CMP /dev/ubi0_14 /dev/ubi0_15
+    if [ $? -ne "0" ]; then
+       echo "Mirrored volumes not equal!"
+       exit_failure
+    fi
+    passed
+}
+
+### Test each and everything
+pfiflash_advanced ()
+{
+    if [ -e  example_complete.pfi ]; then
+       echo "Calling pfiflash with real data ... "
+       $PFIFLASH -p overwrite --complete example_complete.pfi
+       if [ $? -ne "0" ]; then
+           echo "Uhhh something went wrong!"
+           exit_failure
+       fi
+       passed
+       
+       echo "Testing if data is correct 2 and 3 ... "
+       $CMP /dev/ubi0_2 /dev/ubi0_3
+       if [ $? -ne "0" ]; then
+           echo "Mirrored volumes not equal!"
+           exit_failure
+       fi
+       passed
+       
+       echo "Comparing against original data ... "
+       $CMP /dev/ubi0_2 u-boot.bin
+       if [ $? -ne "0" ]; then
+           echo "Compared volume not equal!"
+           exit_failure
+       fi
+       passed
+       
+       echo "Testing if data is correct 6 and 7 ... "
+       $CMP /dev/ubi0_6 /dev/ubi0_7
+       if [ $? -ne "0" ]; then
+           echo "Mirrored volumes not equal!"
+           exit_failure
+       fi
+       passed
+       
+       echo "Comparing against original data ... "
+       $CMP /dev/ubi0_6 vmlinux.bin
+       if [ $? -ne "0" ]; then
+           echo "Compared volume not equal!"
+           exit_failure
+       fi
+       passed
+    fi
+}
+
+echo "***********************************************************************"
+echo "*                Testing pfiflash ...                                 *"
+echo "***********************************************************************"
+echo "VERSION: $VERSION"
+
+pfiflash_basic
+pfiflash_advanced
+    
+echo "***********************************************************************"
+echo "*               Congratulations, no errors found!                     *"
+echo "*              Have fun with your cool UBI system!                    *"
+echo "***********************************************************************"
+
+exit_success
diff --git a/ubi-utils/old-tools/scripts/ubicrc32.pl b/ubi-utils/old-tools/scripts/ubicrc32.pl
new file mode 100644 (file)
index 0000000..92711cb
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+# Subroutine crc32(): Calculates the CRC on a given string.
+
+{
+    my @table = ();
+
+    # @brief Calculate CRC32 for a given string.
+    sub crc32
+    {
+       unless (@table) {
+           # Initialize the CRC table
+           my $poly = 0xEDB88320;
+           @table = ();
+
+           for my $i (0..255) {
+               my $c = $i;
+
+               for my $j (0..7) {
+                   $c = ($c & 1) ? (($c >> 1) ^ $poly) : ($c >> 1);
+               }
+               $table[$i] = $c;
+           }
+       }
+       my $s = shift;          # string to calculate the CRC for
+       my $crc = shift;        # CRC start value
+
+       defined($crc)
+           or $crc = 0xffffffff; # Default CRC start value
+
+       for (my $i = 0; $i < length($s); $i++) {
+           $crc = $table[($crc ^ ord(substr($s, $i, 1))) & 0xff]
+               ^ ($crc >> 8);
+       }
+       return $crc;
+    }
+}
+
+sub crc32_on_file
+{
+    my $file = shift;
+
+    my $crc32 = crc32('');
+    my $buf = '';
+    my $ret = 0;
+
+    while ($ret = read($file, $buf, 8192)) {
+       $crc32 = crc32($buf, $crc32);
+    }
+    defined($ret)
+       or return undef;
+    printf("0x%x\n", $crc32);
+}
+
+
+# Main routine: Calculate the CRCs on the given files and print the
+# results.
+
+{
+    if (@ARGV) {
+       while (my $path = shift) {
+           my $file;
+           open $file, "<", $path
+               or die "Error opening '$path'.\n";
+
+           &crc32_on_file($file)
+               or die "Error reading from '$path'.\n";
+           close $file;
+       }
+    } else {
+       &crc32_on_file(\*STDIN)
+           or die "Error reading from stdin.\n";
+    }
+}
diff --git a/ubi-utils/old-tools/scripts/unubi_test.sh b/ubi-utils/old-tools/scripts/unubi_test.sh
new file mode 100644 (file)
index 0000000..40dc2e2
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Use raw NAND data, extract UBI image and apply tool to it.
+# Test basic functionality.
+#
+# 2007 Frank Haverkamp <haver@vnet.ibm.com>
+#
+
+version=1.1
+
+image=data.mif
+oob=oob.bin
+data=data.bin
+pagesize=2048
+volmax=31
+datadir=unubi_data
+
+# general arguments e.g. debug enablement
+# unubi_args="-D"
+
+echo "------------------------------------------------------------------------"
+echo "Testcase: ${0} Version: ${version}"
+echo "------------------------------------------------------------------------"
+echo "Testing nand2bin ..."
+echo "  Input:    ${image}"
+echo "  Data:     ${data}"
+echo "  OOB:      ${oob}"
+echo "  Pagesize: ${pagesize}"
+nand2bin --pagesize ${pagesize} -o ${data} -O ${oob} ${image}
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Testing unubi ..."
+echo "------------------------------------------------------------------------"
+unubi --version
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Trying to extract first ${volmax} volumes ..."
+echo "------------------------------------------------------------------------"
+mkdir -p ${datadir}/volumes
+for v in `seq 0 ${volmax}` ; do
+    unubi ${unubi_args} -r${v} -d${datadir}/volumes ${data}
+    echo -n "."
+done
+echo "ok"
+ls -l ${datadir}/volumes
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Extracting graphics ..."
+echo "------------------------------------------------------------------------"
+unubi -a  -d${datadir} ${data}
+echo "Use gnuplot to display:"
+ls ${datadir}/*.plot
+ls ${datadir}/*.data
+echo
+
+echo "------------------------------------------------------------------------"
+echo "eb-split"
+echo "------------------------------------------------------------------------"
+unubi -e -d${datadir}/eb-split ${data}
+ls -l ${datadir}/eb-split
+echo
+
+echo "------------------------------------------------------------------------"
+echo "vol-split"
+echo "------------------------------------------------------------------------"
+unubi -v -d${datadir}/vol-split ${data}
+ls  -l ${datadir}/vol-split
+echo
+echo "The generated images contain only the data (126KiB in our   "
+echo "case) not including the UBI erase count and volume info     "
+echo "header. For dynamic volumes the data should be the full     "
+echo "126KiB. Unubi cannot know how much of the data is valid.    "
+echo
+
+echo "------------------------------------------------------------------------"
+echo "!vol-split"
+echo "------------------------------------------------------------------------"
+unubi -V -d${datadir}/vol-split! ${data}
+ls -l ${datadir}/vol-split\!
+echo
+echo "The generated images contain the full block data of 128KiB  "
+echo "including the UBI erase count and volume information header."
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Extracting volume info table ..."
+echo "------------------------------------------------------------------------"
+unubi -i -d${datadir} ${data}
+echo "I strongly hope that empty ubi blocks are filled with 0xff! "
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Table 0"
+echo "------------------------------------------------------------------------"
+cat ${datadir}/vol_info_table0
+echo
+
+echo "------------------------------------------------------------------------"
+echo "Table 1"
+echo "------------------------------------------------------------------------"
+cat ${datadir}/vol_info_table1
+echo
diff --git a/ubi-utils/old-tools/src/bin2nand.c b/ubi-utils/old-tools/src/bin2nand.c
new file mode 100644 (file)
index 0000000..83f50cc
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2007
+ *
+ * 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: Oliver Lohmann
+ */
+
+/*
+ * Create a flashable NAND image from a binary image
+ *
+ * History:
+ * 1.0 Initial release (tglx)
+ * 1.1 Understands hex and dec input parameters (tglx)
+ * 1.2 Generates separated OOB data, if needed. (oloh)
+ * 1.3 Padds data/oob to a given size. (oloh)
+ * 1.4 Removed argp because we want to use uClibc.
+ * 1.5 Minor cleanup
+ * 1.6 Written variable not initialized (-j did not work) (haver)
+ * 1.7 Made NAND ECC layout configurable (haver)
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "error.h"
+#include "config.h"
+#include "nandecc.h"
+#include "ecclayouts.h"
+
+#define PROGRAM_VERSION "1.7"
+
+#define ARRAY_SIZE(a)    (sizeof(a) / sizeof((a)[0]))
+
+#define CHECK_ENDP(option, endp) do {                  \
+       if (*endp) {                                    \
+               fprintf(stderr,                         \
+                       "Parse error option \'%s\'. "   \
+                       "No correct numeric value.\n"   \
+                       , option);                      \
+               exit(EXIT_FAILURE);                     \
+       }                                               \
+} while(0)
+
+typedef enum action_t {
+       ACT_NORMAL          = 0x00000001,
+} action_t;
+
+#define PAGESIZE       2048
+#define PADDING                   0 /* 0 means, do not adjust anything */
+#define BUFSIZE                4096
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "bin2nand - a tool for adding OOB information to a "
+       "binary input file.\n";
+
+static const char *optionsstr =
+"  -c, --copyright          Print copyright informatoin.\n"
+"  -j, --padding=<num>      Padding in Byte/Mi/ki. Default = no padding\n"
+"  -l, --ecc-placement=<MTD,IBM> OOB placement scheme (default is IBM).\n"
+"  -p, --pagesize=<num>     Pagesize in Byte/Mi/ki. Default = 2048\n"
+"  -o, --output=<fname>     Output filename. Interleaved Data/OOB if\n"
+"                           output-oob not specified.\n"
+"  -q, --output-oob=<fname> Write OOB data in separate file.\n"
+"  -?, --help               Give this help list\n"
+"      --usage              Give a short usage message\n"
+"  -V, --version            Print program version\n";
+
+static const char *usage =
+"Usage: bin2nand [-c?V] [-j <num>] [-p <num>] [-o <fname>] [-q <fname>]\n"
+"            [--copyright] [--padding=<num>] [--pagesize=<num>]\n"
+"            [--output=<fname>] [--output-oob=<fname>] [--help] [--usage]\n"
+"            [--version]\n";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "padding", .has_arg = 1, .flag = NULL, .val = 'j' },
+       { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+       { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "output-oob", .has_arg = 1, .flag = NULL, .val = 'q' },
+       { .name = "ecc-layout", .has_arg = 1, .flag = NULL, .val = 'l' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+#define __unused __attribute__((unused))
+static const char copyright [] __unused = "Copyright IBM Corp. 2007";
+
+struct args {
+       action_t action;
+
+       size_t pagesize;
+       size_t oobsize;
+       size_t padding;
+
+       FILE* fp_in;
+       const char *file_out_data; /* Either: Data and OOB interleaved
+                                     or plain data */
+       const char *file_out_oob; /* OOB Data only. */
+       struct nand_ecclayout *nand_oob;
+
+       /* special stuff needed to get additional arguments */
+       char *arg1;
+       char **options;                 /* [STRING...] */
+};
+
+
+static int ustrtoull(const char *cp, char **endp, unsigned int base)
+{
+       unsigned long long res = strtoull(cp, endp, base);
+
+       switch (**endp) {
+       case 'G':
+               res *= 1024;
+       case 'M':
+               res *= 1024;
+       case 'k':
+       case 'K':
+               res *= 1024;
+       /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+               if ((*endp)[1] == 'i')
+                       (*endp) += 2;
+       }
+       return res;
+}
+
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+       const char *ecc_layout = NULL;
+       unsigned int i, oob_idx = 0;
+       char* endp;
+
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "cj:l:p:o:q:?V", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+               case 'p': /* pagesize */
+                       args->pagesize = (size_t)
+                               ustrtoull(optarg, &endp, 0);
+                       CHECK_ENDP("p", endp);
+                       break;
+               case 'j': /* padding */
+                       args->padding = (size_t)
+                               ustrtoull(optarg, &endp, 0);
+                       CHECK_ENDP("j", endp);
+                       break;
+               case 'o': /* output */
+                       args->file_out_data = optarg;
+                       break;
+               case 'q': /* output oob */
+                       args->file_out_oob = optarg;
+                       break;
+               case 'l': /* --ecc-layout=<...> */
+                       ecc_layout = optarg;
+                       break;
+               case '?': /* help */
+                       printf("%s%s", doc, optionsstr);
+                       exit(0);
+                       break;
+               case 'V':
+                       printf("%s\n", PROGRAM_VERSION);
+                       exit(0);
+                       break;
+               case 'c':
+                       printf("%s\n", copyright);
+                       exit(0);
+               default:
+                       printf("%s", usage);
+                       exit(-1);
+               }
+       }
+
+       if (optind < argc) {
+               args->fp_in = fopen(argv[optind++], "rb");
+               if ((args->fp_in) == NULL) {
+                       err_quit("Cannot open file %s for input\n",
+                                argv[optind++]);
+               }
+       }
+
+       switch (args->pagesize) {
+       case 512:  args->oobsize = 16; oob_idx = 0; break;
+       case 2048: args->oobsize = 64; oob_idx = 1; break;
+       default:
+               err_msg("Unsupported page size: %d\n", args->pagesize);
+               return -EINVAL;
+       }
+
+       /* Figure out correct oob layout if it differs from default */
+       if (ecc_layout) {
+               for (i = 0; i < ARRAY_SIZE(oob_placement); i++)
+                       if (strcmp(ecc_layout, oob_placement[i].name) == 0)
+                               args->nand_oob =
+                                       oob_placement[i].nand_oob[oob_idx];
+       }
+       return 0;
+}
+
+static int
+process_page(struct args *args, uint8_t *buf, FILE *fp_data, FILE *fp_oob,
+            size_t *written)
+{
+       int eccpoi;
+       size_t i;
+       uint8_t oobbuf[64];
+       uint8_t ecc_code[3] = { 0, }; /* temp */
+
+       /* Calculate ECC for each subpage of 256 bytes */
+       memset(oobbuf, 0xff, sizeof(oobbuf));
+       for (eccpoi = 0, i = 0; i < args->pagesize; i += 256, eccpoi += 3) {
+               int j;
+               nand_calculate_ecc(&buf[i], ecc_code);
+               for (j = 0; j < 3; j++)
+                       oobbuf[args->nand_oob->eccpos[eccpoi + j]] = ecc_code[j];
+       }
+
+       /* write data */
+       *written += fwrite(buf, 1, args->pagesize, fp_data);
+
+       /* either separate oob or interleave with data */
+       if (fp_oob) {
+               i = fwrite(oobbuf, 1, args->oobsize, fp_oob);
+               if (ferror(fp_oob)) {
+                       err_msg("IO error\n");
+                       return -EIO;
+               }
+       }
+       else {
+               i = fwrite(oobbuf, 1, args->oobsize, fp_data);
+               if (ferror(fp_data)) {
+                       err_msg("IO error\n");
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+int main (int argc, char** argv)
+{
+       int rc = -1;
+       int res = 0;
+       size_t written = 0, read;
+       struct args args = {
+               .action   = ACT_NORMAL,
+               .pagesize = PAGESIZE,
+               .padding  = PADDING,
+               .fp_in    = NULL,
+               .file_out_data = NULL,
+               .file_out_oob = NULL,
+               .nand_oob = &ibm_nand_oob_64,
+       };
+
+       FILE* fp_out_data = stdout;
+       FILE* fp_out_oob = NULL;
+
+       parse_opt(argc, argv, &args);
+
+       uint8_t* buf = calloc(1, BUFSIZE);
+       if (!buf) {
+               err_quit("Cannot allocate page buffer.\n");
+       }
+
+       if (!args.fp_in) {
+               err_msg("No input image specified!\n");
+               goto err;
+       }
+
+       if (args.file_out_data) {
+               fp_out_data = fopen(args.file_out_data, "wb");
+               if (fp_out_data == NULL) {
+                       err_sys("Cannot open file %s for output\n",
+                                       args.file_out_data);
+                       goto err;
+               }
+       }
+
+       if (args.file_out_oob) {
+               fp_out_oob = fopen(args.file_out_oob, "wb");
+               if (fp_out_oob == NULL) {
+                       err_sys("Cannot open file %s for output\n",
+                                       args.file_out_oob);
+                       goto err;
+               }
+       }
+
+
+       while(1) {
+               read = fread(buf, 1, args.pagesize, args.fp_in);
+               if (feof(args.fp_in) && read == 0)
+                       break;
+
+               if (read < args.pagesize) {
+                       err_msg("Image not page aligned\n");
+                       goto err;
+               }
+
+               if (ferror(args.fp_in)) {
+                       err_msg("Read error\n");
+                       goto err;
+               }
+
+               res = process_page(&args, buf, fp_out_data, fp_out_oob,
+                                  &written);
+               if (res != 0)
+                       goto err;
+       }
+
+       while (written < args.padding) {
+               memset(buf, 0xff, args.pagesize);
+               res = process_page(&args, buf, fp_out_data, fp_out_oob,
+                                  &written);
+               if (res != 0)
+                       goto err;
+       }
+
+       rc = 0;
+err:
+       free(buf);
+
+       if (args.fp_in)
+               fclose(args.fp_in);
+
+       if (fp_out_oob)
+               fclose(fp_out_oob);
+
+       if (fp_out_data && fp_out_data != stdout)
+               fclose(fp_out_data);
+
+       if (rc != 0) {
+               err_msg("Error during conversion. rc: %d\n", rc);
+               if (args.file_out_data)
+                       remove(args.file_out_data);
+               if (args.file_out_oob)
+                       remove(args.file_out_oob);
+       }
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/bootenv.c b/ubi-utils/old-tools/src/bootenv.c
new file mode 100644 (file)
index 0000000..a6dd4de
--- /dev/null
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <bootenv.h>
+
+#include "hashmap.h"
+#include "error.h"
+
+#include <mtd/ubi-header.h>
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define BOOTENV_MAXLINE 512 /* max line size of a bootenv.txt file */
+
+/* Structures */
+struct bootenv {
+       hashmap_t map;   ///< Pointer to hashmap which holds data structure.
+};
+
+struct bootenv_list {
+       hashmap_t head; ///< Pointer to list which holds the data structure.
+};
+
+/**
+ * @brief Remove the '\n' from a given line.
+ * @param line Input/Output line.
+ * @param size Size of the line.
+ * @param fp   File Pointer.
+ * @return 0
+ * @return or error
+ */
+static int
+remove_lf(char *line, size_t size, FILE* fp)
+{
+       size_t i;
+
+       for (i = 0; i < size; i++) {
+               if (line[i] == '\n') {
+                       line[i] = '\0';
+                       return 0;
+               }
+       }
+
+       if (!feof(fp)) {
+               return BOOTENV_EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * @brief Determine if a line contains only WS.
+ * @param line The line to process.
+ * @param size Size of input line.
+ * @return 1   Yes, only WS.
+ * @return 0   No, contains data.
+ */
+static int
+is_ws(const char *line, size_t size)
+{
+       size_t i = 0;
+
+       while (i < size) {
+               switch (line[i]) {
+                       case '\n':
+                               return 1;
+                       case '#':
+                               return 1;
+                       case ' ':
+                               i++;
+                               continue;
+                       case '\t':
+                               i++;
+                               continue;
+                       default: /* any other char -> no cmnt */
+                               return 0;
+               }
+       }
+
+       return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief Build a list from a comma seperated value string.
+ * @param list Pointer to hashmap structure which shall store
+ *             the list.
+ * @param value        Comma seperated value string.
+ * @return 0
+ * @return or error.
+ */
+static int
+build_list_definition(hashmap_t list, const char *value)
+{
+       int rc = 0;
+       char *str = NULL;
+       char *ptr = NULL;
+       size_t len, i, j;
+
+       /* str: val1,val2 , val4,...,valN     */
+       len = strlen(value);
+       str = (char*) malloc((len+1) * sizeof(char));
+
+       /* 1. reformat string: remove spaces */
+       for (i = 0, j = 0; i < len; i++) {
+               if (value[i] == ' ')
+                       continue;
+
+               str[j] = value[i];
+               j++;
+       }
+       str[j] = '\0';
+
+       /* str: val1,val2,val4,...,valN\0*/
+       /* 2. replace ',' seperator with '\0' */
+       len = strlen(str);
+       for (i = 0; i < len; i++) {
+               if (str[i] == ',') {
+                       str[i] = '\0';
+               }
+       }
+
+       /* str: val1\0val2\0val4\0...\0valN\0*/
+       /* 3. insert definitions into a hash map, using it like a list */
+       i = j = 0;
+       ptr = str;
+       while (((i = strlen(ptr)) > 0) && (j < len)) {
+               rc = hashmap_add(list, ptr, "");
+               if (rc != 0) {
+                       free(str);
+                       return rc;
+               }
+               j += i+1;
+               if (j < len)
+                       ptr += i+1;
+       }
+
+       free(str);
+       return rc;
+}
+
+/**
+ * @brief Extract a key value pair and add it to a hashmap
+ * @param str  Input string which contains a key value pair.
+ * @param env  The updated handle which contains the new pair.
+ * @return 0
+ * @return or error
+ * @note The input string format is: "key=value"
+ */
+static int
+extract_pair(const char *str, bootenv_t env)
+{
+       int rc = 0;
+       char *key = NULL;
+       char *val = NULL;
+
+       key = strdup(str);
+       if (key == NULL)
+               return -ENOMEM;
+
+       val = strstr(key, "=");
+       if (val == NULL) {
+               rc = BOOTENV_EBADENTRY;
+               goto err;
+       }
+
+       *val = '\0'; /* split strings */
+       val++;
+
+       rc = bootenv_set(env, key, val);
+
+ err:
+       free(key);
+       return rc;
+}
+
+int
+bootenv_destroy(bootenv_t* env)
+{
+       int rc = 0;
+
+       if (env == NULL || *env == NULL)
+               return -EINVAL;
+
+       bootenv_t tmp = *env;
+
+       rc = hashmap_free(tmp->map);
+       if (rc != 0)
+               return rc;
+
+       free(tmp);
+       return rc;
+}
+
+int
+bootenv_create(bootenv_t* env)
+{
+       bootenv_t res;
+       res = (bootenv_t) calloc(1, sizeof(struct bootenv));
+
+       if (res == NULL)
+               return -ENOMEM;
+
+       res->map = hashmap_new();
+
+       if (res->map == NULL) {
+               free(res);
+               return -ENOMEM;
+       }
+
+       *env = res;
+
+       return 0;
+}
+
+
+/**
+ * @brief Read a formatted buffer and scan it for valid bootenv
+ *       key/value pairs. Add those pairs into a hashmap.
+ * @param env  Hashmap which shall be used to hold the data.
+ * @param buf  Formatted buffer.
+ * @param size Size of the buffer.
+ * @return 0
+ * @return or error
+ */
+static int
+rd_buffer(bootenv_t env, const char *buf, size_t size)
+{
+       const char *curr = buf;         /* ptr to current key/value pair */
+       uint32_t i, j;                  /* current length, chars processed */
+
+       if (buf[size - 1] != '\0')      /* must end in '\0' */
+               return BOOTENV_EFMT;
+
+       for (j = 0; j < size; j += i, curr += i) {
+               /* strlen returns the size of the string upto
+                  but not including the null terminator;
+                  adding 1 to account for '\0' */
+               i = strlen(curr) + 1;
+
+               if (i == 1)
+                       return 0;       /* no string found */
+
+               if (extract_pair(curr, env) != 0)
+                       return BOOTENV_EINVAL;
+       }
+
+       return 0;
+}
+
+
+int
+bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t* ret_crc)
+{
+       int rc;
+       char *buf = NULL;
+       size_t i = 0;
+       uint32_t crc32_table[256];
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       /* allocate temp buffer */
+       buf = (char*) calloc(1, size * sizeof(char));
+       if (buf == NULL)
+               return -ENOMEM;
+
+       /* FIXME Andreas, please review this I removed size-1 and
+        * replaced it by just size, I saw the kernel image starting
+        * with a 0x0060.... and not with the 0x60.... what it should
+        * be. Is this a tools problem or is it a problem here where
+        * fp is moved not to the right place due to the former size-1
+        * here.
+        */
+       while((i < size) && (!feof(fp))) {
+               int c = fgetc(fp);
+               if (c == EOF) {
+                       /* FIXME isn't this dangerous, to update
+                          the boot envs with incomplete data? */
+                       buf[i++] = '\0';
+                       break;  /* we have enough */
+               }
+               if (ferror(fp)) {
+                       rc = -EIO;
+                       goto err;
+               }
+
+               buf[i++] = (char)c;
+       }
+
+       /* calculate crc to return */
+       if (ret_crc != NULL) {
+               init_crc32_table(crc32_table);
+               *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+       }
+
+       /* transfer to hashmap */
+       rc = rd_buffer(env, buf, size);
+
+err:
+       free(buf);
+       return rc;
+}
+
+
+/**
+ * If we have a single file containing the boot-parameter size should
+ * be specified either as the size of the file or as BOOTENV_MAXSIZE.
+ * If the bootparameter are in the middle of a file we need the exact
+ * length of the data.
+ */
+int
+bootenv_read(FILE* fp, bootenv_t env, size_t size)
+{
+       return bootenv_read_crc(fp, env, size, NULL);
+}
+
+
+int
+bootenv_read_txt(FILE* fp, bootenv_t env)
+{
+       int rc = 0;
+       char *buf = NULL;
+       char *line = NULL;
+       char *lstart = NULL;
+       char *curr = NULL;
+       size_t len;
+       size_t size;
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       size = BOOTENV_MAXSIZE;
+
+       /* allocate temp buffers */
+       buf = (char*) calloc(1, size * sizeof(char));
+       lstart = line = (char*) calloc(1, size * sizeof(char));
+       if ((buf == NULL)  || (line == NULL)) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       curr = buf;
+       while ((line = fgets(line, size, fp)) != NULL) {
+               if (is_ws(line, size)) {
+                       continue;
+               }
+               rc = remove_lf(line, BOOTENV_MAXSIZE, fp);
+               if (rc != 0) {
+                       goto err;
+               }
+
+               /* copy new line to binary buffer */
+               len = strlen(line);
+               if (len > size) {
+                       rc = -EFBIG;
+                       goto err;
+               }
+               size -= len; /* track remaining space */
+
+               memcpy(curr, line, len);
+               curr += len + 1; /* for \0 seperator */
+       }
+
+       rc = rd_buffer(env, buf, BOOTENV_MAXSIZE);
+err:
+       if (buf != NULL)
+               free(buf);
+       if (lstart != NULL)
+               free(lstart);
+       return rc;
+}
+
+static int
+fill_output_buffer(bootenv_t env, char *buf, size_t buf_size_max ubi_unused,
+               size_t *written)
+{
+       int rc = 0;
+       size_t keys_size, i;
+       size_t wr = 0;
+       const char **keys = NULL;
+       const char *val = NULL;
+
+       rc = bootenv_get_key_vector(env, &keys_size, 1, &keys);
+       if (rc != 0)
+               goto err;
+
+       for (i = 0; i < keys_size; i++) {
+               if (wr > BOOTENV_MAXSIZE) {
+                       rc = -ENOSPC;
+                       goto err;
+               }
+
+               rc = bootenv_get(env, keys[i], &val);
+               if (rc != 0)
+                       goto err;
+
+               wr += snprintf(buf + wr, BOOTENV_MAXSIZE - wr,
+                               "%s=%s", keys[i], val);
+               wr++; /* for \0 */
+       }
+
+       *written = wr;
+
+err:
+       if (keys != NULL)
+               free(keys);
+
+       return rc;
+}
+
+int
+bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc)
+{
+       int rc = 0;
+       size_t size = 0;
+       char *buf = NULL;
+       uint32_t crc32_table[256];
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+       if (buf == NULL)
+               return -ENOMEM;
+
+
+       rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, &size);
+       if (rc != 0)
+               goto err;
+
+       /* calculate crc to return */
+       if (ret_crc != NULL) {
+               init_crc32_table(crc32_table);
+               *ret_crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, size);
+       }
+
+       if (fwrite(buf, size, 1, fp) != 1) {
+               rc = -EIO;
+               goto err;
+       }
+
+err:
+       if (buf != NULL)
+               free(buf);
+       return rc;
+}
+
+int
+bootenv_write(FILE* fp, bootenv_t env)
+{
+       return bootenv_write_crc(fp, env, NULL);
+}
+
+int
+bootenv_compare(bootenv_t first, bootenv_t second)
+{
+       int rc;
+       size_t written_first, written_second;
+       char *buf_first, *buf_second;
+
+       if (first == NULL || second == NULL)
+               return -EINVAL;
+
+       buf_first = malloc(BOOTENV_MAXSIZE);
+       if (!buf_first)
+               return -ENOMEM;
+       buf_second = malloc(BOOTENV_MAXSIZE);
+       if (!buf_second) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE,
+                       &written_first);
+       if (rc < 0)
+               goto err;
+       rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE,
+                       &written_second);
+       if (rc < 0)
+               goto err;
+
+       if (written_first != written_second) {
+               rc = 1;
+               goto err;
+       }
+
+       rc = memcmp(buf_first, buf_second, written_first);
+       if (rc != 0) {
+               rc = 2;
+               goto err;
+       }
+
+err:
+       if (buf_first)
+               free(buf_first);
+       if (buf_second)
+               free(buf_second);
+
+       return rc;
+}
+
+int
+bootenv_size(bootenv_t env, size_t *size)
+{
+       int rc = 0;
+       char *buf = NULL;
+
+       if (env == NULL)
+               return -EINVAL;
+
+       buf = (char*) calloc(1, BOOTENV_MAXSIZE * sizeof(char));
+       if (buf == NULL)
+               return -ENOMEM;
+
+       rc = fill_output_buffer(env, buf, BOOTENV_MAXSIZE, size);
+       if (rc != 0)
+               goto err;
+
+err:
+       if (buf != NULL)
+               free(buf);
+       return rc;
+}
+
+int
+bootenv_write_txt(FILE* fp, bootenv_t env)
+{
+       int rc = 0;
+       size_t size, wr, i;
+       const char **keys = NULL;
+       const char *key = NULL;
+       const char *val = NULL;
+
+       if ((fp == NULL) || (env == NULL))
+               return -EINVAL;
+
+       rc = bootenv_get_key_vector(env, &size, 1, &keys);
+       if (rc != 0)
+               goto err;
+
+       for (i = 0; i < size; i++) {
+               key = keys[i];
+               rc = bootenv_get(env, key, &val);
+               if (rc != 0)
+                       goto err;
+
+               wr = fprintf(fp, "%s=%s\n", key, val);
+               if (wr != strlen(key) + strlen(val) + 2) {
+                       rc = -EIO;
+                       goto err;
+               }
+       }
+
+err:
+       if (keys != NULL)
+               free(keys);
+       return rc;
+}
+
+int
+bootenv_valid(bootenv_t env ubi_unused)
+{
+       /* @FIXME No sanity check implemented. */
+       return 0;
+}
+
+int
+bootenv_copy_bootenv(bootenv_t in, bootenv_t *out)
+{
+       int rc = 0;
+       const char *tmp = NULL;
+       const char **keys = NULL;
+       size_t vec_size, i;
+
+       if ((in == NULL) || (out == NULL))
+               return -EINVAL;
+
+       /* purge output var for sure... */
+       rc = bootenv_destroy(out);
+       if (rc != 0)
+               return rc;
+
+       /* create the new map  */
+       rc = bootenv_create(out);
+       if (rc != 0)
+               goto err;
+
+       /* get the key list from the input map */
+       rc = bootenv_get_key_vector(in, &vec_size, 0, &keys);
+       if (rc != 0)
+               goto err;
+
+       if (vec_size != hashmap_size(in->map)) {
+               rc = BOOTENV_ECOPY;
+               goto err;
+       }
+
+       /* make a deep copy of the hashmap */
+       for (i = 0; i < vec_size; i++) {
+               rc = bootenv_get(in, keys[i], &tmp);
+               if (rc != 0)
+                       goto err;
+
+               rc = bootenv_set(*out, keys[i], tmp);
+               if (rc != 0)
+                       goto err;
+       }
+
+err:
+       if (keys != NULL)
+               free(keys);
+
+       return rc;
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+int
+bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+                int *warnings, char *err_buf ubi_unused,
+                size_t err_buf_size ubi_unused)
+{
+       bootenv_list_t l_old = NULL;
+       bootenv_list_t l_new = NULL;
+       const char *pdd_old = NULL;
+       const char *pdd_new = NULL;
+       const char *tmp = NULL;
+       const char **vec_old = NULL;
+       const char **vec_new = NULL;
+       const char **pdd_up_vec = NULL;
+       size_t vec_old_size, vec_new_size, pdd_up_vec_size, i;
+       int rc = 0;
+
+       if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+               return -EINVAL;
+
+       /* get the pdd strings, e.g.:
+        * pdd_old=a,b,c
+        * pdd_new=a,c,d,e */
+       rc = bootenv_get(env_old, "pdd", &pdd_old);
+       if (rc != 0)
+               goto err;
+       rc = bootenv_get(env_new, "pdd", &pdd_new);
+       if (rc != 0)
+               goto err;
+
+       /* put it into a list and then convert it to an vector */
+       rc = bootenv_list_create(&l_old);
+       if (rc != 0)
+               goto err;
+       rc  = bootenv_list_create(&l_new);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_import(l_old, pdd_old);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_import(l_new, pdd_new);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_to_vector(l_old, &vec_old_size, &vec_old);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_list_to_vector(l_new, &vec_new_size, &vec_new);
+       if (rc != 0)
+               goto err;
+
+       rc = bootenv_copy_bootenv(env_new, env_res);
+       if (rc != 0)
+               goto err;
+
+       /* calculate the update vector between the old and new pdd */
+       pdd_up_vec = hashmap_get_update_key_vector(vec_old, vec_old_size,
+                       vec_new, vec_new_size, &pdd_up_vec_size);
+
+       if (pdd_up_vec == NULL) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       if (pdd_up_vec_size != 0) {
+               /* need to warn the user about the unset of
+                * some pdd/bootenv values */
+               *warnings = BOOTENV_WPDD_STRING_DIFFERS;
+
+               /* remove all entries in the new bootenv load */
+               for (i = 0; i < pdd_up_vec_size; i++) {
+                       bootenv_unset(*env_res, pdd_up_vec[i]);
+               }
+       }
+
+       /* generate the keep array and copy old pdd values to new bootenv */
+       for (i = 0; i < vec_old_size; i++) {
+               rc = bootenv_get(env_old, vec_old[i], &tmp);
+               if (rc != 0) {
+                       rc = BOOTENV_EPDDINVAL;
+                       goto err;
+               }
+               rc = bootenv_set(*env_res, vec_old[i], tmp);
+               if (rc != 0) {
+                       goto err;
+               }
+       }
+       /* put the old pdd string into the result map */
+       rc = bootenv_set(*env_res, "pdd", pdd_old);
+       if (rc != 0) {
+               goto err;
+       }
+
+
+err:
+       if (vec_old != NULL)
+               free(vec_old);
+       if (vec_new != NULL)
+               free(vec_new);
+       if (pdd_up_vec != NULL)
+               free(pdd_up_vec);
+
+       bootenv_list_destroy(&l_old);
+       bootenv_list_destroy(&l_new);
+       return rc;
+}
+
+
+int
+bootenv_pdd_overwrite(bootenv_t env_old, bootenv_t env_new,
+                     bootenv_t *env_res, int *warnings ubi_unused,
+                     char *err_buf ubi_unused, size_t err_buf_size ubi_unused)
+{
+       if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+               return -EINVAL;
+
+       return bootenv_copy_bootenv(env_new, env_res);
+}
+
+int
+bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new, bootenv_t *env_res,
+                 int *warnings ubi_unused, char *err_buf, size_t err_buf_size)
+{
+       if ((env_old == NULL) || (env_new == NULL) || (env_res == NULL))
+               return -EINVAL;
+
+       snprintf(err_buf, err_buf_size, "The PDD merge operation is not "
+                       "implemented. Contact: <oliloh@de.ibm.com>");
+
+       return BOOTENV_ENOTIMPL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+bootenv_get(bootenv_t env, const char *key, const char **value)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       *value = hashmap_lookup(env->map, key);
+       if (*value == NULL)
+               return BOOTENV_ENOTFOUND;
+
+       return 0;
+}
+
+int
+bootenv_get_num(bootenv_t env, const char *key, uint32_t *value)
+{
+       char *endptr = NULL;
+       const char *str;
+
+       if (env == NULL)
+               return 0;
+
+       str = hashmap_lookup(env->map, key);
+       if (!str)
+               return -EINVAL;
+
+       *value = strtoul(str, &endptr, 0);
+
+       if (*endptr == '\0') {
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int
+bootenv_set(bootenv_t env, const char *key, const char *value)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       return hashmap_add(env->map, key, value);
+}
+
+int
+bootenv_unset(bootenv_t env, const char *key)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       return hashmap_remove(env->map, key);
+}
+
+int
+bootenv_get_key_vector(bootenv_t env, size_t* size, int sort,
+                      const char ***vector)
+{
+       if ((env == NULL) || (size == NULL))
+               return -EINVAL;
+
+       *vector = hashmap_get_key_vector(env->map, size, sort);
+
+       if (*vector == NULL)
+               return -EINVAL;
+
+       return 0;
+}
+
+int
+bootenv_dump(bootenv_t env)
+{
+       if (env == NULL)
+               return -EINVAL;
+
+       return hashmap_dump(env->map);
+}
+
+int
+bootenv_list_create(bootenv_list_t *list)
+{
+       bootenv_list_t res;
+       res = (bootenv_list_t) calloc(1, sizeof(struct bootenv_list));
+
+       if (res == NULL)
+               return -ENOMEM;
+
+       res->head = hashmap_new();
+
+       if (res->head == NULL) {
+               free(res);
+               return -ENOMEM;
+       }
+
+       *list = res;
+       return 0;
+}
+
+int
+bootenv_list_destroy(bootenv_list_t *list)
+{
+       int rc = 0;
+
+       if (list == NULL)
+               return -EINVAL;
+
+       bootenv_list_t tmp = *list;
+       if (tmp == 0)
+               return 0;
+
+       rc = hashmap_free(tmp->head);
+       if (rc != 0)
+               return rc;
+
+       free(tmp);
+       *list = NULL;
+       return 0;
+}
+
+int
+bootenv_list_import(bootenv_list_t list, const char *str)
+{
+       if (list == NULL)
+               return -EINVAL;
+
+       return build_list_definition(list->head, str);
+}
+
+int
+bootenv_list_export(bootenv_list_t list, char **string)
+{
+       size_t size, i, j, bufsize, tmp, rc = 0;
+       const char **items;
+
+       if (list == NULL)
+               return -EINVAL;
+
+       bufsize = BOOTENV_MAXLINE;
+       char *res = (char*) malloc(bufsize * sizeof(char));
+       if (res == NULL)
+               return -ENOMEM;
+
+       rc = bootenv_list_to_vector(list, &size, &items);
+       if (rc != 0) {
+               goto err;
+       }
+
+       j = 0;
+       for (i = 0; i < size; i++) {
+               tmp = strlen(items[i]);
+               if (j >= bufsize) {
+                       bufsize += BOOTENV_MAXLINE;
+                       res = (char*) realloc(res, bufsize * sizeof(char));
+                       if (res == NULL)  {
+                               rc = -ENOMEM;
+                               goto err;
+                       }
+               }
+               memcpy(res + j, items[i], tmp);
+               j += tmp;
+               if (i < (size - 1)) {
+                       res[j] = ',';
+                       j++;
+               }
+       }
+       j++;
+       res[j] = '\0';
+       free(items);
+       *string = res;
+       return 0;
+err:
+       free(items);
+       return rc;
+}
+
+int
+bootenv_list_add(bootenv_list_t list, const char *item)
+{
+       if ((list == NULL) || (item == NULL))
+               return -EINVAL;
+
+       return hashmap_add(list->head, item, "");
+}
+
+int
+bootenv_list_remove(bootenv_list_t list, const char *item)
+{
+       if ((list == NULL) || (item == NULL))
+               return -EINVAL;
+
+       return hashmap_remove(list->head, item);
+}
+
+int
+bootenv_list_is_in(bootenv_list_t list, const char *item)
+{
+       if ((list == NULL) || (item == NULL))
+               return -EINVAL;
+
+       return hashmap_lookup(list->head, item) != NULL ? 1 : 0;
+}
+
+int
+bootenv_list_to_vector(bootenv_list_t list, size_t *size, const char ***vector)
+{
+       if ((list == NULL) || (size == NULL))
+               return -EINVAL;
+
+       *vector = hashmap_get_key_vector(list->head, size, 1);
+       if (*vector == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+
+int
+bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+               uint32_t **vector)
+{
+       int rc = 0;
+       size_t i;
+       uint32_t* res = NULL;
+       char *endptr = NULL;
+       const char **a = NULL;
+
+       rc = bootenv_list_to_vector(list, size, &a);
+       if (rc != 0)
+               goto err;
+
+       res = (uint32_t*) malloc (*size * sizeof(uint32_t));
+       if (!res)
+               goto err;
+
+       for (i = 0; i < *size; i++) {
+               res[i] = strtoul(a[i], &endptr, 0);
+               if (*endptr != '\0')
+                       goto err;
+       }
+
+       if (a)
+               free(a);
+       *vector = res;
+       return 0;
+
+err:
+       if (a)
+               free(a);
+       if (res)
+               free(res);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/bootenv.h b/ubi-utils/old-tools/src/bootenv.h
new file mode 100644 (file)
index 0000000..8fecdbf
--- /dev/null
@@ -0,0 +1,434 @@
+#ifndef __BOOTENV_H__
+#define __BOOTENV_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <pfiflash.h>
+
+/* DOXYGEN DOCUMENTATION */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file bootenv.h
+ * @author oliloh@de.ibm.com
+ * @version 1.3
+ *
+ * 1.3 Some renaming
+ */
+
+/**
+ * @mainpage Usage
+ *
+ * @section intro Introduction
+ * This library provides all functionality to handle with the so-called
+ * platform description data (PDD) and the bootparameters defined in
+ * U-Boot. It is able to apply the defined PDD operations in PDD update
+ * scenarios. For more information about the PDD and bootparameter
+ * environment "bootenv" confer the PDD documentation.
+ *
+ * @section ret Return codes
+ * This library defines some return codes which will be delivered classified
+ * as warnings or errors. See the "Defines" section for details and numeric
+ * values.
+ *
+ * @section benv Bootenv format description
+ * There are two different input formats:
+ *     - text files
+ *     - binary files
+ *
+ * @subsection txt Text Files
+ * Text files have to be specified like:
+ * @verbatim key1=value1,value2,value7\n key2=value55,value1\n key4=value1\n@endverbatim
+ *
+ * @subsection bin Binary files
+ * Binary files have to be specified like:
+ * @verbatim<CRC32-bit>key1=value1,value2,value7\0key2=value55,value1\0... @endverbatim
+ * You can confer the U-Boot documentation for more details.
+ *
+ * @section benvlists Bootenv lists format description.
+ * Values referenced in the preceeding subsection can be
+ * defined like lists:
+ * @verbatim value1,value2,value3 @endverbatim
+ * There are some situation where a conversion of a comma
+ * seperated list can be useful, e.g. to get a list
+ * of defined PDD entries.
+ */
+
+#define BOOTENV_MAXSIZE (1024 * 100) /* max 100kiB space for bootenv */
+
+/**
+ * @def BOOTENV_ECRC
+ *     @brief Given binary file is to large.
+ * @def BOOTENV_EFMT
+ *     @brief Given bootenv section has an invalid format
+ * @def BOOTENV_EBADENTRY
+ *     @brief Bad entry in the bootenv section.
+ * @def BOOTENV_EINVAL
+ *     @brief Invalid bootenv defintion.
+ * @def BOOTENV_ENOPDD
+ *     @brief Given bootenv sectoin has no PDD defintion string (pdd=...).
+ * @def BOOTENV_EPDDINVAL
+ *     @brief Given bootenv section has an invalid PDD defintion.
+ * @def BOOTENV_ENOTIMPL
+ *     @brief Functionality not implemented.
+ * @def BOOTENV_ECOPY
+ *     @brief Bootenv memory copy error
+ * @def BOOTENV_ENOTFOUND
+ *     @brief Given key has has no value.
+ * @def BOOTENV_EMAX
+ *     @brief Highest error value.
+ */
+#define BOOTENV_ETOOBIG                1
+#define BOOTENV_EFMT           2
+#define BOOTENV_EBADENTRY      3
+#define BOOTENV_EINVAL         4
+#define BOOTENV_ENOPDD         5
+#define BOOTENV_EPDDINVAL      6
+#define BOOTENV_ENOTIMPL       7
+#define BOOTENV_ECOPY          8
+#define BOOTENV_ENOTFOUND      9
+#define BOOTENV_EMAX           10
+
+/**
+ * @def BOOTENV_W
+ *     @brief A warning which is handled internally as an error
+ *      but can be recovered by manual effort.
+ * @def BOOTENV_WPDD_STRING_DIFFERS
+ *     @brief The PDD strings of old and new PDD differ and
+ *     can cause update problems, because new PDD values
+ *     are removed from the bootenv section completely.
+ */
+#define BOOTENV_W                   20
+#define BOOTENV_WPDD_STRING_DIFFERS  21
+#define BOOTENV_WMAX 22 /* highest warning value */
+
+
+typedef struct bootenv *bootenv_t;
+       /**< A bootenv library handle. */
+
+typedef struct bootenv_list *bootenv_list_t;
+       /**< A handle for a value list. */
+
+typedef int(*pdd_func_t)(bootenv_t, bootenv_t, bootenv_t*,
+               int*, char*, size_t);
+
+
+/**
+ * @brief Get a new handle.
+ * @return 0
+ * @return or error
+ * */
+int bootenv_create(bootenv_t *env);
+
+/**
+ * @brief      Cleanup structure.
+ * @param env  Bootenv structure which shall be destroyed.
+ * @return 0
+ * @return or error
+ */
+int bootenv_destroy(bootenv_t *env);
+
+/**
+ * @brief Copy a bootenv handle.
+ * @param in   The input bootenv.
+ * @param out  The copied output bootenv. Discards old data.
+ * @return 0
+ * @return or error
+ */
+int bootenv_copy_bootenv(bootenv_t in, bootenv_t *out);
+
+/**
+ * @brief Looks for a value inside the bootenv data.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @return NULL         key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get(bootenv_t env, const char *key, const char **value);
+
+
+/**
+ * @brief Looks for a value inside the bootenv data and converts it to num.
+ * @param env Handle to a bootenv structure.
+ * @param key The key.
+ * @param value A pointer to the resulting numerical value
+ * @return NULL         key not found
+ * @return !NULL ptr to value
+ */
+int bootenv_get_num(bootenv_t env, const char *key, uint32_t *value);
+
+/**
+ * @brief Set a bootenv value by key.
+ * @param env   Handle to a bootenv structure.
+ * @param key  Key.
+ * @param value        Value to set.
+ * @return 0
+ * @return or error
+ */
+int bootenv_set(bootenv_t env, const char *key, const char *value);
+
+/**
+ * @brief Remove the given key (and its value) from a bootenv structure.
+ * @param env  Handle to a bootenv structure.
+ * @param key  Key.
+ * @return 0
+ * @return or error
+ */
+int bootenv_unset(bootenv_t env, const char *key);
+
+
+/**
+ * @brief Get a vector of all keys which are currently set
+ *        within a bootenv handle.
+ * @param env  Handle to a bootenv structure.
+ * @param size The size of the allocated array structure.
+ * @param sort Flag, if set the vector is sorted ascending.
+ * @return NULL on error.
+ * @return !NULL a pointer to the first element the allocated vector.
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_get_key_vector(bootenv_t env, size_t *size, int sort,
+                               const char ***vector);
+
+/**
+ * @brief Calculate the size in bytes which are necessary to write the
+ *        current bootenv section in a *binary file.
+ * @param env  bootenv handle.
+ * @param size  The size in bytes of the bootenv handle.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_size(bootenv_t env, size_t *size);
+
+/**
+ * @brief Read a binary bootenv file.
+ * @param fp   File pointer to input stream.
+ * @param env  bootenv handle.
+ * @param size  maximum data size.
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read(FILE* fp, bootenv_t env, size_t size);
+
+/**
+ * @param ret_crc  return value of crc of read data
+ */
+int bootenv_read_crc(FILE* fp, bootenv_t env, size_t size, uint32_t *ret_crc);
+
+/**
+ * @brief Read bootenv data from an text/ascii file.
+ * @param fp   File pointer to ascii PDD file.
+ * @param env  bootenv handle
+ * @return 0
+ * @return or ERROR.
+ */
+int bootenv_read_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Write a bootenv structure to the given location (binary).
+ * @param fp   Filepointer to binary file.
+ * @param env  Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write(FILE* fp, bootenv_t env);
+
+/**
+ * @param ret_crc  return value of crc of read data
+ */
+int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc);
+
+/**
+ * @brief Write a bootenv structure to the given location (text).
+ * @param fp   Filepointer to text file.
+ * @param env  Bootenv structure which shall be written.
+ * @return 0
+ * @return or error
+ */
+int bootenv_write_txt(FILE* fp, bootenv_t env);
+
+/**
+ * @brief Compare bootenvs using memcmp().
+ * @param first        First bootenv.
+ * @param second       Second bootenv.
+ * @return 0 if bootenvs are equal
+ * @return < 0 if error
+ * @return > 0 if unequal
+ */
+int bootenv_compare(bootenv_t first, bootenv_t second);
+
+/**
+ * @brief Prototype for a PDD handling funtion
+ */
+
+/**
+ * @brief The PDD keep operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of PDD keep.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ *       PDD documentation.
+ */
+int bootenv_pdd_keep(bootenv_t env_old, bootenv_t env_new,
+               bootenv_t *env_res, int *warnings,
+               char *err_buf, size_t err_buf_size);
+
+
+/**
+ * @brief The PDD merge operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of merge-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ *       PDD documentation.
+ */
+int bootenv_pdd_merge(bootenv_t env_old, bootenv_t env_new,
+               bootenv_t *env_res, int *warnings,
+               char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief The PDD overwrite operation.
+ * @param env_old The old bootenv structure.
+ * @param env_new The new bootenv structure.
+ * @param env_res The result of overwrite-pdd.
+ * @param warnings A flag which marks any warnings.
+ * @return 0
+ * @return or error
+ * @note For a complete documentation about the algorithm confer the
+ *       PDD documentation.
+ */
+int bootenv_pdd_overwrite(bootenv_t env_new,
+               bootenv_t env_old, bootenv_t *env_res, int *warnings,
+               char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Dump a bootenv structure to stdout. (Debug)
+ * @param env  Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_dump(bootenv_t env);
+
+/**
+ * @brief Validate a bootenv structure.
+ * @param env Handle to a bootenv structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_valid(bootenv_t env);
+
+/**
+ * @brief Create a new bootenv list structure.
+ * @return NULL on error
+ * @return or a new list handle.
+ * @note This structure is used to store values in a list.
+ *       A useful addition when handling PDD strings.
+ */
+int bootenv_list_create(bootenv_list_t *list);
+
+/**
+ * @brief Destroy a bootenv list structure
+ * @param list Handle to a bootenv list structure.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_destroy(bootenv_list_t *list);
+
+/**
+ * @brief Import a list from a comma seperated string
+ * @param list Handle to a bootenv list structure.
+ * @param str          Comma seperated string list.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_import(bootenv_list_t list, const char *str);
+
+/**
+ * @brief Export a list to a string of comma seperated values.
+ * @param list Handle to a bootenv list structure.
+ * @return NULL one error
+ * @return or pointer to a newly allocated string.
+ * @warning Free the allocated memory by yourself!
+ */
+int bootenv_list_export(bootenv_list_t list, char **string);
+
+/**
+ * @brief Add an item to the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_add(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Remove an item from the list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 0
+ * @return or error
+ */
+int bootenv_list_remove(bootenv_list_t list, const char *item);
+
+/**
+ * @brief Check if a given item is in a given list.
+ * @param list A handle of a list structure.
+ * @param item An item.
+ * @return 1 Item is in list.
+ * @return 0 Item is not in list.
+ */
+int bootenv_list_is_in(bootenv_list_t list, const char *item);
+
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_vector(bootenv_list_t list, size_t *size,
+                          const char ***vector);
+
+/**
+ * @brief Convert a list into a vector of all values inside the list.
+ * @param list Handle to a bootenv structure.
+ * @param size The size of the allocated vector structure.
+ * @return 0
+ * @return or error
+ * @warning Free the allocate memory yourself!
+ */
+int bootenv_list_to_num_vector(bootenv_list_t list, size_t *size,
+                                       uint32_t **vector);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*__BOOTENV_H__ */
diff --git a/ubi-utils/old-tools/src/common.c b/ubi-utils/old-tools/src/common.c
new file mode 100644 (file)
index 0000000..7ed1ade
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) Artem Bityutskiy, 2007
+ *
+ * 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.
+ */
+
+/*
+ * This file contains various common stuff used by UBI utilities.
+ *
+ * Author: Artem Bityutskiy
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * ubiutils_bytes_multiplier - convert size specifier to an integer
+ *                             multiplier.
+ *
+ * @str: the size specifier string
+ *
+ * This function parses the @str size specifier, which may be one of
+ * 'KiB', 'MiB', or 'GiB' into an integer multiplier. Returns positive
+ * size multiplier in case of success and %-1 in case of failure.
+ */
+int ubiutils_get_multiplier(const char *str)
+{
+       if (!str)
+               return 1;
+
+       /* Remove spaces before the specifier */
+       while (*str == ' ' || *str == '\t')
+               str += 1;
+
+       if (!strcmp(str, "KiB"))
+               return 1024;
+       if (!strcmp(str, "MiB"))
+               return 1024 * 1024;
+       if (!strcmp(str, "GiB"))
+               return 1024 * 1024 * 1024;
+
+       return -1;
+}
+
+/**
+ * ubiutils_print_bytes - print bytes.
+ * @bytes: variable to print
+ * @bracket: whether brackets have to be put or not
+ *
+ * This is a helper function which prints amount of bytes in a human-readable
+ * form, i.e., it prints the exact amount of bytes following by the approximate
+ * amount of Kilobytes, Megabytes, or Gigabytes, depending on how big @bytes
+ * is.
+ */
+void ubiutils_print_bytes(long long bytes, int bracket)
+{
+       const char *p;
+
+       if (bracket)
+               p = " (";
+       else
+               p = ", ";
+
+       printf("%lld bytes", bytes);
+
+       if (bytes > 1024 * 1024 * 1024)
+               printf("%s%.1f GiB", p, (double)bytes / (1024 * 1024 * 1024));
+       else if (bytes > 1024 * 1024)
+               printf("%s%.1f MiB", p, (double)bytes / (1024 * 1024));
+       else if (bytes > 1024)
+               printf("%s%.1f KiB", p, (double)bytes / 1024);
+       else
+               return;
+
+       if (bracket)
+               printf(")");
+}
diff --git a/ubi-utils/old-tools/src/common.h b/ubi-utils/old-tools/src/common.h
new file mode 100644 (file)
index 0000000..06ae623
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Artem Bityutskiy, 2007
+ *
+ * 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.
+ */
+
+#ifndef __UBI_UTILS_COMMON_H__
+#define __UBI_UTILS_COMMON_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Error messages */
+#define errmsg(fmt, ...) do {                                             \
+        fprintf(stderr, PROGRAM_NAME " error: " fmt "\n", ##__VA_ARGS__); \
+} while(0)
+
+/* Warnings */
+#define warnmsg(fmt, ...) do {                                              \
+        fprintf(stderr, PROGRAM_NAME " warning: " fmt "\n", ##__VA_ARGS__); \
+} while(0)
+
+int ubiutils_get_multiplier(const char *str);
+void ubiutils_print_bytes(long long bytes, int bracket);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__UBI_UTILS_COMMON_H__ */
diff --git a/ubi-utils/old-tools/src/config.h b/ubi-utils/old-tools/src/config.h
new file mode 100644 (file)
index 0000000..55e60f3
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Frank Haverkamp
+ */
+
+#define PACKAGE_BUGREPORT                                              \
+       "haver@vnet.ibm.com, dedekind@linutronix.de, or tglx@linutronix.de"
+
+#define ubi_unused __attribute__((unused))
+
+#endif
diff --git a/ubi-utils/old-tools/src/crc32.c b/ubi-utils/old-tools/src/crc32.c
new file mode 100644 (file)
index 0000000..666e217
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Thomas Gleixner
+ */
+
+/*
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+
+#include <stdint.h>
+#include <crc32.h>
+
+/* CRC polynomial */
+#define CRC_POLY       0xEDB88320
+
+/**
+ * init_crc32_table - Initialize crc table
+ *
+ * @table:     pointer to the CRC table which must be initialized
+ *
+ * Create CRC32 table for given polynomial. The table is created with
+ * the lowest order term in the highest order bit. So the x^32 term
+ * has to implied in the crc calculation function.
+ */
+void init_crc32_table(uint32_t *table)
+{
+       uint32_t crc;
+       int i, j;
+
+       for (i = 0; i < 256; i++) {
+               crc = i;
+               for (j = 8; j > 0; j--) {
+                       if (crc & 1)
+                               crc = (crc >> 1) ^ CRC_POLY;
+                       else
+                               crc >>= 1;
+               }
+               table[i] = crc;
+       }
+}
+
+/**
+ * clc_crc32 - Calculate CRC32 over a buffer
+ *
+ * @table:     pointer to the CRC table
+ * @crc:       initial crc value
+ * @buf:       pointer to the buffer
+ * @len:       number of bytes to calc
+ *
+ * Returns the updated crc value.
+ *
+ * The algorithm resembles a hardware shift register, but calculates 8
+ * bit at once.
+ */
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf,
+                  int len)
+{
+       const unsigned char *p = buf;
+
+       while(--len >= 0)
+               crc = table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+       return crc;
+}
diff --git a/ubi-utils/old-tools/src/crc32.h b/ubi-utils/old-tools/src/crc32.h
new file mode 100644 (file)
index 0000000..31362b0
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __CRC32_H__
+#define __CRC32_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Thomas Gleixner
+ *
+ * CRC32 functions
+ *
+ * Can be compiled as seperate object, but is included into the ipl source
+ * so gcc can inline the functions. We optimize for size so the omission of
+ * the function frame is helpful.
+ *
+ */
+#include <stdint.h>
+
+void init_crc32_table(uint32_t *table);
+uint32_t clc_crc32(uint32_t *table, uint32_t crc, void *buf, int len);
+
+#endif /* __CRC32_H__ */
diff --git a/ubi-utils/old-tools/src/eb_chain.c b/ubi-utils/old-tools/src/eb_chain.c
new file mode 100644 (file)
index 0000000..a018ae6
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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:  Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ */
+
+/* see eb_chain.h */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define COPY(dst, src)                                                 \
+       do {                                                            \
+               dst = malloc(sizeof(*dst));                             \
+               if (dst == NULL)                                        \
+                       return -ENOMEM;                                 \
+               memcpy(dst, src, sizeof(*dst));                         \
+       } while (0)
+
+
+/**
+ * inserts an eb_info into the chain starting at head, then searching
+ * linearly for the correct position;
+ * new should contain valid vid and ec headers and the data_crc should
+ * already have been checked before insertion, otherwise the chain
+ * could be have un an undesired manner;
+ * returns -ENOMEM if alloc fails, otherwise SHOULD always return 0,
+ * if not, the code reached the last line and returned -EAGAIN,
+ * meaning there is a bug or a case not being handled here;
+ **/
+int
+eb_chain_insert(struct eb_info **head, struct eb_info *new)
+{
+       uint32_t vol, num, ver;
+       uint32_t new_vol, new_num, new_ver;
+       struct eb_info *prev, *cur, *hist, *ins;
+       struct eb_info **prev_ptr;
+
+       if ((head == NULL) || (new == NULL))
+               return 0;
+
+       if (*head == NULL) {
+               COPY(*head, new);
+               (*head)->next = NULL;
+               return 0;
+       }
+
+       new_vol = __be32_to_cpu(new->vid.vol_id);
+       new_num = __be32_to_cpu(new->vid.lnum);
+       new_ver = __be32_to_cpu(new->vid.leb_ver);
+
+       /** TRAVERSE HORIZONTALY **/
+
+       cur = *head;
+       prev = NULL;
+
+       /* traverse until vol_id/lnum align */
+       vol = __be32_to_cpu(cur->vid.vol_id);
+       num = __be32_to_cpu(cur->vid.lnum);
+       while ((new_vol > vol) || ((new_vol == vol) && (new_num > num))) {
+               /* insert new at end of chain */
+               if (cur->next == NULL) {
+                       COPY(ins, new);
+                       ins->next = NULL;
+                       cur->next = ins;
+                       return 0;
+               }
+
+               prev = cur;
+               cur = cur->next;
+               vol = __be32_to_cpu(cur->vid.vol_id);
+               num = __be32_to_cpu(cur->vid.lnum);
+       }
+
+       if (prev == NULL)
+               prev_ptr = head;
+       else
+               prev_ptr = &(prev->next);
+
+       /* insert new into the middle of chain */
+       if ((new_vol != vol) || (new_num != num)) {
+               COPY(ins, new);
+               ins->next = cur;
+               *prev_ptr = ins;
+               return 0;
+       }
+
+       /** TRAVERSE VERTICALY **/
+
+       hist = cur;
+       prev = NULL;
+
+       /* traverse until versions align */
+       ver = __be32_to_cpu(cur->vid.leb_ver);
+       while (new_ver < ver) {
+               /* insert new at bottom of history */
+               if (hist->older == NULL) {
+                       COPY(ins, new);
+                       ins->next = NULL;
+                       ins->older = NULL;
+                       hist->older = ins;
+                       return 0;
+               }
+
+               prev = hist;
+               hist = hist->older;
+               ver = __be32_to_cpu(hist->vid.leb_ver);
+       }
+
+       if (prev == NULL) {
+               /* replace active version */
+               COPY(ins, new);
+               ins->next = hist->next;
+               *prev_ptr = ins;
+
+               /* place cur in vertical histroy */
+               ins->older = hist;
+               hist->next = NULL;
+               return 0;
+       }
+
+       /* insert between versions, beneath active version */
+       COPY(ins, new);
+       ins->next = NULL;
+       ins->older = prev->older;
+       prev->older = ins;
+       return 0;
+}
+
+
+/**
+ * sets the pointer at pos to the position of the first entry in the chain
+ * with of vol_id and, if given, with the same lnum as *lnum;
+ * if there is no entry in the chain, then *pos is NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+                 struct eb_info **pos)
+{
+       uint32_t vol, num;
+       struct eb_info *cur;
+
+       if ((head == NULL) || (*head == NULL) || (pos == NULL))
+               return 0;
+
+       *pos = NULL;
+
+       cur = *head;
+       while (cur != NULL) {
+               vol = __be32_to_cpu(cur->vid.vol_id);
+               num = __be32_to_cpu(cur->vid.lnum);
+
+               if ((vol_id == vol) && ((lnum == NULL) || (*lnum == num))) {
+                       *pos = cur;
+                       return 0;
+               }
+
+               cur = cur->next;
+       }
+
+       return 0;
+}
+
+
+/**
+ * prints to stream, the vol_id, lnum and leb_ver for each entry in the
+ * chain, starting at head;
+ * this is intended for debuging purposes;
+ * always returns 0;
+ *
+ * FIXME I do not like the double list traversion ...
+ **/
+int
+eb_chain_print(FILE* stream, struct eb_info *head)
+{
+       struct eb_info *cur;
+
+       if (stream == NULL)
+               stream = stdout;
+
+       if (head == NULL) {
+               fprintf(stream, "EMPTY\n");
+               return 0;
+       }
+       /*               012345678012345678012345678012301230123 0123 01234567 0123457 01234567*/
+       fprintf(stream, "VOL_ID   LNUM     LEB_VER  EC  VID DAT  PBLK PADDR    DSIZE   EC\n");
+       cur = head;
+       while (cur != NULL) {
+               struct eb_info *hist;
+
+               fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+                       __be32_to_cpu(cur->vid.vol_id),
+                       __be32_to_cpu(cur->vid.lnum),
+                       __be32_to_cpu(cur->vid.leb_ver),
+                       cur->ec_crc_ok   ? "ok":"bad",
+                       cur->vid_crc_ok  ? "ok":"bad");
+               if (cur->vid.vol_type == UBI_VID_STATIC)
+                       fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"bad");
+               else    fprintf(stream, "%-4s", cur->data_crc_ok ? "ok":"ign");
+               fprintf(stream, " %-4d %08x %-8u %-8llu\n", cur->phys_block,
+                       cur->phys_addr, __be32_to_cpu(cur->vid.data_size),
+                       __be64_to_cpu(cur->ec.ec));
+
+               hist = cur->older;
+               while (hist != NULL) {
+                       fprintf(stream, "%08x %-8u %08x %-4s%-4s",
+                               __be32_to_cpu(hist->vid.vol_id),
+                               __be32_to_cpu(hist->vid.lnum),
+                               __be32_to_cpu(hist->vid.leb_ver),
+                               hist->ec_crc_ok   ? "ok":"bad",
+                               hist->vid_crc_ok  ? "ok":"bad");
+                       if (hist->vid.vol_type == UBI_VID_STATIC)
+                               fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"bad");
+                       else    fprintf(stream, "%-4s", hist->data_crc_ok ? "ok":"ign");
+                       fprintf(stream, " %-4d %08x %-8u %-8llu (*)\n",
+                               hist->phys_block, hist->phys_addr,
+                               __be32_to_cpu(hist->vid.data_size),
+                               __be64_to_cpu(hist->ec.ec));
+
+                       hist = hist->older;
+               }
+               cur = cur->next;
+       }
+
+       return 0;
+}
+
+
+/**
+ * frees the memory of the entire chain, starting at head;
+ * head will be NULL on return;
+ * always returns 0;
+ **/
+int
+eb_chain_destroy(struct eb_info **head)
+{
+       if (head == NULL)
+               return 0;
+
+       while (*head != NULL) {
+               struct eb_info *cur;
+               struct eb_info *hist;
+
+               cur = *head;
+               *head = (*head)->next;
+
+               hist = cur->older;
+               while (hist != NULL) {
+                       struct eb_info *temp;
+
+                       temp = hist;
+                       hist = hist->older;
+                       free(temp);
+               }
+               free(cur);
+       }
+       return 0;
+}
+
diff --git a/ubi-utils/old-tools/src/ecclayouts.h b/ubi-utils/old-tools/src/ecclayouts.h
new file mode 100644 (file)
index 0000000..a1c7823
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef __ECCLAYOUTS_H__
+#define __ECCLAYOUTS_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2007
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+#include <mtd/mtd-abi.h>
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_ecclayout mtd_nand_oob_16 = {
+       .eccbytes = 6,
+       .eccpos = { 0, 1, 2, 3, 6, 7 },
+       .oobfree = {{ .offset = 8, .length = 8 }}
+};
+
+static struct nand_ecclayout mtd_nand_oob_64 = {
+       .eccbytes = 24,
+       .eccpos = { 40, 41, 42, 43, 44, 45, 46, 47,
+                   48, 49, 50, 51, 52, 53, 54, 55,
+                   56, 57, 58, 59, 60, 61, 62, 63 },
+       .oobfree = {{ .offset = 2, .length = 38 }}
+};
+
+/* Define IBM oob placement schemes */
+static struct nand_ecclayout ibm_nand_oob_16 = {
+       .eccbytes = 6,
+       .eccpos = { 9, 10, 11, 13, 14, 15 },
+       .oobfree = {{ .offset = 8, .length = 8 }}
+};
+
+static struct nand_ecclayout ibm_nand_oob_64 = {
+       .eccbytes = 24,
+       .eccpos = { 33, 34, 35, 37, 38, 39, 41, 42,
+                   43, 45, 46, 47, 49, 50, 51, 53,
+                   54, 55, 57, 58, 59, 61, 62, 63 },
+       .oobfree = {{ .offset = 2, .length = 30 }}
+};
+
+struct oob_placement {
+       const char *name;
+       struct nand_ecclayout *nand_oob[2];
+};
+
+static struct oob_placement oob_placement[] = {
+       { .name = "IBM",
+         .nand_oob = { &ibm_nand_oob_16, &ibm_nand_oob_64 }},
+       { .name = "MTD",
+         .nand_oob = { &mtd_nand_oob_16, &mtd_nand_oob_64 }},
+};
+
+#endif
diff --git a/ubi-utils/old-tools/src/error.c b/ubi-utils/old-tools/src/error.c
new file mode 100644 (file)
index 0000000..4aaedad
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <sys/errno.h>
+#include <string.h>
+#include "error.h"
+
+#define MAXLINE 4096
+#define MAXWIDTH 80
+
+static FILE *logfp = NULL;
+
+static void err_doit(int, int, const char *, va_list);
+
+int
+read_procfile(FILE *fp_out, const char *procfile)
+{
+       FILE *fp;
+
+       if (!fp_out)
+               return -ENXIO;
+
+       fp = fopen(procfile, "r");
+       if (!fp)
+               return -ENOENT;
+
+       while(!feof(fp)) {
+               int c = fgetc(fp);
+
+               if (c == EOF)
+                       return 0;
+
+               if (putc(c, fp_out) == EOF)
+                       return -EIO;
+
+               if (ferror(fp))
+                       return -EIO;
+       }
+       return fclose(fp);
+}
+
+void
+error_initlog(const char *logfile)
+{
+       if (!logfile)
+               return;
+
+       logfp = fopen(logfile, "a+");
+       read_procfile(logfp, "/proc/cpuinfo");
+}
+
+void
+info_msg(const char *fmt, ...)
+{
+       FILE* fpout;
+       char buf[MAXLINE + 1];
+       va_list ap;
+       int n;
+
+       fpout = stdout;
+
+       va_start(ap, fmt);
+       vsnprintf(buf, MAXLINE, fmt, ap);
+       n = strlen(buf);
+       strcat(buf, "\n");
+
+       fputs(buf, fpout);
+       fflush(fpout);
+       if (fpout != stdout)
+               fclose(fpout);
+
+       va_end(ap);
+       return;
+}
+
+void
+__err_ret(const char *fmt, ...)
+{
+       va_list         ap;
+
+       va_start(ap, fmt);
+       err_doit(1, LOG_INFO, fmt, ap);
+       va_end(ap);
+       return;
+}
+
+void
+__err_sys(const char *fmt, ...)
+{
+       va_list         ap;
+
+       va_start(ap, fmt);
+       err_doit(1, LOG_ERR, fmt, ap);
+       va_end(ap);
+       exit(EXIT_FAILURE);
+}
+
+
+void
+__err_msg(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       err_doit(0, LOG_INFO, fmt, ap);
+       va_end(ap);
+
+       return;
+}
+
+void
+__err_quit(const char *fmt, ...)
+{
+       va_list         ap;
+
+       va_start(ap, fmt);
+       err_doit(0, LOG_ERR, fmt, ap);
+       va_end(ap);
+       exit(EXIT_FAILURE);
+}
+
+void
+__err_dump(const char *fmt, ...)
+{
+       va_list         ap;
+
+       va_start(ap, fmt);
+       err_doit(1, LOG_ERR, fmt, ap);
+       va_end(ap);
+       abort();                /* dump core and terminate */
+       exit(EXIT_FAILURE);     /* shouldn't get here */
+}
+
+/**
+ * If a logfile is used we must not print on stderr and stdout
+ * anymore. Since pfilfash might be used in a server context, it is
+ * even dangerous to write to those descriptors.
+ */
+static void
+err_doit(int errnoflag, int level __attribute__((unused)),
+        const char *fmt, va_list ap)
+{
+       FILE* fpout;
+       int errno_save, n;
+       char buf[MAXLINE + 1];
+       fpout = stderr;
+
+       errno_save = errno; /* value caller might want printed */
+
+       vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
+
+       n = strlen(buf);
+
+       if (errnoflag)
+               snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
+       strcat(buf, "\n");
+
+       if (logfp) {
+               fputs(buf, logfp);
+               fflush(logfp);
+               return;         /* exit when logging completes */
+       }
+
+       if (fpout == stderr) {
+               /* perform line wrap when outputting to stderr */
+               int word_len, post_len, chars;
+               char *buf_ptr;
+               const char *frmt = "%*s%n %n";
+
+               chars = 0;
+               buf_ptr = buf;
+               while (sscanf(buf_ptr, frmt, &word_len, &post_len) != EOF) {
+                       int i;
+                       char word[word_len + 1];
+                       char post[post_len + 1];
+
+                       strncpy(word, buf_ptr, word_len);
+                       word[word_len] = '\0';
+                       buf_ptr += word_len;
+                       post_len -= word_len;
+
+                       if (chars + word_len > MAXWIDTH) {
+                               fputc('\n', fpout);
+                               chars = 0;
+                       }
+                       fputs(word, fpout);
+                       chars += word_len;
+
+                       if (post_len > 0) {
+                               strncpy(post, buf_ptr, post_len);
+                               post[post_len] = '\0';
+                               buf_ptr += post_len;
+                       }
+                       for (i = 0; i < post_len; i++) {
+                               int inc = 1, chars_new;
+
+                               if (post[i] == '\t')
+                                       inc = 8;
+                               if (post[i] == '\n') {
+                                       inc = 0;
+                                       chars_new = 0;
+                               } else
+                                       chars_new = chars + inc;
+
+                               if (chars_new > MAXWIDTH) {
+                                       fputc('\n', fpout);
+                                       chars_new = inc;
+                               }
+                               fputc(post[i], fpout);
+                               chars = chars_new;
+                       }
+               }
+       }
+       else
+               fputs(buf, fpout);
+       fflush(fpout);
+       if (fpout != stderr)
+               fclose(fpout);
+
+       return;
+}
diff --git a/ubi-utils/old-tools/src/error.h b/ubi-utils/old-tools/src/error.h
new file mode 100644 (file)
index 0000000..05d8078
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef __ERROR_H__
+#define __ERROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+void error_initlog(const char *logfile);
+int read_procfile(FILE *fp_out, const char *procfile);
+
+void __err_ret(const char *fmt, ...);
+void __err_sys(const char *fmt, ...);
+void __err_msg(const char *fmt, ...);
+void __err_quit(const char *fmt, ...);
+void __err_dump(const char *fmt, ...);
+
+void info_msg(const char *fmt, ...);
+
+#ifdef DEBUG
+#define __loc_msg(str) do {                                    \
+       __err_msg("[%s. FILE: %s FUNC: %s LINE: %d]\n",         \
+               str, __FILE__, __FUNCTION__, __LINE__);         \
+} while (0)
+#else
+#define __loc_msg(str)
+#endif
+
+
+#define err_dump(fmt, ...) do {                                        \
+       __loc_msg("ErrDump");                                   \
+       __err_dump(fmt, ##__VA_ARGS__);                         \
+} while (0)
+
+#define err_quit(fmt, ...) do {                                        \
+       __loc_msg("ErrQuit");                                   \
+       __err_quit(fmt, ##__VA_ARGS__);                         \
+} while (0)
+
+
+#define err_ret(fmt, ...) do {                                 \
+       __loc_msg("ErrRet");                                    \
+       __err_ret(fmt, ##__VA_ARGS__);                          \
+} while (0)
+
+#define err_sys(fmt, ...) do {                                 \
+       __loc_msg("ErrSys");                                    \
+       __err_sys(fmt, ##__VA_ARGS__);                          \
+} while (0)
+
+#define err_msg(fmt, ...) do {                                 \
+       __loc_msg("ErrMsg");                                    \
+       __err_msg(fmt, ##__VA_ARGS__);                          \
+} while (0)
+
+#define log_msg(fmt, ...) do {                                 \
+               /* __loc_msg("LogMsg"); */                      \
+       __err_msg(fmt, ##__VA_ARGS__);                          \
+} while (0)
+
+#ifdef DEBUG
+#define dbg_msg(fmt, ...) do {                                 \
+       __loc_msg("DbgMsg");                                    \
+       __err_msg(fmt, ##__VA_ARGS__);                          \
+} while (0)
+#else
+#define dbg_msg(fmt, ...) do {} while (0)
+#endif
+
+#endif /* __ERROR_H__ */
diff --git a/ubi-utils/old-tools/src/example_ubi.h b/ubi-utils/old-tools/src/example_ubi.h
new file mode 100644 (file)
index 0000000..23c7b54
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __EXAMPLE_UBI_H__
+#define __EXAMPLE_UBI_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/**
+ * Defaults for our cards.
+ */
+#define EXAMPLE_UBI_DEVICE      0
+#define EXAMPLE_BOOTENV_VOL_ID_1 4
+#define EXAMPLE_BOOTENV_VOL_ID_2 5
+
+#endif /* __EXAMPLE_UBI_H__ */
diff --git a/ubi-utils/old-tools/src/hashmap.c b/ubi-utils/old-tools/src/hashmap.c
new file mode 100644 (file)
index 0000000..3511d56
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "hashmap.h"
+#define DEFAULT_BUCKETS 4096
+
+#if 0
+#define INFO_MSG(fmt...) do {  \
+       info_msg(fmt);          \
+} while (0)
+#else
+#define INFO_MSG(fmt...)
+#endif
+
+struct hashentry {
+       char* key;      /* key '0' term. str */
+       char* value;    /* payload '0' term. str */
+
+       hashentry_t next;
+};
+
+struct hashmap {
+       size_t entries;     /* current #entries */
+       size_t maxsize;     /* no. of hash buckets */
+       hashentry_t* data;  /* array of buckets */
+};
+
+static int
+is_empty(hashentry_t l)
+{
+       return l == NULL ? 1 : 0;
+}
+
+hashmap_t
+hashmap_new(void)
+{
+       hashmap_t res;
+       res = (hashmap_t) calloc(1, sizeof(struct hashmap));
+
+       if (res == NULL)
+               return NULL;
+
+       res->maxsize = DEFAULT_BUCKETS;
+       res->entries = 0;
+
+       res->data   = (hashentry_t*)
+               calloc(1, res->maxsize * sizeof(struct hashentry));
+
+       if (res->data == NULL)
+               return NULL;
+
+       return res;
+}
+
+static hashentry_t
+new_entry(const char* key, const char* value)
+{
+       hashentry_t res;
+
+       res = (hashentry_t) calloc(1, sizeof(struct hashentry));
+
+       if (res == NULL)
+               return NULL;
+
+       /* allocate key and value and copy them */
+       res->key = strdup(key);
+       if (res->key == NULL) {
+               free(res);
+               return NULL;
+       }
+
+       res->value = strdup(value);
+       if (res->value == NULL) {
+               free(res->key);
+               free(res);
+               return NULL;
+       }
+
+       res->next = NULL;
+
+       return res;
+}
+
+static hashentry_t
+free_entry(hashentry_t e)
+{
+       if (!is_empty(e)) {
+               if(e->key != NULL) {
+                       free(e->key);
+               }
+               if(e->value != NULL)
+                       free(e->value);
+               free(e);
+       }
+
+       return NULL;
+}
+
+static hashentry_t
+remove_entry(hashentry_t l, const char* key, size_t* entries)
+{
+       hashentry_t lnext;
+       if (is_empty(l))
+               return NULL;
+
+       if(strcmp(l->key,key) == 0) {
+               lnext = l->next;
+               l = free_entry(l);
+               (*entries)--;
+               return lnext;
+       }
+
+       l->next = remove_entry(l->next, key, entries);
+
+       return l;
+}
+
+static hashentry_t
+insert_entry(hashentry_t l, hashentry_t e, size_t* entries)
+{
+       if (is_empty(l)) {
+               (*entries)++;
+               return e;
+       }
+
+       /* check for update */
+       if (strcmp(l->key, e->key) == 0) {
+               e->next = l->next;
+               l = free_entry(l);
+               return e;
+       }
+
+       l->next = insert_entry(l->next, e, entries);
+       return l;
+}
+
+static hashentry_t
+remove_all(hashentry_t l, size_t* entries)
+{
+       hashentry_t lnext;
+       if (is_empty(l))
+               return NULL;
+
+       lnext = l->next;
+       free_entry(l);
+       (*entries)--;
+
+       return remove_all(lnext, entries);
+}
+
+static const char*
+value_lookup(hashentry_t l, const char* key)
+{
+       if (is_empty(l))
+               return NULL;
+
+       if (strcmp(l->key, key) == 0)
+               return l->value;
+
+       return value_lookup(l->next, key);
+}
+
+static void
+print_all(hashentry_t l)
+{
+       if (is_empty(l)) {
+               printf("\n");
+               return;
+       }
+
+       printf("%s=%s", l->key, l->value);
+       if (!is_empty(l->next)) {
+               printf(",");
+       }
+
+       print_all(l->next);
+}
+
+static void
+keys_to_array(hashentry_t l, const char** a, size_t* i)
+{
+       if (is_empty(l))
+               return;
+
+       a[*i] = l->key;
+       (*i)++;
+
+       keys_to_array(l->next, a, i);
+}
+
+uint32_t
+hash_str(const char* str, uint32_t mapsize)
+{
+       uint32_t hash = 0;
+       uint32_t x    = 0;
+       uint32_t i    = 0;
+       size_t   len  = strlen(str);
+
+       for(i = 0; i < len; str++, i++) {
+               hash = (hash << 4) + (*str);
+               if((x = hash & 0xF0000000L) != 0) {
+                       hash ^= (x >> 24);
+                       hash &= ~x;
+               }
+       }
+
+       return (hash & 0x7FFFFFFF) % mapsize;
+}
+
+
+int
+hashmap_is_empty(hashmap_t map)
+{
+       if (map == NULL)
+               return -EINVAL;
+
+       return map->entries > 0 ? 1 : 0;
+}
+
+const char*
+hashmap_lookup(hashmap_t map, const char* key)
+{
+       uint32_t i;
+
+       if ((map == NULL) || (key == NULL))
+               return NULL;
+
+       i = hash_str(key, map->maxsize);
+
+       return value_lookup(map->data[i], key);
+}
+
+int
+hashmap_add(hashmap_t map, const char* key, const char* value)
+{
+       uint32_t i;
+       hashentry_t entry;
+
+       if ((map == NULL) || (key == NULL) || (value == NULL))
+               return -EINVAL;
+
+       i = hash_str(key, map->maxsize);
+       entry = new_entry(key, value);
+       if (entry == NULL)
+               return -ENOMEM;
+
+       map->data[i] = insert_entry(map->data[i],
+                       entry, &map->entries);
+
+       INFO_MSG("HASH_ADD: chain[%d] key:%s val:%s",i,  key, value);
+       return 0;
+}
+
+int
+hashmap_remove(hashmap_t map, const char* key)
+{
+       uint32_t i;
+
+       if ((map == NULL) || (key == NULL))
+               return -EINVAL;
+
+       i = hash_str(key, map->maxsize);
+       map->data[i] = remove_entry(map->data[i], key, &map->entries);
+
+       return 0;
+}
+
+size_t
+hashmap_size(hashmap_t map)
+{
+       if (map != NULL)
+               return map->entries;
+       else
+               return 0;
+}
+
+int
+hashmap_free(hashmap_t map)
+{
+       size_t i;
+
+       if (map == NULL)
+               return -EINVAL;
+
+       /* "children" first */
+       for(i = 0; i < map->maxsize; i++) {
+               map->data[i] = remove_all(map->data[i], &map->entries);
+       }
+       free(map->data);
+       free(map);
+
+       return 0;
+}
+
+int
+hashmap_dump(hashmap_t map)
+{
+       size_t i;
+       if (map == NULL)
+               return -EINVAL;
+
+       for(i = 0; i < map->maxsize; i++) {
+               if (map->data[i] != NULL) {
+                       printf("[%zd]: ", i);
+                       print_all(map->data[i]);
+               }
+       }
+
+       return 0;
+}
+
+static const char**
+sort_key_vector(const char** a, size_t size)
+{
+       /* uses bubblesort */
+       size_t i, j;
+       const char* tmp;
+
+       if (size <= 0)
+               return a;
+
+       for (i = size - 1; i > 0; i--) {
+               for (j = 0; j < i; j++) {
+                       if (strcmp(a[j], a[j+1]) > 0) {
+                               tmp  = a[j];
+                               a[j] = a[j+1];
+                               a[j+1] = tmp;
+                       }
+               }
+       }
+       return a;
+}
+
+const char**
+hashmap_get_key_vector(hashmap_t map, size_t* size, int sort)
+{
+       const char** res;
+       size_t i, j;
+       *size = map->entries;
+
+       res = (const char**) malloc(*size * sizeof(char*));
+       if (res == NULL)
+               return NULL;
+
+       j = 0;
+       for(i=0; i < map->maxsize; i++) {
+               keys_to_array(map->data[i], res, &j);
+       }
+
+       if (sort)
+               res = sort_key_vector(res, *size);
+
+       return res;
+}
+
+int
+hashmap_key_is_in_vector(const char** vec, size_t size, const char* key)
+{
+       size_t i;
+       for (i = 0; i < size; i++) {
+               if (strcmp(vec[i], key) == 0) /* found */
+                       return 1;
+       }
+
+       return 0;
+}
+
+const char**
+hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+               const char** vec2, size_t vec2_size, size_t* res_size)
+{
+       const char** res;
+       size_t i, j;
+
+       *res_size = vec2_size;
+
+       res = (const char**) malloc(*res_size * sizeof(char*));
+       if (res == NULL)
+               return NULL;
+
+       /* get all keys from vec2 which are not set in vec1 */
+       j = 0;
+       for (i = 0; i < vec2_size; i++) {
+               if (!hashmap_key_is_in_vector(vec1, vec1_size, vec2[i]))
+                       res[j++] = vec2[i];
+       }
+
+       *res_size = j;
+       return res;
+}
diff --git a/ubi-utils/old-tools/src/hashmap.h b/ubi-utils/old-tools/src/hashmap.h
new file mode 100644 (file)
index 0000000..1b13e95
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef __HASHMAP_H__
+#define __HASHMAP_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+typedef struct hashentry *hashentry_t;
+typedef struct hashmap *hashmap_t;
+
+hashmap_t hashmap_new(void);
+int hashmap_free(hashmap_t map);
+
+int hashmap_add(hashmap_t map, const char* key, const char* value);
+int hashmap_update(hashmap_t map, const char* key, const char* value);
+int hashmap_remove(hashmap_t map, const char* key);
+const char* hashmap_lookup(hashmap_t map, const char* key);
+
+const char** hashmap_get_key_vector(hashmap_t map, size_t* size, int sort);
+int hashmap_key_is_in_vector(const char** vec, size_t size, const char* key);
+const char** hashmap_get_update_key_vector(const char** vec1, size_t vec1_size,
+               const char** vec2, size_t vec2_size, size_t* res_size);
+
+int hashmap_dump(hashmap_t map);
+
+int hashmap_is_empty(hashmap_t map);
+size_t hashmap_size(hashmap_t map);
+
+uint32_t hash_str(const char* str, uint32_t mapsize);
+
+#endif /* __HASHMAP_H__ */
diff --git a/ubi-utils/old-tools/src/libpfiflash.c b/ubi-utils/old-tools/src/libpfiflash.c
new file mode 100644 (file)
index 0000000..7e3d3b3
--- /dev/null
@@ -0,0 +1,1325 @@
+/*
+ * Copyright International Business Machines Corp., 2006, 2007
+ *
+ * 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.
+ */
+
+/*
+ * Authors: Oliver Lohmann <oliloh@de.ibm.com>
+ *         Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <anrez@de.ibm.com>
+ */
+
+/* TODO Compare data before writing it. This implies that the volume
+ * parameters are compared first: size, alignment, name, type, ...,
+ * this is the same, compare the data. Volume deletion is deffered
+ * until the difference has been found out.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define __USE_GNU
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+
+#include <libubi.h>
+#include <pfiflash.h>
+
+#include <mtd/ubi-user.h>      /* FIXME Is this ok here? */
+#include <mtd/mtd-user.h>
+
+#include "pfiflash_error.h"
+#include "ubimirror.h"
+#include "error.h"
+#include "reader.h"
+#include "example_ubi.h"
+#include "bootenv.h"
+
+/* ubi-header.h and crc32.h needed for CRC checking */
+#include <mtd/ubi-header.h>    /* FIXME Is this ok here? */
+#include "crc32.h"
+
+#define ubi_unused __attribute__((unused))
+
+#define COMPARE_BUFFER_SIZE 2048
+
+#define DEFAULT_DEV_PATTERN    "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN    "/dev/ubi%d_%d"
+
+static const char copyright [] ubi_unused =
+       "Copyright International Business Machines Corp., 2006, 2007";
+
+/* simply clear buffer, then write into front of it */
+#define EBUF(fmt...)                                                   \
+               snprintf(err_buf, err_buf_size, fmt);
+
+/* make a history of buffer and then prepend something in front */
+#define EBUF_PREPEND(fmt)                                              \
+       do {                                                            \
+               int EBUF_HISTORY_LENGTH = strlen(err_buf);              \
+               char EBUF_HISTORY[EBUF_HISTORY_LENGTH + 1];             \
+               strncpy(EBUF_HISTORY, err_buf, EBUF_HISTORY_LENGTH + 1);\
+               EBUF(fmt ": %s", EBUF_HISTORY);                         \
+       } while (0)
+
+/* An array of PDD function pointers indexed by the algorithm. */
+static pdd_func_t pdd_funcs[PDD_HANDLING_NUM]  =
+       {
+               &bootenv_pdd_keep,
+               &bootenv_pdd_merge,
+               &bootenv_pdd_overwrite
+       };
+
+typedef enum ubi_update_process_t {
+       UBI_REMOVE = 0,
+       UBI_WRITE,
+       UBI_COMPARE,
+} ubi_update_process_t;
+
+
+/**
+ * skip_raw_volumes - reads data from pfi to advance fp past raw block
+ * @pfi:       fp to pfi data
+ * @pfi_raws:  header information
+ *
+ * Error handling):
+ *     when early EOF in pfi data
+ *     - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ *     when file I/O error
+ *     - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ **/
+static int
+skip_raw_volumes(FILE* pfi, list_t pfi_raws,
+                 char* err_buf, size_t err_buf_size)
+{
+       int rc;
+       void *i;
+       list_t ptr;
+
+       if (is_empty(pfi_raws))
+               return 0;
+
+       rc = 0;
+       foreach(i, ptr, pfi_raws) {
+               size_t j;
+               pfi_raw_t raw;
+
+               raw = (pfi_raw_t)i;
+               for(j = 0; j < raw->data_size; j++) {
+                       int c;
+
+                       c = fgetc(pfi);
+                       if (c == EOF)
+                               rc = -PFIFLASH_ERR_EOF;
+                       else if (ferror(pfi))
+                               rc = -PFIFLASH_ERR_FIO;
+
+                       if (rc != 0)
+                               goto err;
+               }
+       }
+
+ err:
+       EBUF(PFIFLASH_ERRSTR[-rc]);
+       return rc;
+}
+
+
+/**
+ * my_ubi_mkvol - wraps the ubi_mkvol functions and impl. bootenv update hook
+ * @devno:     UBI device number.
+ * @s:         Current seqnum.
+ * @u:         Information about the UBI volume from the PFI.
+ *
+ * Error handling:
+ *     when UBI system couldn't be opened
+ *     - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ *     when UBI system couldn't create a volume
+ *     - returns -PFIFLASH_ERR_UBI_MKVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_mkvol(int devno, int s, pfi_ubi_t u,
+            char *err_buf, size_t err_buf_size)
+{
+       int rc, type;
+       char path[PATH_MAX];
+       libubi_t ulib;
+       struct ubi_mkvol_request req;
+
+       rc = 0;
+       ulib = NULL;
+
+       log_msg("[ ubimkvol id=%d, size=%d, data_size=%d, type=%d, "
+               "alig=%d, nlen=%d, name=%s",
+               u->ids[s], u->size, u->data_size, u->type, u->alignment,
+               strnlen(u->names[s], PFI_UBI_VOL_NAME_LEN), u->names[s]);
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       switch (u->type) {
+       case pfi_ubi_static:
+               type = UBI_STATIC_VOLUME; break;
+       case pfi_ubi_dynamic:
+       default:
+               type = UBI_DYNAMIC_VOLUME;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno);
+
+       req.vol_id = u->ids[s];
+       req.alignment = u->alignment;
+       req.bytes = u->size;
+       req.vol_type = type;
+       req.name = u->names[s];
+
+       rc = ubi_mkvol(ulib, path, &req);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_UBI_MKVOL;
+               EBUF(PFIFLASH_ERRSTR[-rc], u->ids[s]);
+               goto err;
+       }
+
+ err:
+       if (ulib != NULL)
+               libubi_close(ulib);
+
+       return rc;
+}
+
+
+/**
+ * my_ubi_rmvol - a wrapper around the UBI library function ubi_rmvol
+ * @devno      UBI device number
+ * @id         UBI volume id to remove
+ *
+ * If the volume does not exist, the function will return success.
+ *
+ * Error handling:
+ *     when UBI system couldn't be opened
+ *     - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ *     when UBI system couldn't update (truncate) a volume
+ *     - returns -PFIFLASH_ERR_UBI_VOL_UPDATE, err_buf matches text to err
+ *     when UBI system couldn't remove a volume
+ *     - returns -PFIFLASH_ERR_UBI_RMVOL, err_buf matches text to err
+ **/
+static int
+my_ubi_rmvol(int devno, uint32_t id,
+            char *err_buf, size_t err_buf_size)
+{
+       int rc, fd;
+       char path[PATH_MAX];
+       libubi_t ulib;
+
+       rc = 0;
+       ulib = NULL;
+
+       log_msg("[ ubirmvol id=%d", id);
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+       /* truncate whether it exist or not */
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               libubi_close(ulib);
+               return 0;       /* not existent, return 0 */
+       }
+
+       rc = ubi_update_start(ulib, fd, 0);
+       close(fd);
+       if (rc < 0) {
+               rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;       /* if EBUSY than empty device, continue */
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_DEV_PATTERN, devno);
+
+       rc = ubi_rmvol(ulib, path, id);
+       if (rc != 0) {
+#ifdef DEBUG
+               int rc_old = rc;
+               dbg_msg("Remove UBI volume %d returned with error: %d "
+                       "errno=%d", id, rc_old, errno);
+#endif
+
+               rc = -PFIFLASH_ERR_UBI_RMVOL;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+
+               /* TODO Define a ubi_rmvol return value which says
+                * sth like EUBI_NOSUCHDEV. In this case, a failed
+                * operation is acceptable. Everything else has to be
+                * classified as real error. But talk to Andreas Arnez
+                * before defining something odd...
+                */
+               /* if ((errno == EINVAL) || (errno == ENODEV))
+                  return 0; */ /* currently it is EINVAL or ENODEV */
+
+               goto err;
+       }
+
+ err:
+       if (ulib != NULL)
+               libubi_close(ulib);
+
+       return rc;
+}
+
+
+/**
+ * read_bootenv_volume - reads the current bootenv data from id into be_old
+ * @devno      UBI device number
+ * @id         UBI volume id to remove
+ * @bootenv_old        to hold old boot_env data
+ *
+ * Error handling:
+ *     when UBI system couldn't be opened
+ *     - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ *     when UBI system couldn't open a volume to read
+ *     - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ *     when couldn't read bootenv data
+ *     - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ **/
+static int
+read_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+                   char *err_buf, size_t err_buf_size)
+{
+       int rc;
+       FILE* fp_in;
+       char path[PATH_MAX];
+       libubi_t ulib;
+
+       rc = 0;
+       fp_in = NULL;
+       ulib = NULL;
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+       fp_in = fopen(path, "r");
+       if (!fp_in) {
+               rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+
+       log_msg("[ reading old bootenvs ...");
+
+       /* Save old bootenvs for reference */
+       rc = bootenv_read(fp_in, bootenv_old, BOOTENV_MAXSIZE);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_READ;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+ err:
+       if (fp_in)
+               fclose(fp_in);
+       if (ulib)
+               libubi_close(ulib);
+
+       return rc;
+}
+
+
+/**
+ * write_bootenv_volume - writes data from PFI file int to bootenv UBI volume
+ * @devno      UBI device number
+ * @id         UBI volume id
+ * @bootend_old        old PDD data from machine
+ * @pdd_f      function to handle PDD with
+ * @fp_in      new pdd data contained in PFI
+ * @fp_in_size data size of new pdd data in PFI
+ * @pfi_crc    crc value from PFI header
+ *
+ * Error handling:
+ *     when UBI system couldn't be opened
+ *     - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ *     when bootenv can't be created
+ *     - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ *     when bootenv can't be read
+ *     - returns -PFIFLASH_ERR_BOOTENV_READ, err_buf matches text to err
+ *     when PDD handling function returns and error
+ *     - passes rc and err_buf data
+ *     when CRC check fails
+ *     - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ *     when bootenv can't be resized
+ *     - returns -PFIFLASH_ERR_BOOTENV_SIZE, err_buf matches text to err
+ *     when UBI system couldn't open a volume
+ *     - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ *     when couldn't write bootenv data
+ *     - returns -PFIFLASH_ERR_BOOTENV_WRITE, err_buf matches text to err
+ **/
+static int
+write_bootenv_volume(int devno, uint32_t id, bootenv_t bootenv_old,
+                    pdd_func_t pdd_f, FILE* fp_in, size_t fp_in_size,
+                    uint32_t pfi_crc,
+                    char *err_buf, size_t err_buf_size)
+{
+       int rc, warnings, fd_out;
+       uint32_t crc;
+       char path[PATH_MAX];
+       size_t update_size;
+       FILE *fp_out;
+       bootenv_t bootenv_new, bootenv_res;
+       libubi_t ulib;
+
+       rc = 0;
+       warnings = 0;
+       crc = 0;
+       update_size = 0;
+       fp_out = NULL;
+       bootenv_new = NULL;
+       bootenv_res = NULL;
+       ulib = NULL;
+
+       log_msg("[ ubiupdatevol bootenv id=%d, fp_in=%p", id, fp_in);
+
+       /* Workflow:
+        * 1. Apply PDD operation and get the size of the returning
+        *    bootenv_res section. Without the correct size it wouldn't
+        *    be possible to call UBI update vol.
+        * 2. Call UBI update vol
+        * 3. Get FILE* to vol dev
+        * 4. Write to FILE*
+        */
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       rc = bootenv_create(&bootenv_new);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               EBUF(PFIFLASH_ERRSTR[-rc], " 'new'");
+               goto err;
+       }
+
+       rc = bootenv_create(&bootenv_res);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               EBUF(PFIFLASH_ERRSTR[-rc], " 'res'");
+               goto err;
+       }
+
+       rc = bootenv_read_crc(fp_in, bootenv_new, fp_in_size, &crc);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_READ;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       } else if (crc != pfi_crc) {
+               rc = -PFIFLASH_ERR_CRC_CHECK;
+               EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+               goto err;
+       }
+
+       rc = pdd_f(bootenv_old, bootenv_new, &bootenv_res, &warnings,
+                  err_buf, err_buf_size);
+       if (rc != 0) {
+               EBUF_PREPEND("handling PDD");
+               goto err;
+       }
+       else if (warnings)
+               /* TODO do something with warnings */
+               dbg_msg("A warning in the PDD operation occured: %d",
+                       warnings);
+
+       rc = bootenv_size(bootenv_res, &update_size);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_SIZE;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+       fd_out = open(path, O_RDWR);
+       if (fd_out < 0) {
+               rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+       fp_out = fdopen(fd_out, "r+");
+       if (!fp_out) {
+               rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+       rc = ubi_update_start(ulib, fd_out, update_size);
+       if (rc < 0) {
+               rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+
+       rc = bootenv_write(fp_out, bootenv_res);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_WRITE;
+               EBUF(PFIFLASH_ERRSTR[-rc], devno, id);
+               goto err;
+       }
+
+ err:
+       if (ulib != NULL)
+               libubi_close(ulib);
+       if (bootenv_new != NULL)
+               bootenv_destroy(&bootenv_new);
+       if (bootenv_res != NULL)
+               bootenv_destroy(&bootenv_res);
+       if (fp_out)
+               fclose(fp_out);
+
+       return rc;
+}
+
+
+/**
+ * write_normal_volume - writes data from PFI file int to regular UBI volume
+ * @devno      UBI device number
+ * @id         UBI volume id
+ * @update_size        size of data stream
+ * @fp_in      PFI data file pointer
+ * @pfi_crc    CRC data from PFI header
+ *
+ * Error handling:
+ *     when UBI system couldn't be opened
+ *     - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ *     when UBI system couldn't open a volume
+ *     - returns -PFIFLASH_ERR_UBI_VOL_FOPEN, err_buf matches text to err
+ *     when unexpected EOF is encountered
+ *     - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ *     when file I/O error
+ *     - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ *     when CRC check fails
+ *     - retruns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ **/
+static int
+write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in,
+                   uint32_t pfi_crc,
+                   char *err_buf, size_t err_buf_size)
+{
+       int rc, fd_out;
+       uint32_t crc, crc32_table[256];
+       char path[PATH_MAX];
+       size_t bytes_left;
+       FILE* fp_out;
+       libubi_t ulib;
+
+       rc = 0;
+       crc = UBI_CRC32_INIT;
+       bytes_left = update_size;
+       fp_out = NULL;
+       ulib = NULL;
+
+       log_msg("[ ubiupdatevol id=%d, update_size=%d fp_in=%p",
+               id, update_size, fp_in);
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+       fd_out = open(path, O_RDWR);
+       if (fd_out < 0) {
+               rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+       fp_out = fdopen(fd_out, "r+");
+       if (!fp_out) {
+               rc = -PFIFLASH_ERR_UBI_VOL_FOPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+       rc = ubi_update_start(ulib, fd_out, update_size);
+       if (rc < 0) {
+               rc = -PFIFLASH_ERR_UBI_VOL_UPDATE;
+               EBUF(PFIFLASH_ERRSTR[-rc], id);
+               goto err;
+       }
+
+       init_crc32_table(crc32_table);
+       while (bytes_left) {
+               char buf[1024];
+               size_t to_rw = sizeof buf > bytes_left ?
+                       bytes_left : sizeof buf;
+               if (fread(buf, 1, to_rw, fp_in) != to_rw) {
+                       rc = -PFIFLASH_ERR_EOF;
+                       EBUF(PFIFLASH_ERRSTR[-rc]);
+                       goto err;
+               }
+               crc = clc_crc32(crc32_table, crc, buf, to_rw);
+               if (fwrite(buf, 1, to_rw, fp_out) != to_rw) {
+                       rc = -PFIFLASH_ERR_FIO;
+                       EBUF(PFIFLASH_ERRSTR[-rc]);
+                       goto err;
+               }
+               bytes_left -= to_rw;
+       }
+
+       if (crc != pfi_crc) {
+               rc = -PFIFLASH_ERR_CRC_CHECK;
+               EBUF(PFIFLASH_ERRSTR[-rc], pfi_crc, crc);
+               goto err;
+       }
+
+ err:
+       if (fp_out)
+               fclose(fp_out);
+       if (ulib)
+               libubi_close(ulib);
+
+       return rc;
+}
+
+static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+               uint32_t data_size, pdd_func_t pdd_f, char *err_buf,
+               size_t err_buf_size)
+{
+       int rc, warnings = 0;
+       unsigned int i;
+       bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL;
+
+       rc = bootenv_create(&bootenv_pfi);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               goto err;
+       }
+
+       rc = bootenv_create(&bootenv_res);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               goto err;
+       }
+
+       rc = bootenv_read(fp_pfi, bootenv_pfi, data_size);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_READ;
+               goto err;
+       }
+
+       for (i = 0; i < ids_size; i++) {
+               rc = bootenv_create(&bootenv_flash);
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+                       goto err;
+               }
+
+               rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE);
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_BOOTENV_READ;
+                       goto err;
+               }
+
+               rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res,
+                               &warnings, err_buf, err_buf_size);
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+                       goto err;
+               }
+
+               rc = bootenv_compare(bootenv_flash, bootenv_res);
+               if (rc > 0) {
+                       rc = -PFIFLASH_CMP_DIFF;
+                       goto err;
+               } else if (rc < 0) {
+                       rc = -PFIFLASH_ERR_COMPARE;
+                       goto err;
+               }
+
+               bootenv_destroy(&bootenv_flash);
+               bootenv_flash = NULL;
+       }
+
+err:
+       if (bootenv_pfi)
+               bootenv_destroy(&bootenv_pfi);
+       if (bootenv_res)
+               bootenv_destroy(&bootenv_res);
+       if (bootenv_flash)
+               bootenv_destroy(&bootenv_flash);
+
+       return rc;
+}
+
+static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+               uint32_t bytes_left)
+{
+       unsigned int i;
+       size_t read_bytes, rc = 0;
+       char buf_pfi[COMPARE_BUFFER_SIZE];
+       char *buf_flash[ids_size];
+
+       for (i = 0; i < ids_size; i++) {
+               buf_flash[i] = malloc(COMPARE_BUFFER_SIZE);
+               if (!buf_flash[i])
+                       return -PFIFLASH_ERR_COMPARE;
+       }
+
+       while (bytes_left) {
+               if (bytes_left > COMPARE_BUFFER_SIZE)
+                       read_bytes = COMPARE_BUFFER_SIZE;
+               else
+                       read_bytes = bytes_left;
+
+               rc = fread(buf_pfi, 1, read_bytes, fp_pfi);
+               if (rc != read_bytes) {
+                       rc = -PFIFLASH_ERR_COMPARE;
+                       goto err;
+               }
+
+               for (i = 0; i < ids_size; i++) {
+                       rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]);
+                       if (rc != read_bytes) {
+                               rc = -PFIFLASH_CMP_DIFF;
+                               goto err;
+                       }
+
+                       rc = memcmp(buf_pfi, buf_flash[i], read_bytes);
+                       if (rc != 0) {
+                               rc = -PFIFLASH_CMP_DIFF;
+                               goto err;
+                       }
+               }
+
+               bytes_left -= read_bytes;
+       }
+
+err:
+       for (i = 0; i < ids_size; i++)
+               free(buf_flash[i]);
+
+       return rc;
+}
+
+static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi,
+               pdd_func_t pdd_f, char *err_buf, size_t err_buf_size)
+{
+       int rc, is_bootenv = 0;
+       unsigned int i;
+       char path[PATH_MAX];
+       libubi_t ulib = NULL;
+       FILE *fp_flash[u->ids_size];
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               goto err;
+       }
+
+       for (i = 0; i < u->ids_size; i++) {
+               if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 ||
+                   u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2)
+                       is_bootenv = 1;
+
+               snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, u->ids[i]);
+
+               fp_flash[i] = fopen(path, "r");
+               if (fp_flash[i] == NULL) {
+                       rc = -PFIFLASH_ERR_UBI_OPEN;
+                       goto err;
+               }
+       }
+
+       if (is_bootenv)
+               rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size,
+                               u->data_size, pdd_f, err_buf, err_buf_size);
+       else
+               rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size);
+
+err:
+       if (rc < 0)
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+
+       for (i = 0; i < u->ids_size; i++)
+               fclose(fp_flash[i]);
+       if (ulib)
+               libubi_close(ulib);
+
+       return rc;
+}
+
+static int
+erase_mtd_region(FILE* file_p, int start, int length)
+{
+       int rc, fd;
+       erase_info_t erase;
+       mtd_info_t mtdinfo;
+       loff_t offset = start;
+       loff_t end = offset + length;
+
+       fd = fileno(file_p);
+       if (fd < 0)
+               return -PFIFLASH_ERR_MTD_ERASE;
+
+       rc = ioctl(fd, MEMGETINFO, &mtdinfo);
+       if (rc)
+               return -PFIFLASH_ERR_MTD_ERASE;
+
+       /* check for bad blocks in case of NAND flash */
+       if (mtdinfo.type == MTD_NANDFLASH) {
+               while (offset < end) {
+                       rc = ioctl(fd, MEMGETBADBLOCK, &offset);
+                       if (rc > 0) {
+                               return -PFIFLASH_ERR_MTD_ERASE;
+                       }
+
+                       offset += mtdinfo.erasesize;
+               }
+       }
+
+       erase.start = start;
+       erase.length = length;
+
+       rc = ioctl(fd, MEMERASE, &erase);
+       if (rc) {
+               return -PFIFLASH_ERR_MTD_ERASE;
+       }
+
+       return rc;
+}
+
+/**
+ * process_raw_volumes - writes the raw sections of the PFI data
+ * @pfi                PFI data file pointer
+ * @pfi_raws   list of PFI raw headers
+ * @rawdev     device to use to write raw data
+ *
+ * Error handling:
+ *     when early EOF in PFI data
+ *     - returns -PFIFLASH_ERR_EOF, err_buf matches text to err
+ *     when file I/O error
+ *     - returns -PFIFLASH_ERR_FIO, err_buf matches text to err
+ *     when CRC check fails
+ *     - returns -PFIFLASH_ERR_CRC_CHECK, err_buf matches text to err
+ *     when opening MTD device fails
+ *     - reutrns -PFIFLASH_ERR_MTD_OPEN, err_buf matches text to err
+ *     when closing MTD device fails
+ *     - returns -PFIFLASH_ERR_MTD_CLOSE, err_buf matches text to err
+ **/
+static int
+process_raw_volumes(FILE* pfi, list_t pfi_raws, const char* rawdev,
+                   char* err_buf, size_t err_buf_size)
+{
+       int rc;
+       char *pfi_data;
+       void *i;
+       uint32_t crc, crc32_table[256];
+       size_t j, k;
+       FILE* mtd = NULL;
+       list_t ptr;
+
+       if (is_empty(pfi_raws))
+               return 0;
+
+       if (rawdev == NULL)
+               return 0;
+
+       rc = 0;
+
+       pfi_data = NULL;
+
+       log_msg("[ rawupdate dev=%s", rawdev);
+
+       crc = UBI_CRC32_INIT;
+       init_crc32_table(crc32_table);
+
+       /* most likely only one element in list, but just in case */
+       foreach(i, ptr, pfi_raws) {
+               pfi_raw_t r = (pfi_raw_t)i;
+
+               /* read in pfi data */
+               if (pfi_data != NULL)
+                       free(pfi_data);
+               pfi_data = malloc(r->data_size * sizeof(char));
+               for (j = 0; j < r->data_size; j++) {
+                       int c = fgetc(pfi);
+                       if (c == EOF) {
+                               rc = -PFIFLASH_ERR_EOF;
+                               EBUF(PFIFLASH_ERRSTR[-rc]);
+                               goto err;
+                       } else if (ferror(pfi)) {
+                               rc = -PFIFLASH_ERR_FIO;
+                               EBUF(PFIFLASH_ERRSTR[-rc]);
+                               goto err;
+                       }
+                       pfi_data[j] = (char)c;
+               }
+               crc = clc_crc32(crc32_table, crc, pfi_data, r->data_size);
+
+               /* check crc */
+               if (crc != r->crc) {
+                       rc = -PFIFLASH_ERR_CRC_CHECK;
+                       EBUF(PFIFLASH_ERRSTR[-rc], r->crc, crc);
+                       goto err;
+               }
+
+               /* open device */
+               mtd = fopen(rawdev, "r+");
+               if (mtd == NULL) {
+                       rc = -PFIFLASH_ERR_MTD_OPEN;
+                       EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+                       goto err;
+               }
+
+               for (j = 0; j < r->starts_size; j++) {
+                       rc = erase_mtd_region(mtd, r->starts[j], r->data_size);
+                       if (rc) {
+                               EBUF(PFIFLASH_ERRSTR[-rc]);
+                               goto err;
+                       }
+
+                       fseek(mtd, r->starts[j], SEEK_SET);
+                       for (k = 0; k < r->data_size; k++) {
+                               int c = fputc((int)pfi_data[k], mtd);
+                               if (c == EOF) {
+                                       fclose(mtd);
+                                       rc = -PFIFLASH_ERR_EOF;
+                                       EBUF(PFIFLASH_ERRSTR[-rc]);
+                                       goto err;
+                               }
+                               if ((char)c != pfi_data[k]) {
+                                       fclose(mtd);
+                                       rc = -1;
+                                       goto err;
+                               }
+                       }
+               }
+               rc = fclose(mtd);
+               mtd = NULL;
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_MTD_CLOSE;
+                       EBUF(PFIFLASH_ERRSTR[-rc], rawdev);
+                       goto err;
+               }
+       }
+
+ err:
+       if (mtd != NULL)
+               fclose(mtd);
+       if (pfi_data != NULL)
+               free(pfi_data);
+       return rc;
+}
+
+
+/**
+ * erase_unmapped_ubi_volumes - skip volumes provided by PFI file, clear rest
+ * @devno      UBI device number
+ * @pfi_ubis   list of UBI header data
+ *
+ * Error handling:
+ *     when UBI id is out of bounds
+ *     - returns -PFIFLASH_ERR_UBI_VID_OOB, err_buf matches text to err
+ *     when UBI volume can't be removed
+ *     - passes rc, prepends err_buf with contextual aid
+ **/
+static int
+erase_unmapped_ubi_volumes(int devno, list_t pfi_ubis,
+                          char *err_buf, size_t err_buf_size)
+{
+       int rc;
+       uint8_t ubi_volumes[PFI_UBI_MAX_VOLUMES];
+       size_t i;
+       list_t ptr;
+       pfi_ubi_t u;
+
+       rc = 0;
+
+       for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++)
+               ubi_volumes[i] = 1;
+
+       foreach(u, ptr, pfi_ubis) {
+               /* iterate over each vol_id */
+               for(i = 0; i < u->ids_size; i++) {
+                       if (u->ids[i] >= PFI_UBI_MAX_VOLUMES) {
+                               rc = -PFIFLASH_ERR_UBI_VID_OOB;
+                               EBUF(PFIFLASH_ERRSTR[-rc], u->ids[i]);
+                               goto err;
+                       }
+                       /* remove from removal list */
+                       ubi_volumes[u->ids[i]] = 0;
+               }
+       }
+
+       for (i = 0; i < PFI_UBI_MAX_VOLUMES; i++) {
+               if (ubi_volumes[i]) {
+                       rc = my_ubi_rmvol(devno, i, err_buf, err_buf_size);
+                       if (rc != 0) {
+                               EBUF_PREPEND("remove volume failed");
+                               goto err;
+                       }
+               }
+       }
+
+ err:
+       return rc;
+}
+
+
+/**
+ * process_ubi_volumes - delegate tasks regarding UBI volumes
+ * @pfi                        PFI data file pointer
+ * @seqnum             sequence number
+ * @pfi_ubis           list of UBI header data
+ * @bootenv_old                storage for current system PDD
+ * @pdd_f              function to handle PDD
+ * @ubi_update_process whether reading or writing
+ *
+ * Error handling:
+ *     when and unknown ubi_update_process is given
+ *     - returns -PFIFLASH_ERR_UBI_UNKNOWN, err_buf matches text to err
+ *     otherwise
+ *     - passes rc and err_buf
+ **/
+static int
+process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis,
+                   bootenv_t bootenv_old, pdd_func_t pdd_f,
+                   ubi_update_process_t ubi_update_process,
+                   char *err_buf, size_t err_buf_size)
+{
+       int rc;
+       pfi_ubi_t u;
+       list_t ptr;
+
+       rc = 0;
+
+       foreach(u, ptr, pfi_ubis) {
+               int s = seqnum;
+
+               if (s > ((int)u->ids_size - 1))
+                       s = 0; /* per default use the first */
+               u->curr_seqnum = s;
+
+               switch (ubi_update_process) {
+               case UBI_REMOVE:
+                       /* TODO are all these "EXAMPLE" vars okay? */
+                       if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+                           (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+                               rc = read_bootenv_volume(EXAMPLE_UBI_DEVICE,
+                                                        u->ids[s], bootenv_old,
+                                                        err_buf, err_buf_size);
+                               /* it's okay if there is no bootenv
+                                * we're going to write one */
+                               if ((rc == -PFIFLASH_ERR_UBI_VOL_FOPEN) ||
+                                   (rc == -PFIFLASH_ERR_BOOTENV_READ))
+                                       rc = 0;
+                               if (rc != 0)
+                                       goto err;
+                       }
+
+                       rc = my_ubi_rmvol(EXAMPLE_UBI_DEVICE, u->ids[s],
+                                         err_buf, err_buf_size);
+                       if (rc != 0)
+                               goto err;
+
+                       break;
+               case UBI_WRITE:
+                       rc = my_ubi_mkvol(EXAMPLE_UBI_DEVICE, s, u,
+                                         err_buf, err_buf_size);
+                       if (rc != 0) {
+                               EBUF_PREPEND("creating volume");
+                               goto err;
+                       }
+
+                       if ((u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_1) ||
+                           (u->ids[s] == EXAMPLE_BOOTENV_VOL_ID_2)) {
+                               rc = write_bootenv_volume(EXAMPLE_UBI_DEVICE,
+                                                         u->ids[s],
+                                                         bootenv_old, pdd_f,
+                                                         pfi,
+                                                         u->data_size,
+                                                         u->crc,
+                                                         err_buf,
+                                                         err_buf_size);
+                               if (rc != 0)
+                                       EBUF_PREPEND("bootenv volume");
+                       } else {
+                               rc = write_normal_volume(EXAMPLE_UBI_DEVICE,
+                                                        u->ids[s],
+                                                        u->data_size, pfi,
+                                                        u->crc,
+                                                        err_buf,
+                                                        err_buf_size);
+                               if (rc != 0)
+                                       EBUF_PREPEND("normal volume");
+                       }
+                       if (rc != 0)
+                               goto err;
+
+                       break;
+               case UBI_COMPARE:
+                       rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f,
+                                       err_buf, err_buf_size);
+                       if (rc != 0) {
+                               EBUF_PREPEND("compare volume");
+                               goto err;
+                       }
+
+                       break;
+               default:
+                       rc = -PFIFLASH_ERR_UBI_UNKNOWN;
+                       EBUF(PFIFLASH_ERRSTR[-rc]);
+                       goto err;
+               }
+       }
+
+ err:
+       return rc;
+}
+
+
+/**
+ * mirror_ubi_volumes - mirror redundant pairs of volumes
+ * @devno      UBI device number
+ * @pfi_ubis   list of PFI header data
+ *
+ * Error handling:
+ *     when UBI system couldn't be opened
+ *     - returns -PFIFLASH_ERR_UBI_OPEN, err_buf matches text to err
+ **/
+static int
+mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis,
+                  char *err_buf, size_t err_buf_size)
+{
+       int rc;
+       uint32_t j;
+       list_t ptr;
+       pfi_ubi_t i;
+       libubi_t ulib;
+
+       rc = 0;
+       ulib = NULL;
+
+       log_msg("[ mirror ...");
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       /**
+        * Execute all mirror operations on redundant groups.
+        * Create a volume within a redundant group if it does
+        * not exist already (this is a precondition of
+        * ubimirror).
+        */
+       foreach(i, ptr, pfi_ubis) {
+               for (j = 0; j < i->ids_size; j++) {
+                       /* skip self-match */
+                       if (i->ids[j] == i->ids[i->curr_seqnum])
+                               continue;
+
+                       rc = my_ubi_rmvol(devno, i->ids[j],
+                                         err_buf, err_buf_size);
+                       if (rc != 0)
+                               goto err;
+
+                       rc = my_ubi_mkvol(devno, j, i,
+                                         err_buf, err_buf_size);
+                       if (rc != 0)
+                               goto err;
+               }
+       }
+
+       foreach(i, ptr, pfi_ubis) {
+               rc = ubimirror(devno, i->curr_seqnum, i->ids, i->ids_size,
+                              err_buf, err_buf_size);
+               if (rc != 0)
+                       goto err;
+       }
+
+
+ err:
+       if (ulib != NULL)
+               libubi_close(ulib);
+
+       return rc;
+}
+
+
+/**
+ * pfiflash_with_options - exposed func to flash memory with a PFI file
+ * @pfi                        PFI data file pointer
+ * @complete           flag to erase unmapped volumes
+ * @seqnum             sequence number
+ * @compare            flag to compare
+ * @pdd_handling       method to handle pdd (keep, merge, overwrite...)
+ *
+ * Error handling:
+ *     when bootenv can't be created
+ *     - returns -PFIFLASH_ERR_BOOTENV_CREATE, err_buf matches text to err
+ *     when PFI headers can't be read, or
+ *     when fail to skip raw sections, or
+ *     when error occurs while processing raw volumes, or
+ *     when fail to erase unmapped UBI vols, or
+ *     when error occurs while processing UBI volumes, or
+ *     when error occurs while mirroring UBI volumes
+ *     - passes rc, prepends err_buf with contextual aid
+ **/
+int
+pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
+                 pdd_handling_t pdd_handling, const char* rawdev,
+                 char *err_buf, size_t err_buf_size)
+{
+       int rc;
+       bootenv_t bootenv;
+       pdd_func_t pdd_f;
+
+       if (pfi == NULL)
+               return -EINVAL;
+
+       rc = 0;
+       pdd_f = NULL;
+
+       /* If the user didnt specify a seqnum we start per default
+        * with the index 0 */
+       int curr_seqnum = seqnum < 0 ? 0 : seqnum;
+
+       list_t pfi_raws   = mk_empty(); /* list of raw sections from a pfi */
+       list_t pfi_ubis   = mk_empty(); /* list of ubi sections from a pfi */
+
+       rc = bootenv_create(&bootenv);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               EBUF(PFIFLASH_ERRSTR[-rc], "");
+               goto err;
+       }
+
+       rc = read_pfi_headers(&pfi_raws, &pfi_ubis, pfi, err_buf, err_buf_size);
+       if (rc != 0) {
+               EBUF_PREPEND("reading PFI header");
+               goto err;
+       }
+
+       if (rawdev == NULL || compare)
+               rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size);
+       else
+               rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf,
+                                        err_buf_size);
+       if (rc != 0) {
+               EBUF_PREPEND("handling raw section");
+               goto err;
+       }
+
+       if (complete && !compare) {
+               rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+                                               err_buf, err_buf_size);
+               if (rc != 0) {
+                       EBUF_PREPEND("deleting unmapped UBI volumes");
+                       goto err;
+               }
+       }
+
+       if (((int)pdd_handling >= 0) &&
+           (pdd_handling < PDD_HANDLING_NUM))
+               pdd_f = pdd_funcs[pdd_handling];
+       else {
+               rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+               goto err;
+       }
+
+       if (!compare) {
+               rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+                               pdd_f, UBI_REMOVE, err_buf, err_buf_size);
+               if (rc != 0) {
+                       EBUF_PREPEND("removing UBI volumes");
+                       goto err;
+               }
+
+               rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+                               pdd_f, UBI_WRITE, err_buf, err_buf_size);
+               if  (rc != 0) {
+                       EBUF_PREPEND("writing UBI volumes");
+                       goto err;
+               }
+
+               if (seqnum < 0) { /* mirror redundant pairs */
+                       rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+                                       err_buf, err_buf_size);
+                       if (rc != 0) {
+                               EBUF_PREPEND("mirroring UBI volumes");
+                               goto err;
+                       }
+               }
+       } else {
+               /* only compare volumes, don't alter the content */
+               rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+                               pdd_f, UBI_COMPARE, err_buf, err_buf_size);
+
+               if (rc == -PFIFLASH_CMP_DIFF)
+                       /* update is necessary, return positive value */
+                       rc = 1;
+
+               if (rc < 0) {
+                       EBUF_PREPEND("comparing UBI volumes");
+                       goto err;
+               }
+       }
+
+ err:
+       pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+       pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+       bootenv_destroy(&bootenv);
+       return rc;
+}
+
+
+/**
+ * pfiflash - passes to pfiflash_with_options
+ * @pfi                        PFI data file pointer
+ * @complete           flag to erase unmapped volumes
+ * @seqnum             sequence number
+ * @pdd_handling       method to handle pdd (keep, merge, overwrite...)
+ **/
+int
+pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+               char *err_buf, size_t err_buf_size)
+{
+       return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling,
+                                NULL, err_buf, err_buf_size);
+}
diff --git a/ubi-utils/old-tools/src/libubi.c b/ubi-utils/old-tools/src/libubi.c
new file mode 100644 (file)
index 0000000..be06f70
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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 B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <limits.h>
+#include "libubi.h"
+#include "libubi_int.h"
+
+/**
+ * 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) {
+               errmsg("cannot allocate %d bytes", len1 + len2 + 2);
+               perror("malloc");
+               return NULL;
+       }
+
+       memcpy(n, path, len1);
+       if (n[len1 - 1] != '/')
+               n[len1++] = '/';
+
+       memcpy(n + len1, name, len2 + 1);
+       return n;
+}
+
+/**
+ * read_positive_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_positive_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) {
+               errmsg("cannot read \"%s\"", file);
+               perror("read");
+               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) {
+               /* This must be a UBI bug */
+               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)) {
+               errmsg("close failed on \"%s\"", file);
+               perror("close");
+               return -1;
+       }
+
+       return 0;
+
+out_error:
+       close(fd);
+       return -1;
+}
+
+/**
+ * read_positive_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_positive_ll()', but it reads an 'int'
+ * value, not 'long long'.
+ */
+static int read_positive_int(const char *file, int *value)
+{
+       long long res;
+
+       if (read_positive_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;
+}
+
+/**
+ * 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) {
+               errmsg("cannot read \"%s\"", file);
+               perror("read");
+               goto out_error;
+       }
+
+       /* Make sure all data is read */
+       tmp1 = read(fd, &tmp, 1);
+       if (tmp1 == 1) {
+               errmsg("cannot read \"%s\"", file);
+               perror("read");
+               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)) {
+               errmsg("close failed on \"%s\"", file);
+               perror("close");
+               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 succes, 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) {
+               errmsg("\"%s\" does not have major:minor format", file);
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (*major < 0 || *minor < 0) {
+               errmsg("bad major:minor %d:%d in \"%s\"",
+                      *major, *minor, file);
+               errno = EINVAL;
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * 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
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int dev_read_int(const char *patt, int dev_num, int *value)
+{
+       char file[strlen(patt) + 50];
+
+       sprintf(file, patt, dev_num);
+       return read_positive_int(file, value);
+}
+
+/**
+ * vol_read_int - read a positive 'int' value from an UBI volume sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_int(const char *patt, int dev_num, int vol_id, int *value)
+{
+       char file[strlen(patt) + 100];
+
+       sprintf(file, patt, dev_num, vol_id);
+       return read_positive_int(file, value);
+}
+
+/**
+ * dev_read_ll - read a positive 'long long' value from an UBI device sysfs file.
+ * @patt: file pattern to read from
+ * @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.
+ */
+static int dev_read_ll(const char *patt, int dev_num, long long *value)
+{
+       char file[strlen(patt) + 50];
+
+       sprintf(file, patt, dev_num);
+       return read_positive_ll(file, value);
+}
+
+/**
+ * vol_read_ll - read a positive 'long long' value from an UBI volume sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @value: the result is stored here
+ *
+ * This function returns %0 in case of success and %-1 in case of failure.
+ */
+static int vol_read_ll(const char *patt, int dev_num, int vol_id,
+                      long long *value)
+{
+       char file[strlen(patt) + 100];
+
+       sprintf(file, patt, dev_num, vol_id);
+       return read_positive_ll(file, value);
+}
+
+/**
+ * vol_read_data - read data from an UBI volume's sysfs file.
+ * @patt: file pattern to read from
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @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 vol_read_data(const char *patt, int dev_num, int vol_id, void *buf,
+                        int buf_len)
+{
+       char file[strlen(patt) + 100];
+
+       sprintf(file, patt, dev_num, vol_id);
+       return read_data(file, buf, buf_len);
+}
+
+/**
+ * dent_is_dir - check if a file is a directory.
+ * @dir: the base directory path of the file
+ * @name: file name
+ *
+ * This function returns %1 if file @name in directory @dir is a directoru, and
+ * %0 if not.
+ */
+static int dent_is_dir(const char *dir, const char *name)
+{
+       int ret;
+       struct stat st;
+       char full_path[strlen(dir) + strlen(name) + 2];
+
+       sprintf(full_path, "%s/%s", dir, name);
+       ret = lstat(full_path, &st);
+       if (ret) {
+               errmsg("lstat failed on \"%s\"", full_path);
+               perror("lstat");
+               return -1;
+       }
+
+       return !!S_ISDIR(st.st_mode);
+}
+
+/**
+ * dev_get_major - get major and minor numbers of an UBI device.
+ * @lib: libubi descriptor
+ * @dev_num: UBI device number
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns zero in case of succes and %-1 in case of failure.
+ */
+static int dev_get_major(struct libubi *lib, int dev_num, int *major, int *minor)
+{
+       char file[strlen(lib->dev_dev) + 50];
+
+       sprintf(file, lib->dev_dev, dev_num);
+       return read_major(file, major, minor);
+}
+
+/**
+ * vol_get_major - get major and minor numbers of an UBI volume.
+ * @lib: libubi descriptor
+ * @dev_num: UBI device number
+ * @vol_id: volume ID
+ * @major: major number is returned here
+ * @minor: minor number is returned here
+ *
+ * This function returns zero in case of succes and %-1 in case of failure.
+ */
+static int vol_get_major(struct libubi *lib, int dev_num, int vol_id,
+                        int *major, int *minor)
+{
+       char file[strlen(lib->vol_dev) + 100];
+
+       sprintf(file, lib->vol_dev, dev_num, vol_id);
+       return read_major(file, major, minor);
+}
+
+/**
+ * vol_node2nums - find UBI device number and volume ID by volume device node
+ *                 file.
+ * @lib: UBI library descriptor
+ * @node: UBI character device node name
+ * @dev_num: UBI device number is returned here
+ * @vol_id: volume ID is returned hers
+ *
+ * This function returns zero in case of succes and %-1 in case of failure.
+ */
+static int vol_node2nums(struct libubi *lib, const char *node, int *dev_num,
+                        int *vol_id)
+{
+       struct stat st;
+       struct ubi_info info;
+       int i, fd, major, minor;
+       char file[strlen(lib->ubi_vol) + 100];
+
+       if (lstat(node, &st))
+               return -1;
+
+       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 (minor == 0) {
+               errmsg("\"%s\" is not a volume character device", node);
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (ubi_get_info((libubi_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;
+                       return -1;
+               }
+
+               if (major1 == major)
+                       break;
+       }
+
+       if (i > info.highest_dev_num) {
+               errno = ENODEV;
+               return -1;
+       }
+
+       /* Make sure this UBI volume exists */
+       sprintf(file, lib->ubi_vol, i, minor - 1);
+       fd = open(file, O_RDONLY);
+       if (fd == -1) {
+               errno = ENODEV;
+               return -1;
+       }
+
+       *dev_num = i;
+       *vol_id = minor - 1;
+       errno = 0;
+       return 0;
+}
+
+/**
+ * dev_node2num - find UBI device number by its character device node.
+ * @lib: UBI library descriptor
+ * @node: UBI character device node name
+ *
+ * This function returns positive UBI device number in case of success and %-1
+ * in case of failure.
+ */
+static int dev_node2num(struct libubi *lib, const char *node, int *dev_num)
+{
+       struct stat stat;
+       struct ubi_info info;
+       int i, major, minor;
+
+       if (lstat(node, &stat))
+               return -1;
+
+       if (!S_ISCHR(stat.st_mode)) {
+               errmsg("\"%s\" is not a character device", node);
+               errno = EINVAL;
+               return -1;
+       }
+
+       major = major(stat.st_rdev);
+       minor = minor(stat.st_rdev);
+
+       if (minor != 0) {
+               errmsg("\"%s\" is not an UBI character device", node);
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (ubi_get_info((libubi_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;
+                       return -1;
+               }
+
+               if (major1 == major) {
+                       if (minor1 != 0) {
+                               errmsg("UBI character device minor number is "
+                                      "%d, but must be 0", minor1);
+                               errno = EINVAL;
+                               return -1;
+                       }
+                       errno = 0;
+                       *dev_num = i;
+                       return 0;
+               }
+       }
+
+       errno = ENODEV;
+       return -1;
+}
+
+static int mtd_num2ubi_dev(struct libubi *lib, int mtd_num, int *dev_num)
+{
+       struct ubi_info info;
+       int i, ret, mtd_num1;
+
+       if (ubi_get_info((libubi_t *)lib, &info))
+               return -1;
+
+       for (i = info.lowest_dev_num; i <= info.highest_dev_num; i++) {
+               ret = dev_read_int(lib->dev_mtd_num, i, &mtd_num1);
+               if (ret) {
+                       if (errno == ENOENT)
+                               continue;
+                       return -1;
+               }
+
+               if (mtd_num1 == mtd_num) {
+                       errno = 0;
+                       *dev_num = i;
+                       return 0;
+               }
+       }
+
+       errno = ENODEV;
+       return -1;
+}
+
+libubi_t libubi_open(void)
+{
+       int fd, version;
+       struct libubi *lib;
+
+       lib = calloc(1, sizeof(struct libubi));
+       if (!lib)
+               return NULL;
+
+       /* TODO: this must be discovered instead */
+       lib->sysfs = strdup("/sys");
+       if (!lib->sysfs)
+               goto out_error;
+
+       lib->sysfs_ctrl = mkpath(lib->sysfs, SYSFS_CTRL);
+       if (!lib->sysfs_ctrl)
+               goto out_error;
+
+       lib->ctrl_dev = mkpath(lib->sysfs_ctrl, CTRL_DEV);
+       if (!lib->ctrl_dev)
+               goto out_error;
+
+       lib->sysfs_ubi = mkpath(lib->sysfs, SYSFS_UBI);
+       if (!lib->sysfs_ubi)
+               goto out_error;
+
+       /* Make sure UBI is present */
+       fd = open(lib->sysfs_ubi, O_RDONLY);
+       if (fd == -1) {
+               errmsg("cannot open \"%s\", UBI does not seem to exist in system",
+                      lib->sysfs_ubi);
+               goto out_error;
+       }
+
+       if (close(fd)) {
+               errmsg("close failed on \"%s\"", lib->sysfs_ubi);
+               perror("close");
+               goto out_error;
+       }
+
+       lib->ubi_dev = mkpath(lib->sysfs_ubi, UBI_DEV_NAME_PATT);
+       if (!lib->ubi_dev)
+               goto out_error;
+
+       lib->ubi_version = mkpath(lib->sysfs_ubi, UBI_VER);
+       if (!lib->ubi_version)
+               goto out_error;
+
+       lib->dev_dev = mkpath(lib->ubi_dev, DEV_DEV);
+       if (!lib->dev_dev)
+               goto out_error;
+
+       lib->dev_avail_ebs = mkpath(lib->ubi_dev, DEV_AVAIL_EBS);
+       if (!lib->dev_avail_ebs)
+               goto out_error;
+
+       lib->dev_total_ebs = mkpath(lib->ubi_dev, DEV_TOTAL_EBS);
+       if (!lib->dev_total_ebs)
+               goto out_error;
+
+       lib->dev_bad_count = mkpath(lib->ubi_dev, DEV_BAD_COUNT);
+       if (!lib->dev_bad_count)
+               goto out_error;
+
+       lib->dev_eb_size = mkpath(lib->ubi_dev, DEV_EB_SIZE);
+       if (!lib->dev_eb_size)
+               goto out_error;
+
+       lib->dev_max_ec = mkpath(lib->ubi_dev, DEV_MAX_EC);
+       if (!lib->dev_max_ec)
+               goto out_error;
+
+       lib->dev_bad_rsvd = mkpath(lib->ubi_dev, DEV_MAX_RSVD);
+       if (!lib->dev_bad_rsvd)
+               goto out_error;
+
+       lib->dev_max_vols = mkpath(lib->ubi_dev, DEV_MAX_VOLS);
+       if (!lib->dev_max_vols)
+               goto out_error;
+
+       lib->dev_min_io_size = mkpath(lib->ubi_dev, DEV_MIN_IO_SIZE);
+       if (!lib->dev_min_io_size)
+               goto out_error;
+
+       lib->dev_mtd_num = mkpath(lib->ubi_dev, DEV_MTD_NUM);
+       if (!lib->dev_mtd_num)
+               goto out_error;
+
+       lib->ubi_vol = mkpath(lib->sysfs_ubi, UBI_VOL_NAME_PATT);
+       if (!lib->ubi_vol)
+               goto out_error;
+
+       lib->vol_type = mkpath(lib->ubi_vol, VOL_TYPE);
+       if (!lib->vol_type)
+               goto out_error;
+
+       lib->vol_dev = mkpath(lib->ubi_vol, VOL_DEV);
+       if (!lib->vol_dev)
+               goto out_error;
+
+       lib->vol_alignment = mkpath(lib->ubi_vol, VOL_ALIGNMENT);
+       if (!lib->vol_alignment)
+               goto out_error;
+
+       lib->vol_data_bytes = mkpath(lib->ubi_vol, VOL_DATA_BYTES);
+       if (!lib->vol_data_bytes)
+               goto out_error;
+
+       lib->vol_rsvd_ebs = mkpath(lib->ubi_vol, VOL_RSVD_EBS);
+       if (!lib->vol_rsvd_ebs)
+               goto out_error;
+
+       lib->vol_eb_size = mkpath(lib->ubi_vol, VOL_EB_SIZE);
+       if (!lib->vol_eb_size)
+               goto out_error;
+
+       lib->vol_corrupted = mkpath(lib->ubi_vol, VOL_CORRUPTED);
+       if (!lib->vol_corrupted)
+               goto out_error;
+
+       lib->vol_name = mkpath(lib->ubi_vol, VOL_NAME);
+       if (!lib->vol_name)
+               goto out_error;
+
+       if (read_positive_int(lib->ubi_version, &version))
+               goto out_error;
+       if (version != LIBUBI_UBI_VERSION) {
+               fprintf(stderr, "LIBUBI: this library was made for UBI version "
+                               "%d, but UBI version %d is detected\n",
+                       LIBUBI_UBI_VERSION, version);
+               goto out_error;
+       }
+
+       return lib;
+
+out_error:
+       libubi_close((libubi_t)lib);
+       return NULL;
+}
+
+void libubi_close(libubi_t desc)
+{
+       struct libubi *lib = (struct libubi *)desc;
+
+       free(lib->vol_name);
+       free(lib->vol_corrupted);
+       free(lib->vol_eb_size);
+       free(lib->vol_rsvd_ebs);
+       free(lib->vol_data_bytes);
+       free(lib->vol_alignment);
+       free(lib->vol_dev);
+       free(lib->vol_type);
+       free(lib->ubi_vol);
+       free(lib->dev_mtd_num);
+       free(lib->dev_min_io_size);
+       free(lib->dev_max_vols);
+       free(lib->dev_bad_rsvd);
+       free(lib->dev_max_ec);
+       free(lib->dev_eb_size);
+       free(lib->dev_bad_count);
+       free(lib->dev_total_ebs);
+       free(lib->dev_avail_ebs);
+       free(lib->dev_dev);
+       free(lib->ubi_version);
+       free(lib->ubi_dev);
+       free(lib->sysfs_ubi);
+       free(lib->ctrl_dev);
+       free(lib->sysfs_ctrl);
+       free(lib->sysfs);
+       free(lib);
+}
+
+int ubi_attach_mtd(libubi_t desc, const char *node,
+                  struct ubi_attach_request *req)
+{
+       int fd, ret;
+       struct ubi_attach_req r;
+
+       memset(&r, sizeof(struct ubi_attach_req), '\0');
+
+       desc = desc;
+       r.ubi_num = req->dev_num;
+       r.mtd_num = req->mtd_num;
+       r.vid_hdr_offset = req->vid_hdr_offset;
+
+       fd = open(node, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       ret = ioctl(fd, UBI_IOCATT, &r);
+       close(fd);
+       if (ret == -1)
+               return -1;
+
+       req->dev_num = r.ubi_num;
+
+#ifdef UDEV_SETTLE_HACK
+       if (system("udevsettle") == -1)
+               return -1;
+       if (system("udevsettle") == -1)
+               return -1;
+#endif
+
+       return ret;
+}
+
+int ubi_detach_mtd(libubi_t desc, const char *node, int mtd_num)
+{
+       int ret, ubi_dev;
+
+       ret = mtd_num2ubi_dev(desc, mtd_num, &ubi_dev);
+       if (ret == -1)
+               return ret;
+
+       return ubi_remove_dev(desc, node, ubi_dev);
+}
+
+int ubi_remove_dev(libubi_t desc, const char *node, int ubi_dev)
+{
+       int fd, ret;
+
+       desc = desc;
+
+       fd = open(node, O_RDONLY);
+       if (fd == -1)
+               return -1;
+       ret = ioctl(fd, UBI_IOCDET, &ubi_dev);
+       if (ret == -1)
+               goto out_close;
+
+#ifdef UDEV_SETTLE_HACK
+       if (system("udevsettle") == -1)
+               return -1;
+#endif
+
+out_close:
+       close(fd);
+       return ret;
+}
+
+int ubi_node_type(libubi_t desc, const char *node)
+{
+       struct stat st;
+       struct ubi_info info;
+       int i, fd, major, minor;
+       struct libubi *lib = (struct libubi *)desc;
+       char file[strlen(lib->ubi_vol) + 100];
+
+       if (lstat(node, &st))
+               return -1;
+
+       if (!S_ISCHR(st.st_mode)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       major = major(st.st_rdev);
+       minor = minor(st.st_rdev);
+
+       if (ubi_get_info((libubi_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;
+                       return -1;
+               }
+
+               if (major1 == major)
+                       break;
+       }
+
+       if (i > info.highest_dev_num) {
+               /*
+                * The character device node does not correspond to any
+                * existing UBI device or volume, but we do not want to return
+                * any error number in this case, to indicate the fact that it
+                * could be a UBI device/volume, but it doesn't.
+                */
+               errno = 0;
+               return -1;
+       }
+
+       if (minor == 0)
+               return 1;
+
+       /* This is supposdely an UBI volume device node */
+       sprintf(file, lib->ubi_vol, i, minor - 1);
+       fd = open(file, O_RDONLY);
+       if (fd == -1) {
+               errno = 0;
+               return -1;
+       }
+
+       return 2;
+}
+
+int ubi_get_info(libubi_t desc, struct ubi_info *info)
+{
+       DIR *sysfs_ubi;
+       struct dirent *dirent;
+       struct libubi *lib = (struct libubi *)desc;
+
+       memset(info, '\0', sizeof(struct ubi_info));
+
+       if (read_major(lib->ctrl_dev, &info->ctrl_major, &info->ctrl_minor)) {
+               /*
+                * Older UBI versions did not have control device, so we do not
+                * panic here for compatibility reasons. May be few years later
+                * we could return -1 here, but for now just set major:minor to
+                * -1.
+                */
+               info->ctrl_major = info->ctrl_minor = -1;
+       }
+
+       /*
+        * We have to scan the UBI sysfs directory to identify how many UBI
+        * devices are present.
+        */
+       sysfs_ubi = opendir(lib->sysfs_ubi);
+       if (!sysfs_ubi) {
+               errmsg("cannot open %s", lib->sysfs_ubi);
+               perror("opendir");
+               return -1;
+       }
+
+       info->lowest_dev_num = INT_MAX;
+       while (1) {
+               int dev_num, ret;
+
+               errno = 0;
+               dirent = readdir(sysfs_ubi);
+               if (!dirent)
+                       break;
+               /*
+                * Make sure this direntry is a directory and not a symlink -
+                * Linux puts symlinks to UBI volumes on this UBI device to the
+                * same sysfs directory.
+                */
+               if (!dent_is_dir(lib->sysfs_ubi, dirent->d_name))
+                       continue;
+
+               ret = sscanf(dirent->d_name, UBI_DEV_NAME_PATT, &dev_num);
+               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 (!dirent && errno) {
+               errmsg("readdir failed on \"%s\"", lib->sysfs_ubi);
+               perror("readdir");
+               goto out_close;
+       }
+
+       if (closedir(sysfs_ubi)) {
+               errmsg("closedir failed on \"%s\"", lib->sysfs_ubi);
+               perror("closedir");
+               return -1;
+       }
+
+       if (info->lowest_dev_num == INT_MAX)
+               info->lowest_dev_num = 0;
+
+       if (read_positive_int(lib->ubi_version, &info->version))
+               return -1;
+
+       return 0;
+
+out_close:
+       closedir(sysfs_ubi);
+       return -1;
+}
+
+int ubi_mkvol(libubi_t desc, const char *node, struct ubi_mkvol_request *req)
+{
+       int fd, ret;
+       struct ubi_mkvol_req r;
+       size_t n;
+
+       memset(&r, sizeof(struct ubi_mkvol_req), '\0');
+
+       desc = desc;
+       r.vol_id = req->vol_id;
+       r.alignment = req->alignment;
+       r.bytes = req->bytes;
+       r.vol_type = req->vol_type;
+
+       n = strlen(req->name);
+       if (n > UBI_MAX_VOLUME_NAME)
+               return -1;
+
+       strncpy(r.name, req->name, UBI_MAX_VOLUME_NAME + 1);
+       r.name_len = n;
+
+       fd = open(node, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       ret = ioctl(fd, UBI_IOCMKVOL, &r);
+       if (ret == -1)
+               goto out_close;
+
+       req->vol_id = r.vol_id;
+
+#ifdef UDEV_SETTLE_HACK
+       if (system("udevsettle") == -1)
+               return -1;
+#endif
+
+out_close:
+       close(fd);
+       return ret;
+}
+
+int ubi_rmvol(libubi_t desc, const char *node, int vol_id)
+{
+       int fd, ret;
+
+       desc = desc;
+       fd = open(node, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       ret = ioctl(fd, UBI_IOCRMVOL, &vol_id);
+       if (ret == -1)
+               goto out_close;
+
+#ifdef UDEV_SETTLE_HACK
+       if (system("udevsettle") == -1)
+               return -1;
+#endif
+
+out_close:
+       close(fd);
+       return ret;
+}
+
+int ubi_rsvol(libubi_t desc, const char *node, int vol_id, long long bytes)
+{
+       int fd, ret;
+       struct ubi_rsvol_req req;
+
+       desc = desc;
+       fd = open(node, O_RDONLY);
+       if (fd == -1)
+               return -1;
+
+       req.bytes = bytes;
+       req.vol_id = vol_id;
+
+       ret = ioctl(fd, UBI_IOCRSVOL, &req);
+       close(fd);
+       return ret;
+}
+
+int ubi_update_start(libubi_t desc, int fd, long long bytes)
+{
+       desc = desc;
+       if (ioctl(fd, UBI_IOCVOLUP, &bytes))
+               return -1;
+       return 0;
+}
+
+int ubi_get_dev_info1(libubi_t desc, int dev_num, struct ubi_dev_info *info)
+{
+       DIR *sysfs_ubi;
+       struct dirent *dirent;
+       struct libubi *lib = (struct libubi *)desc;
+
+       memset(info, '\0', sizeof(struct ubi_dev_info));
+       info->dev_num = dev_num;
+
+       sysfs_ubi = opendir(lib->sysfs_ubi);
+       if (!sysfs_ubi)
+               return -1;
+
+       info->lowest_vol_num = INT_MAX;
+
+       while (1) {
+               int vol_id, ret, devno;
+
+               errno = 0;
+               dirent = readdir(sysfs_ubi);
+               if (!dirent)
+                       break;
+               ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT, &devno, &vol_id);
+               if (ret == 2 && devno == dev_num) {
+                       info->vol_count += 1;
+                       if (vol_id > info->highest_vol_num)
+                               info->highest_vol_num = vol_id;
+                       if (vol_id < info->lowest_vol_num)
+                               info->lowest_vol_num = vol_id;
+               }
+       }
+
+       if (!dirent && errno) {
+               errmsg("readdir failed on \"%s\"", lib->sysfs_ubi);
+               perror("readdir");
+               goto out_close;
+       }
+
+       if (closedir(sysfs_ubi)) {
+               errmsg("closedir failed on \"%s\"", lib->sysfs_ubi);
+               perror("closedir");
+               return -1;
+       }
+
+       if (info->lowest_vol_num == INT_MAX)
+               info->lowest_vol_num = 0;
+
+       if (dev_get_major(lib, dev_num, &info->major, &info->minor))
+               return -1;
+
+       if (dev_read_int(lib->dev_avail_ebs, dev_num, &info->avail_lebs))
+               return -1;
+       if (dev_read_int(lib->dev_total_ebs, dev_num, &info->total_lebs))
+               return -1;
+       if (dev_read_int(lib->dev_bad_count, dev_num, &info->bad_count))
+               return -1;
+       if (dev_read_int(lib->dev_eb_size, dev_num, &info->leb_size))
+               return -1;
+       if (dev_read_int(lib->dev_bad_rsvd, dev_num, &info->bad_rsvd))
+               return -1;
+       if (dev_read_ll(lib->dev_max_ec, dev_num, &info->max_ec))
+               return -1;
+       if (dev_read_int(lib->dev_max_vols, dev_num, &info->max_vol_count))
+               return -1;
+       if (dev_read_int(lib->dev_min_io_size, dev_num, &info->min_io_size))
+               return -1;
+
+       info->avail_bytes = info->avail_lebs * info->leb_size;
+       info->total_bytes = info->total_lebs * info->leb_size;
+
+       return 0;
+
+out_close:
+       closedir(sysfs_ubi);
+       return -1;
+}
+
+int ubi_get_dev_info(libubi_t desc, const char *node, struct ubi_dev_info *info)
+{
+       int dev_num;
+       struct libubi *lib = (struct libubi *)desc;
+
+       if (dev_node2num(lib, node, &dev_num))
+               return -1;
+
+       return ubi_get_dev_info1(desc, dev_num, info);
+}
+
+int ubi_get_vol_info1(libubi_t desc, int dev_num, int vol_id,
+                     struct ubi_vol_info *info)
+{
+       int ret;
+       struct libubi *lib = (struct libubi *)desc;
+       char buf[50];
+
+       memset(info, '\0', sizeof(struct ubi_vol_info));
+       info->dev_num = dev_num;
+       info->vol_id = vol_id;
+
+       if (dev_get_major(lib, dev_num, &info->dev_major, &info->dev_minor))
+               return -1;
+       if (vol_get_major(lib, dev_num, vol_id, &info->major, &info->minor))
+               return -1;
+
+       ret = vol_read_data(lib->vol_type, dev_num, vol_id, buf, 50);
+       if (ret < 0)
+               return -1;
+
+       if (strncmp(buf, "static\n", ret) == 0)
+               info->type = UBI_STATIC_VOLUME;
+       else if (strncmp(buf, "dynamic\n", ret) == 0)
+               info->type = UBI_DYNAMIC_VOLUME;
+       else {
+               errmsg("bad value at \"%s\"", buf);
+               errno = EINVAL;
+               return -1;
+       }
+
+       ret = vol_read_int(lib->vol_alignment, dev_num, vol_id,
+                          &info->alignment);
+       if (ret)
+               return -1;
+       ret = vol_read_ll(lib->vol_data_bytes, dev_num, vol_id,
+                         &info->data_bytes);
+       if (ret)
+               return -1;
+       ret = vol_read_int(lib->vol_rsvd_ebs, dev_num, vol_id, &info->rsvd_lebs);
+       if (ret)
+               return -1;
+       ret = vol_read_int(lib->vol_eb_size, dev_num, vol_id, &info->leb_size);
+       if (ret)
+               return -1;
+       ret = vol_read_int(lib->vol_corrupted, dev_num, vol_id,
+                          &info->corrupted);
+       if (ret)
+               return -1;
+       info->rsvd_bytes = info->leb_size * info->rsvd_lebs;
+
+       ret = vol_read_data(lib->vol_name, dev_num, vol_id, &info->name,
+                           UBI_VOL_NAME_MAX + 2);
+       if (ret < 0)
+               return -1;
+
+       info->name[ret - 1] = '\0';
+       return 0;
+}
+
+int ubi_get_vol_info(libubi_t desc, const char *node, struct ubi_vol_info *info)
+{
+       int vol_id, dev_num;
+       struct libubi *lib = (struct libubi *)desc;
+
+       if (vol_node2nums(lib, node, &dev_num, &vol_id))
+               return -1;
+
+       return ubi_get_vol_info1(desc, dev_num, vol_id, info);
+}
diff --git a/ubi-utils/old-tools/src/libubi_int.h b/ubi-utils/old-tools/src/libubi_int.h
new file mode 100644 (file)
index 0000000..6490864
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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 B. Bityutskiy
+ *
+ * UBI (Unsorted Block Images) library.
+ */
+
+#ifndef __LIBUBI_INT_H__
+#define __LIBUBI_INT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Error messages */
+#define errmsg(fmt, ...) do {                                      \
+        fprintf(stderr, "libubi error: " fmt "\n", ##__VA_ARGS__); \
+} while(0)
+
+/*
+ * The below are pre-define UBI file and directory names.
+ *
+ * Note, older kernels put 'ubiX_Y' directories straight to '/sys/class/ubi/'.
+ * New kernels puts 'ubiX_Y' directories to '/sys/class/ubi/ubiX/', which is
+ * saner. And for compatibility reasons it also puts symlinks to 'ubiX_Y'
+ * directories to '/sys/class/ubi/'. For now libubi assumes old layout.
+ */
+
+#define SYSFS_UBI         "class/ubi"
+#define SYSFS_CTRL        "class/misc/ubi_ctrl/"
+
+#define CTRL_DEV          "dev"
+
+#define UBI_VER           "version"
+#define UBI_DEV_NAME_PATT "ubi%d"
+
+#define DEV_DEV           "dev"
+#define DEV_AVAIL_EBS     "avail_eraseblocks"
+#define DEV_TOTAL_EBS     "total_eraseblocks"
+#define DEV_BAD_COUNT     "bad_peb_count"
+#define DEV_EB_SIZE       "eraseblock_size"
+#define DEV_MAX_EC        "max_ec"
+#define DEV_MAX_RSVD      "reserved_for_bad"
+#define DEV_MAX_VOLS      "max_vol_count"
+#define DEV_MIN_IO_SIZE   "min_io_size"
+#define DEV_MTD_NUM       "mtd_num"
+
+#define UBI_VOL_NAME_PATT "ubi%d_%d"
+#define VOL_TYPE          "type"
+#define VOL_DEV           "dev"
+#define VOL_ALIGNMENT     "alignment"
+#define VOL_DATA_BYTES    "data_bytes"
+#define VOL_RSVD_EBS      "reserved_ebs"
+#define VOL_EB_SIZE       "usable_eb_size"
+#define VOL_CORRUPTED     "corrupted"
+#define VOL_NAME          "name"
+
+/**
+ * libubi - UBI library description data structure.
+ * @sysfs: sysfs file system path
+ * @sysfs_ctrl: UBI control device directory in sysfs
+ * @ctrl_dev: UBI control device major/minor numbers sysfs file
+ * @sysfs_ubi: UBI directory in sysfs
+ * @ubi_dev: UBI device sysfs directory pattern
+ * @ubi_version: UBI version file sysfs path
+ * @dev_dev: UBI device major/minor numbers file pattern
+ * @dev_avail_ebs: count of available eraseblocks sysfs path pattern
+ * @dev_total_ebs: total eraseblocks count sysfs path pattern
+ * @dev_bad_count: count of bad eraseblocks sysfs path pattern
+ * @dev_eb_size: size of UBI device's eraseblocks sysfs path pattern
+ * @dev_max_ec: maximum erase counter sysfs path pattern
+ * @dev_bad_rsvd: count of physical eraseblock reserved for bad eraseblocks
+ *                handling
+ * @dev_max_vols: maximum volumes number count sysfs path pattern
+ * @dev_min_io_size: minimum I/O unit size sysfs path pattern
+ * @ubi_vol: UBI volume sysfs directory pattern
+ * @vol_type: volume type sysfs path pattern
+ * @vol_dev: volume major/minor numbers file pattern
+ * @vol_alignment: volume alignment sysfs path pattern
+ * @vol_data_bytes: volume data size sysfs path pattern
+ * @vol_rsvd_ebs: volume reserved size sysfs path pattern
+ * @vol_eb_size: volume eraseblock size sysfs path pattern
+ * @vol_corrupted: volume corruption flag sysfs path pattern
+ * @vol_name: volume name sysfs path pattern
+ */
+struct libubi
+{
+       char *sysfs;
+       char *sysfs_ctrl;
+       char *ctrl_dev;
+       char *sysfs_ubi;
+       char *ubi_dev;
+       char *ubi_version;
+       char *dev_dev;
+       char *dev_avail_ebs;
+       char *dev_total_ebs;
+       char *dev_bad_count;
+       char *dev_eb_size;
+       char *dev_max_ec;
+       char *dev_bad_rsvd;
+       char *dev_max_vols;
+       char *dev_min_io_size;
+       char *dev_mtd_num;
+       char *ubi_vol;
+       char *vol_type;
+       char *vol_dev;
+       char *vol_alignment;
+       char *vol_data_bytes;
+       char *vol_rsvd_ebs;
+       char *vol_eb_size;
+       char *vol_corrupted;
+       char *vol_name;
+       char *vol_max_count;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__LIBUBI_INT_H__ */
diff --git a/ubi-utils/old-tools/src/libubigen.c b/ubi-utils/old-tools/src/libubigen.c
new file mode 100644 (file)
index 0000000..87c8c40
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * Add UBI headers to binary data.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "ubigen.h"
+#include "crc32.h"
+
+#define UBI_NAME_SIZE          256
+#define DEFAULT_VID_OFFSET     ((DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE))
+#define MIN(a,b)               ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+
+struct ubi_info {
+       struct ubi_vid_hdr* v;  /* Volume ID header */
+       struct ubi_ec_hdr* ec;  /* Erase count header */
+
+       FILE* fp_in;            /* Input Stream */
+       FILE* fp_out;           /* Output stream */
+
+       size_t peb_size;        /* Physical EB size in bytes */
+       size_t leb_size;        /* Size of a logical EB in a physical EB */
+       size_t leb_total;       /* Total input size in logical EB */
+       size_t alignment;       /* Block alignment */
+       size_t data_pad;        /* Size of padding in each physical EB */
+
+       size_t bytes_total;     /* Total input size in bytes */
+       size_t bytes_read;      /* Nymber of read bytes (total) */
+
+       uint32_t blks_written;  /* Number of written logical EB */
+
+       uint8_t* buf;           /* Allocated buffer */
+       uint8_t* ptr_ec_hdr;    /* Pointer to EC hdr in buf */
+       uint8_t* ptr_vid_hdr;   /* Pointer to VID hdr in buf */
+       uint8_t* ptr_data;      /* Pointer to data region in buf */
+};
+
+
+static uint32_t
+byte_to_blk(uint64_t byte, uint32_t peb_size)
+{
+       return (byte % peb_size) == 0
+               ? (byte / peb_size)
+               : (byte / peb_size) + 1;
+}
+
+static int
+validate_ubi_info(ubi_info_t u)
+{
+       if ((u->v->vol_type != UBI_VID_DYNAMIC) &&
+           (u->v->vol_type != UBI_VID_STATIC)) {
+               return EUBIGEN_INVALID_TYPE;
+       }
+
+       if (__be32_to_cpu(u->ec->vid_hdr_offset) < UBI_VID_HDR_SIZE) {
+               return EUBIGEN_INVALID_HDR_OFFSET;
+       }
+
+       return 0;
+}
+
+static int
+skip_blks(ubi_info_t u, uint32_t blks)
+{
+       uint32_t i;
+       size_t read = 0, to_read = 0;
+
+       /* Step to a maximum of leb_total - 1 to keep the
+          restrictions. */
+       for (i = 0; i < MIN(blks, u->leb_total-1); i++) {
+               /* Read in data */
+               to_read = MIN(u->leb_size,
+                             (u->bytes_total - u->bytes_read));
+               read = fread(u->ptr_data, 1, to_read, u->fp_in);
+               if (read != to_read) {
+                       return -EIO;
+               }
+               u->bytes_read += read;
+               u->blks_written++;
+       }
+
+       return 0;
+}
+
+static void
+clear_buf(ubi_info_t u)
+{
+       memset(u->buf, 0xff, u->peb_size);
+}
+
+static void
+write_ec_hdr(ubi_info_t u)
+{
+       memcpy(u->ptr_ec_hdr, u->ec, UBI_EC_HDR_SIZE);
+}
+
+static int
+fill_data_buffer_from_file(ubi_info_t u, size_t* read)
+{
+       size_t to_read = 0;
+
+       if (u-> fp_in == NULL)
+               return -EIO;
+
+       to_read = MIN(u->leb_size, (u->bytes_total - u->bytes_read));
+       *read = fread(u->ptr_data, 1, to_read, u->fp_in);
+       if (*read != to_read) {
+               return -EIO;
+       }
+       return 0;
+}
+
+static void
+add_static_info(ubi_info_t u, size_t data_size, ubigen_action_t action)
+{
+       uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+                                u->ptr_data, data_size);
+
+       u->v->data_size = __cpu_to_be32(data_size);
+       u->v->data_crc = __cpu_to_be32(crc);
+
+       if (action & BROKEN_DATA_CRC) {
+               u->v->data_crc =
+                       __cpu_to_be32(__be32_to_cpu(u->v->data_crc) + 1);
+       }
+       if (action & BROKEN_DATA_SIZE) {
+               u->v->data_size =
+                       __cpu_to_be32(__be32_to_cpu(u->v->data_size) + 1);
+       }
+}
+
+static void
+write_vid_hdr(ubi_info_t u, ubigen_action_t action)
+{
+       uint32_t crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+                                u->v, UBI_VID_HDR_SIZE_CRC);
+       /* Write VID header */
+       u->v->hdr_crc = __cpu_to_be32(crc);
+       if (action & BROKEN_HDR_CRC) {
+               u->v->hdr_crc = __cpu_to_be32(__be32_to_cpu(u->v->hdr_crc) + 1);
+       }
+       memcpy(u->ptr_vid_hdr, u->v, UBI_VID_HDR_SIZE);
+}
+
+static int
+write_to_output_stream(ubi_info_t u)
+{
+       size_t written;
+
+       written = fwrite(u->buf, 1, u->peb_size, u->fp_out);
+       if (written != u->peb_size) {
+               return -EIO;
+       }
+       return 0;
+}
+
+int
+ubigen_write_leb(ubi_info_t u, ubigen_action_t action)
+{
+       int rc = 0;
+       size_t read = 0;
+
+       clear_buf(u);
+       write_ec_hdr(u);
+
+       rc = fill_data_buffer_from_file(u, &read);
+       if (rc != 0)
+               return rc;
+
+       if (u->v->vol_type == UBI_VID_STATIC)  {
+               add_static_info(u, read, action);
+       }
+
+       u->v->lnum = __cpu_to_be32(u->blks_written);
+
+       if (action & MARK_AS_UPDATE) {
+               u->v->copy_flag = (u->v->copy_flag)++;
+       }
+
+       write_vid_hdr(u, action);
+       rc = write_to_output_stream(u);
+       if (rc != 0)
+               return rc;
+
+       /* Update current handle */
+       u->bytes_read += read;
+       u->blks_written++;
+       return 0;
+}
+
+int
+ubigen_write_complete(ubi_info_t u)
+{
+       size_t i;
+       int rc = 0;
+
+       for (i = 0; i < u->leb_total; i++) {
+               rc = ubigen_write_leb(u,  NO_ERROR);
+               if (rc != 0)
+                       return rc;
+       }
+
+       return 0;
+}
+
+int
+ubigen_write_broken_update(ubi_info_t u, uint32_t blk)
+{
+       int rc = 0;
+
+       rc = skip_blks(u, blk);
+       if (rc != 0)
+               return rc;
+
+       rc = ubigen_write_leb(u, MARK_AS_UPDATE | BROKEN_DATA_CRC);
+       if (rc != 0)
+               return rc;
+
+
+       return 0;
+}
+
+void
+dump_info(ubi_info_t u ubi_unused)
+{
+#ifdef DEBUG
+       int err = 0;
+       if (!u) {
+               fprintf(stderr, "<empty>");
+               return;
+       }
+       if (!u->ec) {
+               fprintf(stderr, "<ec-empty>");
+               err = 1;
+       }
+       if (!u->v) {
+               fprintf(stderr, "<v-empty>");
+               err = 1;
+       }
+       if (err) return;
+
+       fprintf(stderr, "ubi volume\n");
+       fprintf(stderr, "version      :   %8d\n", u->v->version);
+       fprintf(stderr, "vol_id       :   %8d\n", __be32_to_cpu(u->v->vol_id));
+       fprintf(stderr, "vol_type     :   %8s\n",
+               u->v->vol_type == UBI_VID_STATIC ?
+               "static" : "dynamic");
+       fprintf(stderr, "used_ebs     :   %8d\n",
+               __be32_to_cpu(u->v->used_ebs));
+       fprintf(stderr, "peb_size     : 0x%08x\n", u->peb_size);
+       fprintf(stderr, "leb_size     : 0x%08x\n", u->leb_size);
+       fprintf(stderr, "data_pad     : 0x%08x\n",
+               __be32_to_cpu(u->v->data_pad));
+       fprintf(stderr, "leb_total    :   %8d\n", u->leb_total);
+       fprintf(stderr, "header offs  : 0x%08x\n",
+               __be32_to_cpu(u->ec->vid_hdr_offset));
+       fprintf(stderr, "bytes_total  :   %8d\n", u->bytes_total);
+       fprintf(stderr, "  +  in MiB  : %8.2f M\n",
+               ((float)(u->bytes_total)) / 1024 / 1024);
+       fprintf(stderr, "-------------------------------\n\n");
+#else
+       return;
+#endif
+}
+
+int
+ubigen_destroy(ubi_info_t *u)
+{
+       if (u == NULL)
+               return -EINVAL;
+
+       ubi_info_t tmp = *u;
+
+       if (tmp) {
+               if (tmp->v)
+                       free(tmp->v);
+               if (tmp->ec)
+                       free(tmp->ec);
+               if (tmp->buf)
+                       free(tmp->buf);
+               free(tmp);
+       }
+       *u = NULL;
+       return 0;
+}
+
+void
+ubigen_init(void)
+{
+       init_crc32_table(crc32_table);
+}
+
+int
+ubigen_create(ubi_info_t* u, uint32_t vol_id, uint8_t vol_type,
+             uint32_t peb_size, uint64_t ec, uint32_t alignment,
+             uint8_t version, uint32_t vid_hdr_offset, uint8_t compat_flag,
+             size_t data_size, FILE* fp_in, FILE* fp_out)
+{
+       int rc = 0;
+       ubi_info_t res = NULL;
+       uint32_t crc;
+       uint32_t data_offset;
+
+       if (alignment == 0) {
+               rc = EUBIGEN_INVALID_ALIGNMENT;
+               goto ubigen_create_err;
+       }
+       if ((fp_in == NULL) || (fp_out == NULL)) {
+               rc = -EINVAL;
+               goto ubigen_create_err;
+       }
+
+       res = (ubi_info_t) calloc(1, sizeof(struct ubi_info));
+       if (res == NULL) {
+               rc = -ENOMEM;
+               goto ubigen_create_err;
+       }
+
+       res->v = (struct ubi_vid_hdr*) calloc(1, sizeof(struct ubi_vid_hdr));
+       if (res->v == NULL) {
+               rc = -ENOMEM;
+               goto ubigen_create_err;
+       }
+
+       res->ec = (struct ubi_ec_hdr*) calloc(1, sizeof(struct ubi_ec_hdr));
+       if (res->ec == NULL) {
+               rc = -ENOMEM;
+               goto ubigen_create_err;
+       }
+
+       /* data which is needed in the general process */
+       vid_hdr_offset = vid_hdr_offset ? vid_hdr_offset : DEFAULT_VID_OFFSET;
+       data_offset = vid_hdr_offset + UBI_VID_HDR_SIZE;
+       res->bytes_total = data_size;
+       res->peb_size = peb_size ? peb_size : DEFAULT_BLOCKSIZE;
+       res->data_pad = (res->peb_size - data_offset) % alignment;
+       res->leb_size = res->peb_size - data_offset - res->data_pad;
+       res->leb_total = byte_to_blk(data_size, res->leb_size);
+       res->alignment = alignment;
+
+       if ((res->peb_size < (vid_hdr_offset + UBI_VID_HDR_SIZE))) {
+               rc = EUBIGEN_TOO_SMALL_EB;
+               goto ubigen_create_err;
+       }
+       res->fp_in = fp_in;
+       res->fp_out = fp_out;
+
+       /* vid hdr data which doesn't change */
+       res->v->magic = __cpu_to_be32(UBI_VID_HDR_MAGIC);
+       res->v->version = version ? version : UBI_VERSION;
+       res->v->vol_type = vol_type;
+       res->v->vol_id = __cpu_to_be32(vol_id);
+       res->v->compat = compat_flag;
+       res->v->data_pad = __cpu_to_be32(res->data_pad);
+
+       /* static only: used_ebs */
+       if (res->v->vol_type == UBI_VID_STATIC) {
+               res->v->used_ebs = __cpu_to_be32(byte_to_blk
+                                               (res->bytes_total,
+                                                res->leb_size));
+       }
+
+       /* ec hdr (fixed, doesn't change) */
+       res->ec->magic = __cpu_to_be32(UBI_EC_HDR_MAGIC);
+       res->ec->version = version ? version : UBI_VERSION;
+       res->ec->ec = __cpu_to_be64(ec);
+       res->ec->vid_hdr_offset = __cpu_to_be32(vid_hdr_offset);
+
+       res->ec->data_offset = __cpu_to_be32(data_offset);
+
+       crc = clc_crc32(crc32_table, UBI_CRC32_INIT, res->ec,
+                       UBI_EC_HDR_SIZE_CRC);
+       res->ec->hdr_crc = __cpu_to_be32(crc);
+
+       /* prepare a read buffer */
+       res->buf = (uint8_t*) malloc (res->peb_size * sizeof(uint8_t));
+       if (res->buf == NULL) {
+               rc = -ENOMEM;
+               goto ubigen_create_err;
+       }
+
+       /* point to distinct regions within the buffer */
+       res->ptr_ec_hdr = res->buf;
+       res->ptr_vid_hdr = res->buf + __be32_to_cpu(res->ec->vid_hdr_offset);
+       res->ptr_data = res->buf + __be32_to_cpu(res->ec->vid_hdr_offset)
+               + UBI_VID_HDR_SIZE;
+
+       rc = validate_ubi_info(res);
+       if (rc != 0) {
+               fprintf(stderr, "Volume validation failed: %d\n", rc);
+               goto ubigen_create_err;
+       }
+
+       dump_info(res);
+       *u = res;
+       return rc;
+
+ ubigen_create_err:
+       if (res) {
+               if (res->v)
+                       free(res->v);
+               if (res->ec)
+                       free(res->ec);
+               if (res->buf)
+                       free(res->buf);
+               free(res);
+       }
+       *u = NULL;
+       return rc;
+}
+
+int
+ubigen_get_leb_size(ubi_info_t u, size_t* size)
+{
+       if (u == NULL)
+               return -EINVAL;
+
+       *size = u->leb_size;
+       return 0;
+}
+
+
+int
+ubigen_get_leb_total(ubi_info_t u, size_t* total)
+{
+       if (u == NULL)
+               return -EINVAL;
+
+       *total = u->leb_total;
+       return 0;
+}
+
+int
+ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+                   const char* vol_name, struct ubi_vtbl_record *lvol_rec)
+{
+       uint32_t crc;
+
+       if ((u == NULL) || (vol_name == NULL))
+               return -EINVAL;
+
+       memset(lvol_rec, 0x0, UBI_VTBL_RECORD_SIZE);
+
+       lvol_rec->reserved_pebs =
+               __cpu_to_be32(byte_to_blk(reserved_bytes, u->leb_size));
+       lvol_rec->alignment = __cpu_to_be32(u->alignment);
+       lvol_rec->data_pad = u->v->data_pad;
+       lvol_rec->vol_type = u->v->vol_type;
+
+       lvol_rec->name_len =
+               __cpu_to_be16((uint16_t)strlen((const char*)vol_name));
+
+       memcpy(lvol_rec->name, vol_name, UBI_VOL_NAME_MAX + 1);
+
+       crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+                       lvol_rec, UBI_VTBL_RECORD_SIZE_CRC);
+       lvol_rec->crc =  __cpu_to_be32(crc);
+
+       return 0;
+}
diff --git a/ubi-utils/old-tools/src/libubimirror.c b/ubi-utils/old-tools/src/libubimirror.c
new file mode 100644 (file)
index 0000000..d06770e
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <memory.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#include <libubi.h>
+#include "ubimirror.h"
+
+#define COMPARE_BUF_SIZE    (128 * 1024)
+
+#define DEFAULT_DEV_PATTERN    "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN    "/dev/ubi%d_%d"
+
+#define EBUF(fmt...) do {                      \
+       snprintf(err_buf, err_buf_size, fmt);   \
+} while (0)
+
+enum {
+       compare_error = -1,
+       seek_error = -2,
+       write_error = -3,
+       read_error = -4,
+       update_error = -5,
+       ubi_error = -6,
+       open_error = -7,
+       close_error = -8,
+       compare_equal = 0,
+       compare_different = 1
+};
+
+/*
+ * Read len number of bytes from fd.
+ * Return 0 on EOF, -1 on error.
+ */
+static ssize_t fill_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+       ssize_t got, have = 0;
+
+       do {
+               got = read(fd, buf + have, len - have);
+               if (got == -1 && errno != EINTR)
+                       return -1;
+               have += got;
+       } while (got > 0 && have < len);
+       return have;
+}
+
+/*
+ * Write len number of bytes to fd.
+ * Return bytes written (>= 0), -1 on error.
+ */
+static ssize_t flush_buffer(int fd, unsigned char *buf, ssize_t len)
+{
+       ssize_t done, have = 0;
+
+       do {
+               done = write(fd, buf + have, len - have);
+               if (done == -1 && errno != EINTR)
+                       return -1;
+               have += done;
+       } while (done > 0 && have < len);
+       return have;
+}
+
+/*
+ *  Compare two files.  Return 0, 1, or -1, depending on whether the
+ *  files are equal, different, or an error occured.
+ *  Return compare-different when target volume can not be read. Might be
+ *  an interrupted volume update and then the target device returns -EIO but
+ *  can be updated.
+ *
+ *  fd_a is source
+ *  fd_b is destination
+ */
+static int compare_files(int fd_a, int fd_b)
+{
+       unsigned char buf_a[COMPARE_BUF_SIZE], buf_b[COMPARE_BUF_SIZE];
+       ssize_t len_a, len_b;
+       int rc;
+
+       for (;;) {
+               len_a = fill_buffer(fd_a, buf_a, sizeof(buf_a));
+               if (len_a == -1) {
+                       rc = compare_error;
+                       break;
+               }
+               len_b = fill_buffer(fd_b, buf_b, sizeof(buf_b));
+               if (len_b == -1) {
+                       rc = compare_different;
+                       break;
+               }
+               if (len_a != len_b) {
+                       rc = compare_different;
+                       break;
+               }
+               if (len_a == 0) {       /* Size on both files equal and EOF */
+                       rc = compare_equal;
+                       break;
+               }
+               if (memcmp(buf_a, buf_b, len_a) != 0 ) {
+                       rc = compare_different;
+                       break;
+               }
+       }
+       /* Position both files at the beginning */
+       if (lseek(fd_a, 0, SEEK_SET) == -1 ||
+          lseek(fd_b, 0, SEEK_SET) == -1)
+               rc = seek_error;
+       return rc;
+}
+
+int vol_get_used_bytes(int vol_fd, unsigned long long *bytes)
+{
+       off_t res;
+
+       res = lseek(vol_fd, 0, SEEK_END);
+       if (res == (off_t)-1)
+               return -1;
+       *bytes = (unsigned long long) res;
+       res = lseek(vol_fd, 0, SEEK_SET);
+       return res == (off_t)-1 ? -1 : 0;
+}
+
+static int copy_files(libubi_t ulib, int fd_in, int fd_out)
+{
+       unsigned char buf_a[COMPARE_BUF_SIZE];
+       ssize_t len_a, len_b;
+       unsigned long long update_size, copied;
+
+       if (vol_get_used_bytes(fd_in, &update_size) == -1 ||
+           ubi_update_start(ulib, fd_out, update_size) == -1)
+               return update_error;
+       for (copied = 0; copied < update_size; copied += len_b ) {
+               len_a = fill_buffer(fd_in, buf_a, sizeof(buf_a));
+               if (len_a == -1)
+                       return read_error;
+               if (len_a == 0)         /* Reach EOF */
+                       return 0;
+               len_b = flush_buffer(fd_out, buf_a, len_a);
+               if (len_b != len_a)
+                       return write_error;
+       }
+       return 0;
+}
+
+int ubimirror(uint32_t devno, int seqnum, uint32_t *ids, ssize_t ids_size,
+               char *err_buf, size_t err_buf_size)
+{
+       int rc = 0;
+       uint32_t src_id;
+       char path[PATH_MAX];
+       libubi_t ulib;
+       int fd_in = -1, i = 0, fd_out = -1;
+
+       if (ids_size == 0)
+               return 0;
+       else {
+               if ((seqnum < 0) || (seqnum > (ids_size - 1))) {
+                       EBUF("volume id %d out of range", seqnum);
+                       return EUBIMIRROR_NO_SRC;
+               }
+               src_id = ids[seqnum];
+       }
+
+       ulib = libubi_open();
+       if (ulib == NULL)
+               return ubi_error;
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, src_id);
+
+       fd_in = open(path, O_RDONLY);
+       if (fd_in == -1) {
+               EBUF("open error source volume %d", ids[i]);
+               rc = open_error;
+               goto err;
+       }
+
+       for (i = 0; i < ids_size; i++) {
+               if (ids[i] == src_id)           /* skip self-mirror */
+                       continue;
+
+               snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, ids[i]);
+
+               fd_out = open(path, O_RDWR);
+               if (fd_out < 0){
+                       EBUF("open error destination volume %d", ids[i]);
+                       rc = open_error;
+                       goto err;
+               }
+               rc = compare_files(fd_in, fd_out);
+               if (rc < 0) {
+                       EBUF("compare error volume %d and %d", src_id, ids[i]);
+                       goto err;
+               } else if (rc == compare_different) {
+                       rc = copy_files(ulib, fd_in, fd_out);
+                       if (rc != 0) {
+                               EBUF("mirror error volume %d to %d", src_id,
+                                               ids[i]);
+                               goto err;
+                       }
+               }
+               if ((rc = close(fd_out)) == -1) {
+                       EBUF("close error volume %d", ids[i]);
+                       rc = close_error;
+                       goto err;
+               } else
+                       fd_out = -1;
+       }
+err:
+       if (fd_out != -1)
+               close(fd_out);
+       if (fd_in != -1)
+               close(fd_in);
+       if (ulib != NULL)
+               libubi_close(ulib);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/list.c b/ubi-utils/old-tools/src/list.c
new file mode 100644 (file)
index 0000000..6eb716b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "list.h"
+
+list_t
+mk_empty(void)
+{
+       return (list_t) NULL;
+}
+
+int
+is_empty(list_t l)
+{
+       return l == NULL;
+}
+
+info_t
+head(list_t l)
+{
+       assert(!is_empty(l));
+       return l->info;
+}
+
+list_t
+tail(list_t l)
+{
+       assert(!is_empty(l));
+       return l->next;
+}
+
+list_t
+remove_head(list_t l)
+{
+       list_t res;
+       assert(!is_empty(l));
+
+       res = l->next;
+       free(l);
+       return res;
+}
+
+list_t
+cons(info_t e, list_t l)
+{
+       list_t res = malloc(sizeof(*l));
+       if (!res)
+               return NULL;
+       res->info = e;
+       res->next = l;
+
+       return res;
+}
+
+list_t
+prepend_elem(info_t e, list_t l)
+{
+       return cons(e,l);
+}
+
+list_t
+append_elem(info_t e, list_t l)
+{
+       if (is_empty(l)) {
+               return cons(e,l);
+       }
+       l->next = append_elem(e, l->next);
+
+       return l;
+}
+
+list_t
+insert_sorted(cmp_func_t cmp, info_t e, list_t l)
+{
+       if (is_empty(l))
+               return cons(e, l);
+
+       switch (cmp(e, l->info)) {
+       case -1:
+       case  0:
+               return l;
+               break;
+       case  1:
+               l->next = insert_sorted(cmp, e, l);
+               break;
+       default:
+               break;
+       }
+
+       /* never reached */
+       return NULL;
+}
+
+list_t
+remove_all(free_func_t free_func, list_t l)
+{
+       if (is_empty(l))
+               return l;
+       list_t lnext = l->next;
+
+       if (free_func && l->info) {
+               free_func(&(l->info));
+       }
+       free(l);
+
+       return remove_all(free_func, lnext);
+}
+
+
+info_t
+is_in(cmp_func_t cmp, info_t e, list_t l)
+{
+       return
+       (is_empty(l))
+       ? NULL
+       : (cmp(e, l->info)) == 0 ? l->info : is_in(cmp, e, l->next);
+}
+
+
+void
+apply(process_func_t process_func, list_t l)
+{
+       list_t ptr;
+       void *i;
+       foreach(i, ptr, l) {
+               process_func(i);
+       }
+}
diff --git a/ubi-utils/old-tools/src/list.h b/ubi-utils/old-tools/src/list.h
new file mode 100644 (file)
index 0000000..e8452a2
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __LIST_H__
+#define __LIST_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ */
+
+#include <stdint.h>
+
+#define foreach(elem, ptr, list)                               \
+       for (elem = list != NULL ? (typeof(elem)) head(list)    \
+                                : NULL, ptr = list;            \
+               ptr != NULL;                                    \
+               ptr = tail(ptr),                                \
+               elem = (typeof(elem)) ptr ? head(ptr) : NULL)
+
+typedef struct node* list_t;
+typedef void* info_t;
+typedef int  (*free_func_t)(info_t*);
+typedef int  (*cmp_func_t)(info_t, info_t);
+typedef void (*process_func_t)(info_t);
+
+struct node {
+       list_t next;
+       info_t  info;
+};
+
+list_t mk_empty(void);
+int    is_empty(list_t l);
+info_t is_in(cmp_func_t cmp, info_t e, list_t l);
+info_t head(list_t l);
+list_t tail(list_t l);
+list_t remove_head(list_t l);
+list_t cons(info_t e, list_t l);
+list_t prepend_elem(info_t e, list_t);
+list_t append_elem(info_t e, list_t);
+list_t remove_all(free_func_t free_func, list_t l);
+list_t insert_sorted(cmp_func_t cmp_func, info_t e, list_t l);
+void   apply(process_func_t process_func, list_t l);
+
+#endif /* __LIST_H__ */
diff --git a/ubi-utils/old-tools/src/mkbootenv.c b/ubi-utils/old-tools/src/mkbootenv.c
new file mode 100644 (file)
index 0000000..952f651
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * Create boot-parameter/pdd data from an ASCII-text input file.
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+
+#define PROGRAM_VERSION "1.3"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "mkbootenv - processes bootenv text files and convertes "
+       "them into a binary format.\n";
+
+static const char copyright [] __attribute__((unused)) =
+       "Copyright (c) International Business Machines Corp., 2006";
+
+static const char *optionsstr =
+"  -c, --copyright          Print copyright informatoin.\n"
+"  -o, --output=<fname>     Write the output data to <output> instead of\n"
+"                           stdout.\n"
+"  -?, --help               Give this help list\n"
+"      --usage              Give a short usage message\n"
+"  -V, --version            Print program version\n";
+
+static const char *usage =
+"Usage: mkbootenv [-c?V] [-o <output>] [--copyright] [--output=<output>]\n"
+"            [--help] [--usage] [--version] [bootenv-txt-file]\n";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+       FILE* fp_in;
+       FILE* fp_out;
+
+       char *arg1;
+       char **options;                 /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "co:?V", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       case 'c':
+                               fprintf(stderr, "%s\n", copyright);
+                               exit(0);
+                               break;
+                       case 'o':
+                               args->fp_out = fopen(optarg, "wb");
+                               if ((args->fp_out) == NULL) {
+                                       fprintf(stderr, "Cannot open file %s "
+                                               "for output\n", optarg);
+                                       exit(1);
+                               }
+                               break;
+                       case '?': /* help */
+                               printf("%s", doc);
+                               printf("%s", optionsstr);
+                               printf("\nReport bugs to %s\n",
+                                      PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+                       case 'V':
+                               printf("%s\n", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+                       default:
+                               printf("%s", usage);
+                               exit(-1);
+               }
+       }
+
+       if (optind < argc) {
+               args->fp_in = fopen(argv[optind++], "rb");
+               if ((args->fp_in) == NULL) {
+                       fprintf(stderr, "Cannot open file %s for input\n",
+                               argv[optind]);
+                       exit(1);
+               }
+       }
+
+       return 0;
+}
+
+int
+main(int argc, char **argv) {
+       int rc = 0;
+       bootenv_t env;
+
+       myargs args = {
+               .fp_in = stdin,
+               .fp_out = stdout,
+               .arg1 = NULL,
+               .options = NULL,
+       };
+
+       parse_opt(argc, argv, &args);
+
+       rc = bootenv_create(&env);
+       if (rc != 0) {
+               err_msg("Cannot create bootenv handle.");
+               goto err;
+       }
+       rc = bootenv_read_txt(args.fp_in, env);
+       if (rc != 0) {
+               err_msg("Cannot read bootenv from input file.");
+               goto err;
+       }
+       rc = bootenv_write(args.fp_out, env);
+       if (rc != 0) {
+               err_msg("Cannot write bootenv to output file.");
+               goto err;
+       }
+
+       if (args.fp_in != stdin) {
+               fclose(args.fp_in);
+       }
+       if (args.fp_out != stdout) {
+               fclose(args.fp_out);
+       }
+
+err:
+       bootenv_destroy(&env);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/nand2bin.c b/ubi-utils/old-tools/src/nand2bin.c
new file mode 100644 (file)
index 0000000..8c95b27
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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: Frank Haverkamp
+ *
+ * An utility to decompose NAND images and strip OOB off. Not yet finished ...
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanup
+ * 1.4 Fixed OOB output file
+ * 1.5 Added verbose output and option to set blocksize.
+ *     Added split block mode for more convenient analysis.
+ * 1.6 Fixed ECC error detection and correction.
+ * 1.7 Made NAND ECC layout configurable, the holes which were previously
+ *     filled with 0x00 are untouched now and will be 0xff just like MTD
+ *     behaves when writing the oob (haver)
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "config.h"
+#include "nandecc.h"
+#include "ecclayouts.h"
+
+#define PROGRAM_VERSION "1.7"
+
+#define ARRAY_SIZE(a)    (sizeof(a) / sizeof((a)[0]))
+#define MAXPATH                1024
+#define MIN(x,y)       ((x)<(y)?(x):(y))
+
+struct args {
+       const char *oob_file;
+       const char *output_file;
+       size_t pagesize;
+       size_t oobsize;
+       int bad_marker_offs_in_oob;
+       size_t blocksize;
+       int split_blocks;
+       size_t in_len;          /* size of input file */
+       int correct_ecc;
+       struct nand_ecclayout *nand_oob;
+
+       /* special stuff needed to get additional arguments */
+       char *arg1;
+       char **options;         /* [STRING...] */
+};
+
+static struct args myargs = {
+       .output_file = "data.bin",
+       .oob_file = "oob.bin",
+       .pagesize = 2048,
+       .blocksize = 128 * 1024,
+       .nand_oob = &ibm_nand_oob_64,
+       .in_len = 0,
+       .split_blocks = 0,
+       .correct_ecc = 0,
+       .arg1 = NULL,
+       .options = NULL,
+};
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "nand2bin - split data and OOB.\n";
+
+static const char *optionsstr =
+"  -l, --ecc-placement=<MTD,IBM> OOB placement scheme (default is IBM).\n"
+"  -o, --output=<output>      Data output file\n"
+"  -O, --oob=<oob>            OOB output file\n"
+"  -p, --pagesize=<pagesize>  NAND pagesize\n"
+"  -b, --blocksize=<blocksize> NAND blocksize\n"
+"  -s, --split-blocks         generate binaries for each block\n"
+"  -e, --correct-ecc          Correct data according to ECC info\n"
+"  -v, --verbose              verbose output\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n";
+
+static const char *usage =
+"Usage: nand2bin [-?] [-o <output>] [-O <oob>] [-p <pagesize>]\n"
+"          [--output=<output>] [--oob=<oob>] [--pagesize=<pagesize>] [--help]\n"
+"          [--usage] input.mif\n";
+
+static int verbose = 0;
+
+static struct option long_options[] = {
+       { .name = "ecc-layout", .has_arg = 1, .flag = NULL, .val = 'l' },
+       { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "oob", .has_arg = 1, .flag = NULL, .val = 'O' },
+       { .name = "pagesize", .has_arg = 1, .flag = NULL, .val = 'p' },
+       { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+       { .name = "split-blocks", .has_arg = 0, .flag = NULL, .val = 's' },
+       { .name = "correct-ecc", .has_arg = 0, .flag = NULL, .val = 'e' },
+       { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { NULL, 0, NULL, 0}
+};
+
+/*
+ * str_to_num - Convert string into number and cope with endings like
+ *              k, K, kib, KiB for kilobyte
+ *              m, M, mib, MiB for megabyte
+ */
+static uint32_t str_to_num(char *str)
+{
+       char *s = str;
+       ulong num = strtoul(s, &s, 0);
+
+       if (*s != '\0') {
+               if (strcmp(s, "KiB") == 0)
+                       num *= 1024;
+               else if (strcmp(s, "MiB") == 0)
+                       num *= 1024*1024;
+               else {
+                       fprintf(stderr, "WARNING: Wrong number format "
+                               "\"%s\", check your paramters!\n", str);
+               }
+       }
+       return num;
+}
+
+/*
+ * @brief Parse the arguments passed into the test case.
+ *
+ * @param argc           The number of arguments
+ * @param argv           The argument list
+ * @param args           Pointer to program args structure
+ *
+ * @return error
+ *
+ */
+static int parse_opt(int argc, char **argv, struct args *args)
+{
+       unsigned int i, oob_idx = 0;
+       const char *ecc_layout = NULL;
+
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "b:el:o:O:p:sv?", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+               case 'b': /* --blocksize<blocksize> */
+                       args->blocksize = str_to_num(optarg);
+                       break;
+               case 'e': /* --correct-ecc */
+                       args->correct_ecc = 1;
+                       break;
+               case 'l': /* --ecc-layout=<...> */
+                       ecc_layout = optarg;
+                       break;
+               case 'o': /* --output=<output.bin> */
+                       args->output_file = optarg;
+                       break;
+               case 'O': /* --oob=<oob.bin> */
+                       args->oob_file = optarg;
+                       break;
+               case 'p': /* --pagesize<pagesize> */
+                       args->pagesize = str_to_num(optarg);
+                       break;
+               case 's': /* --split-blocks */
+                       args->split_blocks = 1;
+                       break;
+               case 'v': /* --verbose */
+                       verbose++;
+                       break;
+               case 'V':
+                       printf("%s\n", PROGRAM_VERSION);
+                       exit(0);
+                       break;
+               case '?': /* help */
+                       printf("Usage: nand2bin [OPTION...] input.mif\n");
+                       printf("%s%s", doc, optionsstr);
+                       printf("\nReport bugs to %s\n",
+                              PACKAGE_BUGREPORT);
+                       exit(0);
+                       break;
+               default:
+                       printf("%s", usage);
+                       exit(-1);
+               }
+       }
+
+       if (optind < argc)
+               args->arg1 = argv[optind++];
+
+       switch (args->pagesize) {
+       case 512:
+               args->oobsize = 16;
+               args->bad_marker_offs_in_oob = 5;
+               oob_idx = 0;
+               break;
+       case 2048:
+               args->oobsize = 64;
+               args->bad_marker_offs_in_oob = 0;
+               oob_idx = 1;
+               break;
+       default:
+               fprintf(stderr, "Unsupported page size: %d\n", args->pagesize);
+               return -EINVAL;
+       }
+
+       /* Figure out correct oob layout if it differs from default */
+       if (ecc_layout) {
+               for (i = 0; i < ARRAY_SIZE(oob_placement); i++)
+                       if (strcmp(ecc_layout, oob_placement[i].name) == 0)
+                               args->nand_oob =
+                                       oob_placement[i].nand_oob[oob_idx];
+       }
+       return 0;
+}
+
+/*
+ * We must only compare the relevant bytes in the OOB area. All other
+ * bytes can be ignored. The information we need to do this is in
+ * nand_oob.
+ */
+static int oob_cmp(struct nand_ecclayout *nand_oob, uint8_t *oob,
+                  uint8_t *calc_oob)
+{
+       unsigned int i;
+       for (i = 0; i < nand_oob->eccbytes; i++)
+               if (oob[nand_oob->eccpos[i]] != calc_oob[nand_oob->eccpos[i]])
+                       return 1;
+       return 0;
+}
+
+static inline void hexdump(FILE *fp, const uint8_t *buf, ssize_t size)
+{
+       int k;
+
+       for (k = 0; k < size; k++) {
+               fprintf(fp, "%02x ", buf[k]);
+               if ((k & 15) == 15)
+                       fprintf(fp, "\n");
+       }
+}
+
+static int process_page(struct args *args, uint8_t *buf, uint8_t *oobbuf)
+{
+       size_t i, j;
+       int eccpoi;
+       uint8_t ecc_code[3] = { 0, }; /* temp */
+
+       /* Calculate ECC */
+       memset(oobbuf, 0xff, args->oobsize);
+       for (eccpoi = 0, i = 0; i < args->pagesize; i += 256, eccpoi += 3) {
+               nand_calculate_ecc(&buf[i], ecc_code);
+               for (j = 0; j < 3; j++)
+                       oobbuf[args->nand_oob->eccpos[eccpoi + j]] = ecc_code[j];
+       }
+       return 0;
+}
+
+static int decompose_image(struct args *args, FILE *in_fp,
+                          FILE *bin_fp, FILE *oob_fp)
+{
+       unsigned int i, eccpoi;
+       int read, rc, page = 0;
+       uint8_t *buf = malloc(args->pagesize);
+       uint8_t *oob = malloc(args->oobsize);
+       uint8_t *calc_oob = malloc(args->oobsize);
+       uint8_t *calc_buf = malloc(args->pagesize);
+       uint8_t *page_buf;
+       int pages_per_block = args->blocksize / args->pagesize;
+       int badpos = args->bad_marker_offs_in_oob;
+       uint8_t ecc_code[3] = { 0, }; /* temp */
+       uint8_t calc_ecc_code[3] = { 0, }; /* temp */
+
+       if (!buf || !oob || !calc_oob || !calc_buf)
+               exit(EXIT_FAILURE);
+
+       while (!feof(in_fp)) {
+               /* read page by page */
+               read = fread(buf, 1, args->pagesize, in_fp);
+               if (ferror(in_fp)) {
+                       fprintf(stderr, "I/O Error.");
+                       exit(EXIT_FAILURE);
+               }
+               if (read != (ssize_t)args->pagesize)
+                       break;
+
+               read = fread(oob, 1, args->oobsize, in_fp);
+               if (ferror(in_fp)) {
+                       fprintf(stderr, "I/O Error.");
+                       exit(EXIT_FAILURE);
+               }
+
+               page_buf = buf; /* default is unmodified data */
+
+               if ((page == 0 || page == 1) && (oob[badpos] != 0xff)) {
+                       if (verbose)
+                               printf("Block %d is bad\n",
+                                      page / pages_per_block);
+                       goto write_data;
+               }
+               if (args->correct_ecc)
+                       page_buf = calc_buf;
+
+               process_page(args, buf, calc_oob);
+               memcpy(calc_buf, buf, args->pagesize);
+
+               if (verbose && oob_cmp(args->nand_oob, oob, calc_oob) != 0) {
+                       printf("\nECC compare mismatch found at block %d page %d!\n",
+                              page / pages_per_block, page % pages_per_block);
+
+                       printf("Read out OOB Data:\n");
+                       hexdump(stdout, oob, args->oobsize);
+
+                       printf("Calculated OOB Data:\n");
+                       hexdump(stdout, calc_oob, args->oobsize);
+               }
+
+               /* Do correction on subpage base */
+               for (i = 0, eccpoi = 0; i < args->pagesize; i += 256, eccpoi += 3) {
+                       int j;
+
+                       for (j = 0; j < 3; j++) {
+                               ecc_code[j] = oob[args->nand_oob->eccpos[eccpoi + j]];
+                               calc_ecc_code[j] =
+                                       calc_oob[args->nand_oob->eccpos[eccpoi + j]];
+                       }
+                       rc = nand_correct_data(calc_buf + i, ecc_code,
+                                              calc_ecc_code);
+                       if (rc == -1)
+                               fprintf(stdout, "Uncorrectable ECC error at "
+                                       "block %d page %d/%d\n",
+                                       page / pages_per_block,
+                                       page % pages_per_block, i / 256);
+                       else if (rc > 0)
+                               fprintf(stdout, "Correctable ECC error at "
+                                       "block %d page %d/%d\n",
+                                       page / pages_per_block,
+                                       page % pages_per_block, i / 256);
+               }
+
+       write_data:
+               rc = fwrite(page_buf, 1, args->pagesize, bin_fp);
+               if (ferror(bin_fp)) {
+                       fprintf(stderr, "I/O Error.");
+                       exit(EXIT_FAILURE);
+               }
+               rc = fwrite(oob, 1, args->oobsize, oob_fp);
+               if (ferror(bin_fp)) {
+                       fprintf(stderr, "I/O Error.");
+                       exit(EXIT_FAILURE);
+               }
+
+               page++;
+       }
+       free(calc_buf);
+       free(calc_oob);
+       free(oob);
+       free(buf);
+       return 0;
+}
+
+static int split_blocks(struct args *args, FILE *in_fp)
+{
+       uint8_t *buf;
+       int pages_per_block = args->blocksize / args->pagesize;
+       int block_len = pages_per_block * (args->pagesize + args->oobsize);
+       int blocks = args->in_len / block_len;
+       char bname[256] = { 0, };
+       int badpos = args->bad_marker_offs_in_oob;
+       int bad_blocks = 0, i, bad_block = 0;
+       ssize_t rc;
+       FILE *b;
+
+       buf = malloc(block_len);
+       if (!buf) {
+               perror("Not enough memory");
+               exit(EXIT_FAILURE);
+       }
+
+       for (i = 0; i < blocks; i++) {
+               rc = fread(buf, 1, block_len, in_fp);
+               if (rc != block_len) {
+                       fprintf(stderr, "cannot read enough data!\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               /* do block analysis */
+               bad_block = 0;
+               if ((buf[args->pagesize + badpos] != 0xff) ||
+                   (buf[2 * args->pagesize + args->oobsize + badpos] != 0xff)) {
+                       bad_blocks++;
+                       bad_block = 1;
+               }
+               if ((verbose && bad_block) || (verbose > 1)) {
+                       printf("-- (block %d oob of page 0 and 1)\n", i);
+                       hexdump(stdout, buf + args->pagesize, args->oobsize);
+                       printf("--\n");
+                       hexdump(stdout, buf + 2 * args->pagesize +
+                               args->oobsize, args->oobsize);
+               }
+
+               /* write complete block out */
+               snprintf(bname, sizeof(bname) - 1, "%s.%d", args->arg1, i);
+               b = fopen(bname, "w+");
+               if (!b) {
+                       perror("Cannot open file");
+                       exit(EXIT_FAILURE);
+               }
+               rc = fwrite(buf, 1, block_len, b);
+               if (rc != block_len) {
+                       fprintf(stderr, "could not write all data!\n");
+                       exit(EXIT_FAILURE);
+               }
+               fclose(b);
+       }
+
+       free(buf);
+       if (bad_blocks || verbose)
+               fprintf(stderr, "%d blocks, %d bad blocks\n",
+                       blocks, bad_blocks);
+       return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+       FILE *in, *bin = NULL, *oob = NULL;
+       struct stat file_info;
+
+       parse_opt(argc, argv, &myargs);
+
+       if (!myargs.arg1) {
+               fprintf(stderr, "Please specify input file!\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (lstat(myargs.arg1, &file_info) != 0) {
+               perror("Cannot fetch file size from input file.\n");
+               exit(EXIT_FAILURE);
+       }
+       myargs.in_len = file_info.st_size;
+
+       in = fopen(myargs.arg1, "r");
+       if (!in) {
+               perror("Cannot open file");
+               exit(EXIT_FAILURE);
+       }
+
+       if (myargs.split_blocks) {
+               split_blocks(&myargs, in);
+               goto out;
+       }
+
+       bin = fopen(myargs.output_file, "w+");
+       if (!bin) {
+               perror("Cannot open file");
+               exit(EXIT_FAILURE);
+       }
+       oob = fopen(myargs.oob_file, "w+");
+       if (!oob) {
+               perror("Cannot open file");
+               exit(EXIT_FAILURE);
+       }
+       decompose_image(&myargs, in, bin, oob);
+
+ out:
+       if (in)  fclose(in);
+       if (bin) fclose(bin);
+       if (oob) fclose(oob);
+       exit(EXIT_SUCCESS);
+}
diff --git a/ubi-utils/old-tools/src/nandcorr.c b/ubi-utils/old-tools/src/nandcorr.c
new file mode 100644 (file)
index 0000000..caa07e2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * ECC algorithm for NAND FLASH. Detects and corrects 1 bit errors in
+ * a 256 bytes of data.
+ *
+ * Reimplement by Thomas Gleixner after staring long enough at the
+ * mess in drivers/mtd/nand/nandecc.c
+ *
+ */
+
+#include "nandecc.h"
+
+static int countbits(uint32_t byte)
+{
+       int res = 0;
+
+       for (;byte; byte >>= 1)
+               res += byte & 0x01;
+       return res;
+}
+
+/**
+ * @dat:       data which should be corrected
+ * @read_ecc:  ecc information read from flash
+ * @calc_ecc:  calculated ecc information from the data
+ * @return:    number of corrected bytes
+ *             or -1 when no correction is possible
+ */
+int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc,
+                     const uint8_t *calc_ecc)
+{
+       uint8_t s0, s1, s2;
+
+       /*
+        * Do error detection
+        *
+        * Be careful, the index magic is due to a pointer to a
+        * uint32_t.
+        */
+       s0 = calc_ecc[0] ^ read_ecc[0];
+       s1 = calc_ecc[1] ^ read_ecc[1];
+       s2 = calc_ecc[2] ^ read_ecc[2];
+
+       if ((s0 | s1 | s2) == 0)
+               return 0;
+
+       /* Check for a single bit error */
+       if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+           ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+           ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+               uint32_t byteoffs, bitnum;
+
+               byteoffs = (s1 << 0) & 0x80;
+               byteoffs |= (s1 << 1) & 0x40;
+               byteoffs |= (s1 << 2) & 0x20;
+               byteoffs |= (s1 << 3) & 0x10;
+
+               byteoffs |= (s0 >> 4) & 0x08;
+               byteoffs |= (s0 >> 3) & 0x04;
+               byteoffs |= (s0 >> 2) & 0x02;
+               byteoffs |= (s0 >> 1) & 0x01;
+
+               bitnum = (s2 >> 5) & 0x04;
+               bitnum |= (s2 >> 4) & 0x02;
+               bitnum |= (s2 >> 3) & 0x01;
+
+               dat[byteoffs] ^= (1 << bitnum);
+
+               return 1;
+       }
+
+       if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+               return 1;
+
+       return -1;
+}
+
diff --git a/ubi-utils/old-tools/src/nandecc.c b/ubi-utils/old-tools/src/nandecc.c
new file mode 100644 (file)
index 0000000..71660ef
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ * drivers/mtd/nand/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ *                        Toshiba America Electronics Components, Inc.
+ *
+ * This file 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 or (at your option) any
+ * later version.
+ *
+ * This file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include "nandecc.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+       0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+       0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+       0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+       0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+       0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+       0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+       0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+       0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+       0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+       0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+       0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+       0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+       0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+       0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+       0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+       0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+       0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+       0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+       0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+       0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+       0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+       0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+       0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+       0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+       0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+       0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+       0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+       0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+       0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+       0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+       0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+       0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+/**
+ * nand_trans_result - [GENERIC] create non-inverted ECC
+ * @reg2:      line parity reg 2
+ * @reg3:      line parity reg 3
+ * @ecc_code:  ecc
+ *
+ * Creates non-inverted ECC code from line parity
+ */
+static void nand_trans_result(uint8_t reg2, uint8_t reg3,
+       uint8_t *ecc_code)
+{
+       uint8_t a, b, i, tmp1, tmp2;
+
+       /* Initialize variables */
+       a = b = 0x80;
+       tmp1 = tmp2 = 0;
+
+       /* Calculate first ECC byte */
+       for (i = 0; i < 4; i++) {
+               if (reg3 & a)           /* LP15,13,11,9 --> ecc_code[0] */
+                       tmp1 |= b;
+               b >>= 1;
+               if (reg2 & a)           /* LP14,12,10,8 --> ecc_code[0] */
+                       tmp1 |= b;
+               b >>= 1;
+               a >>= 1;
+       }
+
+       /* Calculate second ECC byte */
+       b = 0x80;
+       for (i = 0; i < 4; i++) {
+               if (reg3 & a)           /* LP7,5,3,1 --> ecc_code[1] */
+                       tmp2 |= b;
+               b >>= 1;
+               if (reg2 & a)           /* LP6,4,2,0 --> ecc_code[1] */
+                       tmp2 |= b;
+               b >>= 1;
+               a >>= 1;
+       }
+
+       /* Store two of the ECC bytes */
+       ecc_code[1] = tmp1;
+       ecc_code[0] = tmp2;
+}
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for
+ * 256 byte block
+ *
+ * @dat:       raw data
+ * @ecc_code:  buffer for ECC
+ */
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code)
+{
+       uint8_t idx, reg1, reg2, reg3;
+       int j;
+
+       /* Initialize variables */
+       reg1 = reg2 = reg3 = 0;
+       ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
+
+       /* Build up column parity */
+       for(j = 0; j < 256; j++) {
+
+               /* Get CP0 - CP5 from table */
+               idx = nand_ecc_precalc_table[dat[j]];
+               reg1 ^= (idx & 0x3f);
+
+               /* All bit XOR = 1 ? */
+               if (idx & 0x40) {
+                       reg3 ^= (uint8_t) j;
+                       reg2 ^= ~((uint8_t) j);
+               }
+       }
+
+       /* Create non-inverted ECC code from line parity */
+       nand_trans_result(reg2, reg3, ecc_code);
+
+       /* Calculate final ECC code */
+       ecc_code[0] = ~ecc_code[0];
+       ecc_code[1] = ~ecc_code[1];
+       ecc_code[2] = ((~reg1) << 2) | 0x03;
+       return 0;
+}
diff --git a/ubi-utils/old-tools/src/nandecc.h b/ubi-utils/old-tools/src/nandecc.h
new file mode 100644 (file)
index 0000000..bcf1982
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _NAND_ECC_H
+#define _NAND_ECC_H
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ *
+ * NAND ecc functions
+ */
+
+#include <stdint.h>
+
+int nand_calculate_ecc(const uint8_t *dat, uint8_t *ecc_code);
+int nand_correct_data(uint8_t *dat, const uint8_t *read_ecc,
+                     const uint8_t *calc_ecc);
+
+#endif
diff --git a/ubi-utils/old-tools/src/pddcustomize.c b/ubi-utils/old-tools/src/pddcustomize.c
new file mode 100644 (file)
index 0000000..515efd6
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * PDD (platform description data) contains a set of system specific
+ * boot-parameters. Some of those parameters need to be handled
+ * special on updates, e.g. the MAC addresses. They must also be kept
+ * if the system is updated and one must be able to modify them when
+ * the system has booted the first time. This tool is intended to do
+ * PDD modification.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ * 1.5 Migrated to new libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "bootenv.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "libubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.5"
+
+#define DEFAULT_DEV_PATTERN    "/dev/ubi%d"
+#define DEFAULT_VOL_PATTERN    "/dev/ubi%d_%d"
+
+typedef enum action_t {
+       ACT_NORMAL   = 0,
+       ACT_LIST,
+       ACT_ARGP_ABORT,
+       ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do {                        \
+       args->action = ACT_ARGP_ABORT;  \
+} while (0)
+
+#define ERR_ARGP do {                  \
+       args->action = ACT_ARGP_ERR;    \
+} while (0)
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "pddcustomize - customize bootenv and pdd values.\n";
+
+static const char *optionsstr =
+"  -b, --both                 Mirror updated PDD to redundand copy.\n"
+"  -c, --copyright            Print copyright information.\n"
+"  -i, --input=<input>        Binary input file. For debug purposes.\n"
+"  -l, --list                 List card bootenv/pdd values.\n"
+"  -o, --output=<output>      Binary output file. For debug purposes.\n"
+"  -s, --side=<seqnum>        The side/seqnum to update.\n"
+"  -x, --host                 use x86 platform for debugging.\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"  -V, --version              Print program version\n";
+
+static const char *usage =
+"Usage: pddcustomize [-bclx?V] [-i <input>] [-o <output>] [-s <seqnum>]\n"
+"           [--both] [--copyright] [--input=<input>] [--list]\n"
+"           [--output=<output>] [--side=<seqnum>] [--host] [--help] [--usage]\n"
+"           [--version] [key=value] [...]\n";
+
+struct option long_options[] = {
+       { .name = "both", .has_arg = 0, .flag = NULL, .val = 'b' },
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "input", .has_arg = 1, .flag = NULL, .val = 'i' },
+       { .name = "list", .has_arg = 0, .flag = NULL, .val = 'l' },
+       { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+       { .name = "host", .has_arg = 0, .flag = NULL, .val = 'x' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+       "Copyright IBM Corp 2006";
+
+typedef struct myargs {
+       action_t action;
+       const char* file_in;
+       const char* file_out;
+       int both;
+       int side;
+       int x86;                /* X86 host, use files for testing */
+       bootenv_t env_in;
+
+       char *arg1;
+       char **options;         /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+       uint32_t i = strtoul(str, NULL, 0);
+
+       if ((i != 0) && (i != 1)) {
+               return -1;
+       }
+
+       return i;
+}
+
+static int
+extract_pair(bootenv_t env, const char* str)
+{
+       int rc = 0;
+       char* key;
+       char* val;
+
+       key = strdup(str);
+       if (key == NULL)
+               return -ENOMEM;
+
+       val = strstr(key, "=");
+       if (val == NULL) {
+               err_msg("Wrong argument: %s\n"
+                       "Expecting key=value pair.\n", str);
+               rc = -1;
+               goto err;
+       }
+
+       *val = '\0'; /* split strings */
+       val++;
+       rc = bootenv_set(env, key, val);
+
+err:
+       free(key);
+       return rc;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       int rc = 0;
+
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "clbxs:i:o:?V",
+                                 long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       case 'c':
+                               err_msg("%s\n", copyright);
+                               ABORT_ARGP;
+                               break;
+                       case 'l':
+                               args->action = ACT_LIST;
+                               break;
+                       case 'b':
+                               args->both = 1;
+                               break;
+                       case 'x':
+                               args->x86 = 1;
+                               break;
+                       case 's':
+                               args->side = get_update_side(optarg);
+                               if (args->side < 0) {
+                                       err_msg("Unsupported seqnum: %d.\n"
+                                               "Supported seqnums are "
+                                               "'0' and '1'\n",
+                                               args->side, optarg);
+                                       ERR_ARGP;
+                               }
+                               break;
+                       case 'i':
+                               args->file_in = optarg;
+                               break;
+                       case 'o':
+                               args->file_out = optarg;
+                               break;
+                       case '?': /* help */
+                               err_msg("Usage: pddcustomize [OPTION...] "
+                                       "[key=value] [...]");
+                               err_msg("%s", doc);
+                               err_msg("%s", optionsstr);
+                               err_msg("\nReport bugs to %s",
+                                       PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+                       case 'V':
+                               err_msg("%s", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+                       default:
+                               err_msg("%s", usage);
+                               exit(-1);
+               }
+       }
+
+       if (optind < argc) {
+               rc = extract_pair(args->env_in, argv[optind++]);
+               if (rc != 0)
+                       ERR_ARGP;
+       }
+
+       return 0;
+}
+
+static int
+list_bootenv(bootenv_t env)
+{
+       int rc = 0;
+       rc = bootenv_write_txt(stdout, env);
+       if (rc != 0) {
+               err_msg("Cannot list bootenv/pdd. rc: %d\n", rc);
+               goto err;
+       }
+err:
+       return rc;
+}
+
+static int
+process_key_value(bootenv_t env_in, bootenv_t env)
+{
+       int rc = 0;
+       size_t size, i;
+       const char* tmp;
+       const char** key_vec = NULL;
+
+       rc = bootenv_get_key_vector(env_in, &size, 0, &key_vec);
+       if (rc != 0)
+               goto err;
+
+       for (i = 0; i < size; i++) {
+               rc = bootenv_get(env_in, key_vec[i], &tmp);
+               if (rc != 0) {
+                       err_msg("Cannot read value to input key: %s. rc: %d\n",
+                                       key_vec[i], rc);
+                       goto err;
+               }
+               rc = bootenv_set(env, key_vec[i], tmp);
+               if (rc != 0) {
+                       err_msg("Cannot set value key: %s. rc: %d\n",
+                                       key_vec[i], rc);
+                       goto err;
+               }
+       }
+
+err:
+       if (key_vec != NULL)
+               free(key_vec);
+       return rc;
+}
+
+static int
+read_bootenv(const char* file, bootenv_t env)
+{
+       int rc = 0;
+       FILE* fp_in = NULL;
+
+       fp_in = fopen(file, "rb");
+       if (fp_in == NULL) {
+               err_msg("Cannot open file: %s\n", file);
+               return -EIO;
+       }
+
+       rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+       if (rc != 0) {
+               err_msg("Cannot read bootenv from file %s. rc: %d\n",
+                       file, rc);
+               goto err;
+       }
+
+err:
+       fclose(fp_in);
+       return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_read_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+       libubi_t ulib;
+       int rc = 0;
+       char path[PATH_MAX];
+       FILE* fp_in = NULL;
+
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               err_msg("Cannot allocate ubi structure\n");
+               return -1;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+       fp_in = fopen(path, "r");
+       if (fp_in == NULL) {
+               err_msg("Cannot open volume:%d number:%d\n", devno, id);
+               goto err;
+       }
+
+       rc = bootenv_read(fp_in, env, BOOTENV_MAXSIZE);
+       if (rc != 0) {
+               err_msg("Cannot read volume:%d number:%d\n", devno, id);
+               goto err;
+       }
+
+err:
+       if (fp_in)
+               fclose(fp_in);
+       libubi_close(ulib);
+       return rc;
+}
+
+static int
+write_bootenv(const char* file, bootenv_t env)
+{
+       int rc = 0;
+       FILE* fp_out;
+
+       fp_out = fopen(file, "wb");
+       if (fp_out == NULL) {
+               err_msg("Cannot open file: %s\n", file);
+               return -EIO;
+       }
+
+       rc = bootenv_write(fp_out, env);
+       if (rc != 0) {
+               err_msg("Cannot write bootenv to file %s. rc: %d\n", file, rc);
+               goto err;
+       }
+
+err:
+       fclose(fp_out);
+       return rc;
+}
+
+/*
+ * Read bootenv from ubi volume
+ */
+static int
+ubi_write_bootenv(uint32_t devno, uint32_t id, bootenv_t env)
+{
+       libubi_t ulib;
+       int rc = 0;
+       char path[PATH_MAX];
+       FILE* fp_out = NULL;
+       size_t nbytes ;
+
+       rc = bootenv_size(env, &nbytes);
+       if (rc) {
+               err_msg("Cannot determine size of bootenv structure\n");
+               return rc;
+       }
+       ulib = libubi_open();
+       if (ulib == NULL) {
+               err_msg("Cannot allocate ubi structure\n");
+               return rc;
+       }
+
+       snprintf(path, PATH_MAX, DEFAULT_VOL_PATTERN, devno, id);
+
+       fp_out = fopen(path, "r+");
+       if (fp_out == NULL) {
+               err_msg("Cannot fopen volume:%d number:%d\n", devno, id);
+               goto err;
+       }
+
+       rc = bootenv_write(fp_out, env);
+       if (rc != 0) {
+               err_msg("Cannot write bootenv to volume %d number:%d\n",
+                       devno, id);
+               goto err;
+       }
+
+err:
+       if( fp_out )
+               fclose(fp_out);
+       libubi_close(ulib);
+       return rc;
+}
+
+static int
+do_mirror(int volno)
+{
+       char errbuf[1024];
+       uint32_t ids[2];
+       int rc;
+       int src_volno_idx = 0;
+
+       ids[0] = EXAMPLE_BOOTENV_VOL_ID_1;
+       ids[1] = EXAMPLE_BOOTENV_VOL_ID_2;
+
+       if (volno == EXAMPLE_BOOTENV_VOL_ID_2)
+               src_volno_idx = 1;
+
+       rc = ubimirror(EXAMPLE_UBI_DEVICE, src_volno_idx, ids, 2, errbuf,
+                      sizeof errbuf);
+       if( rc )
+               err_msg(errbuf);
+       return rc;
+}
+
+int
+main(int argc, char **argv) {
+       int rc = 0;
+       bootenv_t env = NULL;
+       uint32_t boot_volno;
+       myargs args = {
+               .action = ACT_NORMAL,
+               .file_in  = NULL,
+               .file_out = NULL,
+               .side = -1,
+               .x86 = 0,
+               .both = 0,
+               .env_in = NULL,
+
+               .arg1 = NULL,
+               .options = NULL,
+       };
+
+       rc = bootenv_create(&env);
+       if (rc != 0) {
+               err_msg("Cannot create bootenv handle. rc: %d", rc);
+               goto err;
+       }
+
+       rc = bootenv_create(&(args.env_in));
+       if (rc != 0) {
+               err_msg("Cannot create bootenv handle. rc: %d", rc);
+               goto err;
+       }
+
+       parse_opt(argc, argv, &args);
+       if (args.action == ACT_ARGP_ERR) {
+               rc = -1;
+               goto err;
+       }
+       if (args.action == ACT_ARGP_ABORT) {
+               rc = 0;
+               goto out;
+       }
+
+       if ((args.side == 0) || (args.side == -1))
+               boot_volno = EXAMPLE_BOOTENV_VOL_ID_1;
+       else
+               boot_volno = EXAMPLE_BOOTENV_VOL_ID_2;
+
+       if( args.x86 )
+               rc = read_bootenv(args.file_in, env);
+       else
+               rc = ubi_read_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+       if (rc != 0) {
+               goto err;
+       }
+
+       if (args.action == ACT_LIST) {
+               rc = list_bootenv(env);
+               if (rc != 0) {
+                       goto err;
+               }
+               goto out;
+       }
+
+       rc = process_key_value(args.env_in, env);
+       if (rc != 0) {
+               goto err;
+       }
+
+       if( args.x86 )
+               rc = write_bootenv(args.file_in, env);
+       else
+               rc = ubi_write_bootenv(EXAMPLE_UBI_DEVICE, boot_volno, env);
+       if (rc != 0) {
+               goto err;
+       }
+       if( args.both )         /* No side specified, update both */
+               rc = do_mirror(boot_volno);
+
+ out:
+ err:
+       bootenv_destroy(&env);
+       bootenv_destroy(&(args.env_in));
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/peb.c b/ubi-utils/old-tools/src/peb.c
new file mode 100644 (file)
index 0000000..160a463
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "peb.h"
+
+int
+peb_cmp(peb_t eb_1, peb_t eb_2)
+{
+       assert(eb_1);
+       assert(eb_2);
+
+       return eb_1->num == eb_2->num ? 0
+               : eb_1->num > eb_2->num ? 1 : -1;
+}
+
+int
+peb_new(uint32_t eb_num, uint32_t peb_size, peb_t *peb)
+{
+       int rc = 0;
+
+       peb_t res = (peb_t) malloc(sizeof(struct peb));
+       if (!res) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       res->num  = eb_num;
+       res->size = peb_size;
+       res->data = (uint8_t*) malloc(res->size * sizeof(uint8_t));
+       if (!res->data) {
+               rc = -ENOMEM;
+               goto err;
+       }
+       memset(res->data, 0xff, res->size);
+
+       *peb = res;
+       return 0;
+err:
+       if (res) {
+               if (res->data)
+                       free(res->data);
+               free(res);
+       }
+       *peb = NULL;
+       return rc;
+}
+
+int
+peb_fill(peb_t peb, uint8_t* buf, size_t buf_size)
+{
+       if (!peb)
+               return -EINVAL;
+
+       if (buf_size > peb->size)
+               return -EINVAL;
+
+       memcpy(peb->data, buf, buf_size);
+       return 0;
+}
+
+int
+peb_write(FILE* fp_out, peb_t peb)
+{
+       size_t written = 0;
+
+       if (peb == NULL)
+               return -EINVAL;
+
+       written = fwrite(peb->data, 1, peb->size, fp_out);
+
+       if (written != peb->size)
+               return -EIO;
+
+       return 0;
+}
+
+int
+peb_free(peb_t* peb)
+{
+       peb_t tmp = *peb;
+       if (tmp) {
+               if (tmp->data)
+                       free(tmp->data);
+               free(tmp);
+       }
+       *peb = NULL;
+
+       return 0;
+}
+
+void peb_dump(FILE* fp_out, peb_t peb)
+{
+       fprintf(fp_out, "num: %08d\tsize: 0x%08x\n", peb->num, peb->size);
+}
diff --git a/ubi-utils/old-tools/src/peb.h b/ubi-utils/old-tools/src/peb.h
new file mode 100644 (file)
index 0000000..246bce8
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __RAW_BLOCK_H__
+#define __RAW_BLOCK_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+typedef struct peb *peb_t;
+struct peb {
+       uint32_t num;           /* Physical eraseblock number
+                                * in the RAW file. */
+       uint32_t size;          /* Data Size (equals physical
+                                * erase block size) */
+       uint8_t* data;          /* Data buffer */
+};
+
+int  peb_new(uint32_t peb_num, uint32_t peb_size, peb_t* peb);
+int  peb_free(peb_t* peb);
+int  peb_cmp(peb_t peb_1, peb_t peb_2);
+int  peb_write(FILE* fp_out, peb_t peb);
+void peb_dump(FILE* fp_out, peb_t peb);
+
+#endif /* __RAW_BLOCK_H__ */
diff --git a/ubi-utils/old-tools/src/pfi.c b/ubi-utils/old-tools/src/pfi.c
new file mode 100644 (file)
index 0000000..fa835e2
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * @file pfi.c
+ *
+ * @author Oliver Lohmann
+ *        Andreas Arnez
+ *        Joern Engel
+ *        Frank Haverkamp
+ *
+ * @brief libpfi holds all code to create and process pfi files.
+ *
+ * <oliloh@de.ibm.com> Wed Feb 8 11:38:22 CET 2006: Initial creation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include "pfi.h"
+
+#define PFI_MAGIC     "PFI!\n"
+#define PFI_DATA      "DATA\n" /* The same size as PFI_MAGIC */
+#define PFI_MAGIC_LEN 5
+
+static const char copyright [] __attribute__((unused)) =
+       "Copyright (c) International Business Machines Corp., 2006";
+
+enum key_id {
+       /* version 1 */
+       key_version,          /* must be index position 0! */
+       key_mode,
+       key_size,
+       key_crc,
+       key_label,
+       key_flags,
+       key_ubi_ids,
+       key_ubi_size,
+       key_ubi_type,
+       key_ubi_names,
+       key_ubi_alignment,
+       key_raw_starts,
+       key_raw_total_size,
+       num_keys,
+};
+
+struct pfi_header {
+       char defined[num_keys];  /* reserve all possible keys even if
+                                   version does not require this. */
+       int mode_no;             /* current mode no. -> can only increase */
+       union {
+               char *str;
+               uint32_t num;
+       } value[num_keys];
+};
+
+
+#define PFI_MANDATORY      0x0001
+#define PFI_STRING         0x0002
+#define PFI_LISTVALUE      0x0004      /* comma seperated list of nums */
+#define PFI_MANDATORY_UBI   0x0008
+#define PFI_MANDATORY_RAW   0x0010
+
+struct key_descriptor {
+       enum key_id id;
+       const char *name;
+       uint32_t flags;
+};
+
+static const struct key_descriptor key_desc_v1[] = {
+       { key_version, "version", PFI_MANDATORY },
+       { key_mode, "mode", PFI_MANDATORY | PFI_STRING },
+       { key_size, "size", PFI_MANDATORY },
+       { key_crc, "crc", PFI_MANDATORY },
+       { key_label, "label", PFI_MANDATORY | PFI_STRING },
+       { key_flags, "flags", PFI_MANDATORY },
+       { key_ubi_ids, "ubi_ids", PFI_MANDATORY_UBI | PFI_STRING },
+       { key_ubi_size, "ubi_size", PFI_MANDATORY_UBI },
+       { key_ubi_type, "ubi_type", PFI_MANDATORY_UBI | PFI_STRING },
+       { key_ubi_names, "ubi_names", PFI_MANDATORY_UBI | PFI_STRING },
+       { key_ubi_alignment, "ubi_alignment", PFI_MANDATORY_UBI },
+       { key_raw_starts, "raw_starts", PFI_MANDATORY_RAW | PFI_STRING },
+       { key_raw_total_size, "raw_total_size", PFI_MANDATORY_RAW },
+};
+
+static const struct key_descriptor *key_descriptors[] = {
+       NULL,
+       key_desc_v1,                                       /* version 1 */
+};
+
+static const int key_descriptors_max[] = {
+       0,                                                 /* version 0 */
+       sizeof(key_desc_v1)/sizeof(struct key_descriptor), /* version 1 */
+};
+
+#define ARRAY_SIZE(a)    (sizeof(a) / sizeof((a)[0]))
+
+static const char* modes[] = {"raw", "ubi"}; /* order isn't arbitrary! */
+
+/* latest version contains all possible keys */
+static const struct key_descriptor *key_desc = key_desc_v1;
+
+#define PFI_IS_UBI(mode) \
+       (((mode) != NULL) && (strcmp("ubi", (mode)) == 0))
+
+#define PFI_IS_RAW(mode) \
+       (((mode) != NULL) && (strcmp("raw", (mode)) == 0))
+
+/**
+ * @return      <0     On Error.
+ *             >=0     Mode no.
+ */
+static int
+get_mode_no(const char* mode)
+{
+       int i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(modes); i++)
+               if (strcmp(mode, modes[i]) == 0)
+                       return i;
+       return -1;
+}
+
+static int
+find_key_by_name (const char *name)
+{
+       int i;
+
+       for (i = 0; i < num_keys; i++) {
+               if (strcmp(name, key_desc[i].name) == 0)
+                       return i;
+       }
+       return -1;
+}
+
+static int
+check_valid (pfi_header head)
+{
+       int i;
+       int max_keys;
+       uint32_t version;
+       const char *mode;
+       const struct key_descriptor *desc;
+       uint32_t to_check = PFI_MANDATORY;
+
+       /*
+        * For the validity check the list of possible keys depends on
+        * the version of the PFI file used.
+        */
+       version = head->value[key_version].num;
+       if (version > PFI_HDRVERSION)
+               return PFI_ENOHEADER;
+
+       max_keys = key_descriptors_max[version];
+       desc = key_descriptors[version];
+
+       if (!desc)
+               return PFI_ENOVERSION;
+
+       mode = head->value[key_mode].str;
+       if (PFI_IS_UBI(mode)) {
+               to_check |= PFI_MANDATORY_UBI;
+       }
+       else if (PFI_IS_RAW(mode)) {
+               to_check |= PFI_MANDATORY_RAW;
+       }
+       else { /* neither UBI nor RAW == ERR */
+               return PFI_EINSUFF;
+       }
+
+       for (i = 0; i < max_keys; i++) {
+               if ((desc[i].flags & to_check) && !head->defined[i]) {
+                       fprintf(stderr, "libpfi: %s missing\n", desc[i].name);
+                       return PFI_EINSUFF;
+               }
+       }
+
+       return 0;
+}
+
+int pfi_header_init (pfi_header *head)
+{
+       int i;
+       pfi_header self = (pfi_header) malloc(sizeof(*self));
+
+       *head = self;
+       if (self == NULL)
+               return PFI_ENOMEM;
+
+       /* initialize maximum number of possible keys */
+       for (i = 0; i < num_keys; i++) {
+               memset(self, 0, sizeof(*self));
+               self->defined[i] = 0;
+       }
+
+       return 0;
+}
+
+int pfi_header_destroy (pfi_header *head)
+{
+       int i;
+       pfi_header self = *head;
+
+       for (i = 0; i < num_keys; i++) {
+               if (self->defined[i] && (key_desc[i].flags & PFI_STRING) &&
+                   self->value[i].str) {
+                       free(self->value[i].str);
+               }
+       }
+       free(*head);
+       *head = NULL;
+       return 0;
+}
+
+int pfi_header_setnumber (pfi_header head,
+                          const char *key, uint32_t value)
+{
+       int key_id = find_key_by_name(key);
+
+       if (key_id < 0)
+               return PFI_EUNDEF;
+
+       if (key_desc[key_id].flags & PFI_STRING)
+               return PFI_EBADTYPE;
+
+       head->value[key_id].num = value;
+       head->defined[key_id] = 1;
+       return 0;
+}
+
+int pfi_header_setvalue (pfi_header head,
+                         const char *key, const char *value)
+{
+       int key_id = find_key_by_name(key);
+
+       if (value == NULL)
+               return PFI_EINSUFF;
+
+       if ((key_id < 0) || (key_id >= num_keys))
+               return PFI_EUNDEF;
+
+       if (key_desc[key_id].flags & PFI_STRING) {
+               /*
+                * The value is a string. Copy to a newly allocated
+                * buffer. Delete the old value, if already set.
+                */
+               size_t len = strlen(value) + 1;
+               char *old_str = NULL;
+               char *str;
+
+               old_str = head->value[key_id].str;
+               if (old_str != NULL)
+                       free(old_str);
+
+               str = head->value[key_id].str = (char *) malloc(len);
+               if (str == NULL)
+                       return PFI_ENOMEM;
+
+               strcpy(str, value);
+       } else {
+               int len;
+               int ret;
+               /* FIXME: here we assume that the value is always
+                  given in hex and starts with '0x'. */
+               ret = sscanf(value, "0x%x%n", &head->value[key_id].num, &len);
+               if (ret < 1 || value[len] != '\0')
+                       return PFI_EBADTYPE;
+       }
+       head->defined[key_id] = 1;
+       return 0;
+}
+
+int pfi_header_getnumber (pfi_header head,
+                          const char *key, uint32_t *value)
+{
+       int key_id = find_key_by_name(key);
+
+       if (key_id < 0)
+               return PFI_EUNDEF;
+
+       if (key_desc[key_id].flags & PFI_STRING)
+               return PFI_EBADTYPE;
+
+       if (!head->defined[key_id])
+               return PFI_EUNDEF;
+
+       *value = head->value[key_id].num;
+       return 0;
+}
+
+int pfi_header_getstring (pfi_header head,
+                          const char *key, char *value, size_t size)
+{
+       int key_id = find_key_by_name(key);
+
+       if (key_id < 0)
+               return PFI_EUNDEF;
+
+       if (!(key_desc[key_id].flags & PFI_STRING))
+               return PFI_EBADTYPE;
+
+       if (!head->defined[key_id])
+               return PFI_EUNDEF;
+
+       strncpy(value, head->value[key_id].str, size-1);
+       value[size-1] = '\0';
+       return 0;
+}
+
+int pfi_header_write (FILE *out, pfi_header head)
+{
+       int i;
+       int ret;
+
+       pfi_header_setnumber(head, "version", PFI_HDRVERSION);
+
+       if ((ret = check_valid(head)) != 0)
+               return ret;
+
+       /* OK.  Now write the header. */
+
+       ret = fwrite(PFI_MAGIC, 1, PFI_MAGIC_LEN, out);
+       if (ret < PFI_MAGIC_LEN)
+               return ret;
+
+
+       for (i = 0; i < num_keys; i++) {
+               if (!head->defined[i])
+                       continue;
+
+               ret = fprintf(out, "%s=", key_desc[i].name);
+               if (ret < 0)
+                       return PFI_EFILE;
+
+               if (key_desc[i].flags & PFI_STRING) {
+                       ret = fprintf(out, "%s", head->value[i].str);
+                       if (ret < 0)
+                               return PFI_EFILE;
+               } else {
+                       ret = fprintf(out, "0x%8x", head->value[i].num);
+                       if (ret < 0)
+                               return PFI_EFILE;
+
+               }
+               ret = fprintf(out, "\n");
+               if (ret < 0)
+                       return PFI_EFILE;
+       }
+       ret = fprintf(out, "\n");
+       if (ret < 0)
+               return PFI_EFILE;
+
+       ret = fflush(out);
+       if (ret != 0)
+               return PFI_EFILE;
+
+       return 0;
+}
+
+int pfi_header_read (FILE *in, pfi_header head)
+{
+       char magic[PFI_MAGIC_LEN];
+       char mode[PFI_KEYWORD_LEN];
+       char buf[256];
+
+       if (PFI_MAGIC_LEN != fread(magic, 1, PFI_MAGIC_LEN, in))
+               return PFI_EFILE;
+       if (memcmp(magic, PFI_MAGIC, PFI_MAGIC_LEN) != 0)  {
+               if (memcmp(magic, PFI_DATA, PFI_MAGIC_LEN) == 0) {
+                       return PFI_DATA_START;
+               }
+               return PFI_ENOHEADER;
+       }
+
+       while (fgets(buf, sizeof(buf), in) != NULL && buf[0] != '\n') {
+               char *value;
+               char *end;
+               value = strchr(buf, '=');
+               if (value == NULL)
+                       return PFI_ENOHEADER;
+
+               *value = '\0';
+               value++;
+               end = strchr(value, '\n');
+               if (end)
+                      *end = '\0';
+
+               if (pfi_header_setvalue(head, buf, value))
+                       return PFI_ENOHEADER;
+       }
+
+       if (check_valid(head) != 0)
+               return PFI_ENOHEADER;
+
+       /* set current mode no. in head */
+       pfi_header_getstring(head, "mode", mode, PFI_KEYWORD_LEN);
+       if (head->mode_no > get_mode_no(mode)) {
+               return PFI_EMODE;
+       }
+       head->mode_no = get_mode_no(mode);
+       return 0;
+}
+
+int pfi_header_dump (FILE *out, pfi_header head __attribute__((__unused__)))
+{
+       fprintf(out, "Sorry not implemented yet. Write mail to "
+               "Andreas Arnez and complain!\n");
+       return 0;
+}
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data)
+{
+       int rc;
+       pfi_header header;
+
+       rc = pfi_header_init (&header);
+       if (0 != rc)
+               return rc;
+       if (!func)
+               return PFI_EINVAL;
+
+       while ((0 == rc) && !feof(in)) {
+               /*
+                * Read header and check consistency of the fields.
+                */
+               rc = pfi_header_read( in, header );
+               if (0 != rc)
+                       break;
+               if (func) {
+                       rc = func(in, header, priv_data);
+                       if (rc != 0)
+                               break;
+               }
+       }
+
+       pfi_header_destroy(&header);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/pfi.h b/ubi-utils/old-tools/src/pfi.h
new file mode 100644 (file)
index 0000000..8c5cc07
--- /dev/null
@@ -0,0 +1,244 @@
+#ifndef __pfi_h
+#define __pfi_h
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/**
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ *         Andreas Arnez <arnez@de.ibm.com>
+ *         Joern Engel <engeljoe@de.ibm.com>
+ *         Frank Haverkamp <haverkam@de.ibm.com>
+ *
+ * @brief libpfi will hold all code to create and process pfi
+ * images. Definitions made in this file are equaly usable for the
+ * development host and the target system.
+ *
+ * @note This header additionally holds the official definitions for
+ * the pfi headers.
+ */
+
+#include <stdio.h>             /* FILE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Definitions. */
+
+#define PFI_HDRVERSION 1       /* current header version */
+
+#define PFI_ENOVERSION 1       /* unknown version */
+#define PFI_ENOHEADER  2       /* not a pfi header */
+#define PFI_EINSUFF    3       /* insufficient information */
+#define PFI_EUNDEF     4       /* key not defined */
+#define PFI_ENOMEM     5       /* out of memory */
+#define PFI_EBADTYPE   6       /* bad data type */
+#define PFI_EFILE      7       /* file I/O error: see errno */
+#define PFI_EFILEINVAL 8       /* file format not valid */
+#define PFI_EINVAL     9       /* invalid parameter */
+#define PFI_ERANGE     10      /* invalid range */
+#define PFI_EMODE      11      /* expecting other mode in this header */
+#define PFI_DATA_START 12      /* data section starts */
+#define PFI_EMAX       13      /* should be always larger as the largest
+                                  error code */
+
+#define PFI_LABEL_LEN  64      /* This is the maximum length for a
+                                  PFI header label */
+#define PFI_KEYWORD_LEN 32     /* This is the maximum length for an
+                                  entry in the mode and type fields */
+
+#define PFI_UBI_MAX_VOLUMES 128
+#define PFI_UBI_VOL_NAME_LEN 127
+
+/**
+ * @brief The pfi header allows to set flags which influence the flashing
+ * behaviour.
+ */
+#define PFI_FLAG_PROTECTED   0x00000001
+
+
+/**
+ * @brief Handle to pfi header. Used in most of the functions associated
+ * with pfi file handling.
+ */
+typedef struct pfi_header *pfi_header;
+
+
+/**
+ * @brief Initialize a pfi header object.
+ *
+ * @param head  Pointer to handle. This function allocates memory
+ *              for this data structure.
+ * @return      0 on success, otherwise:
+ *              PFI_ENOMEM : no memory available for the handle.
+ */
+int pfi_header_init (pfi_header *head);
+
+
+/**
+ * @brief Destroy a pfi header object.
+ *
+ * @param head  handle. head is invalid after calling this function.
+ * @return      0 always.
+ */
+int pfi_header_destroy (pfi_header *head);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object.
+ *
+ * @param head  handle.
+ * @param key   pointer to key string. Must be 0 terminated.
+ * @param value         pointer to value string. Must be 0 terminated.
+ * @return      0 on success, otherwise:
+ *              PFI_EUNDEF   : key was not found.
+ *              PFI_ENOMEM   : no memory available for the handle.
+ *              PFI_EBADTYPE : value is not an hex string. This happens
+ *                              when the key stores an integer and the
+ *                              new value is not convertable e.g. not in
+ *                              0xXXXXXXXX format.
+ */
+int pfi_header_setvalue (pfi_header head,
+                         const char *key, const char *value);
+
+
+/**
+ * @brief Add a key/value pair to a pfi header object. Provide the
+ * value as a number.
+ *
+ * @param head  handle.
+ * @param key   pointer to key string. Must be 0 terminated.
+ * @param value         value to set.
+ * @return      0 on success, otherwise:
+ *              PFI_EUNDEF   : key was not found.
+ *              PFI_EBADTYPE : value is not a string. This happens
+ *                              when the key stores a string.
+ */
+int pfi_header_setnumber (pfi_header head,
+                          const char *key, uint32_t value);
+
+
+/**
+ * @brief For a given key, return the numerical value stored in a
+ * pfi header object.
+ *
+ * @param head  handle.
+ * @param key   pointer to key string. Must be 0 terminated.
+ * @param value         pointer to value.
+ * @return      0 on success, otherwise:
+ *              PFI_EUNDEF   : key was not found.
+ *              PFI_EBADTYPE : stored value is not an integer but a string.
+ */
+int pfi_header_getnumber (pfi_header head,
+                          const char *key, uint32_t *value);
+
+
+static inline uint32_t
+pfi_getnumber(pfi_header head, const char *key)
+{
+       uint32_t value;
+       pfi_header_getnumber(head, key, &value);
+       return value;
+}
+
+/**
+ * @brief For a given key, return the string value stored in a pfi
+ * header object.
+ *
+ * @param head  handle.
+ * @param key   pointer to key string. Must be 0 terminated.
+ * @param value         pointer to value string. Memory must be allocated by the user.
+ * @return      0 on success, otherwise:
+ *              PFI_EUNDEF   : key was not found.
+ *              PFI_EBADTYPE : stored value is not a string but an integer.
+ */
+int pfi_header_getstring (pfi_header head,
+                          const char *key, char *value, size_t size);
+
+
+/**
+ * @brief Write a pfi header object into a given file.
+ *
+ * @param out   output stream.
+ * @param head  handle.
+ * @return      0 on success, error values otherwise:
+ *              PFI_EINSUFF   : not all mandatory fields are filled.
+ *              PFI_ENOHEADER : wrong header version or magic number.
+ *              -E*            : see <asm/errno.h>.
+ */
+int pfi_header_write (FILE *out, pfi_header head);
+
+
+/**
+ * @brief Read a pfi header object from a given file.
+ *
+ * @param in    input stream.
+ * @param head  handle.
+ * @return      0 on success, error values otherwise:
+ *              PFI_ENOVERSION: unknown header version.
+ *              PFI_EFILE     : cannot read enough data.
+ *              PFI_ENOHEADER : wrong header version or magic number.
+ *              -E*            : see <asm/errno.h>.
+ *
+ * If the header verification returned success the user can assume that
+ * all mandatory fields for a particular version are accessible. Checking
+ * the return code when calling the get-function for those keys is not
+ * required in those cases. For optional fields the checking must still be
+ * done.
+ */
+int pfi_header_read (FILE *in, pfi_header head);
+
+
+/**
+ * @brief Display a pfi header in human-readable form.
+ *
+ * @param out   output stream.
+ * @param head  handle.
+ * @return      always 0.
+ *
+ * @note Prints out that it is not implemented and whom you should
+ * contact if you need it urgently!.
+ */
+int pfi_header_dump (FILE *out, pfi_header head);
+
+
+/*
+ * @brief       Iterates over a stream of pfi files. The iterator function
+ *              must advance the file pointer in FILE *in to the next pfi
+ *              header. Function exists on feof(in).
+ *
+ * @param in    input file descriptor, must be open and valid.
+ * @param func  iterator function called when pfi header could be
+ *              read and was validated. The function must return 0 on
+ *              success.
+ * @return      See pfi_header_init and pfi_header_read.
+ *              PFI_EINVAL       : func is not valid
+ *              0 ok.
+ */
+typedef int (* pfi_read_func)(FILE *in, pfi_header hdr, void *priv_data);
+
+int pfi_read (FILE *in, pfi_read_func func, void *priv_data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __pfi_h */
diff --git a/ubi-utils/old-tools/src/pfi2bin.c b/ubi-utils/old-tools/src/pfi2bin.c
new file mode 100644 (file)
index 0000000..7f31938
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * Convert a PFI file (partial flash image) into a plain binary file.
+ * This tool can be used to prepare the data to be burned into flash
+ * chips in a manufacturing step where the flashes are written before
+ * being soldered onto the hardware. For NAND images another step is
+ * required to add the right OOB data to the binary image.
+ *
+ * 1.3 Removed argp because we want to use uClibc.
+ * 1.4 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <ubigen.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "list.h"
+#include "error.h"
+#include "reader.h"
+#include "peb.h"
+#include "crc32.h"
+
+#define PROGRAM_VERSION "1.4"
+
+#define MAX_FNAME 255
+#define DEFAULT_ERASE_COUNT  0 /* Hmmm.... Perhaps */
+#define ERR_BUF_SIZE 1024
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+static uint32_t crc32_table[256];
+static char err_buf[ERR_BUF_SIZE];
+
+/*
+ * Data used to buffer raw blocks which have to be
+ * located at a specific point inside the generated RAW file
+ */
+
+typedef enum action_t {
+       ACT_NOTHING   = 0x00000000,
+       ACT_RAW    = 0x00000001,
+} action_t;
+
+static const char copyright [] __attribute__((unused)) =
+       "(c) Copyright IBM Corp 2006\n";
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "pfi2bin - a tool to convert PFI files into binary images.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+"  -c, --copyright\n"
+"  -v, --verbose              Print more information.\n"
+"\n"
+" Input:\n"
+"  -j, --platform=pdd-file    PDD information which contains the card settings.\n"
+"\n"
+" Output:\n"
+"  -o, --output=filename      Outputfile, default: stdout.\n"
+"\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"  -V, --version              Print program version\n";
+
+static const char *usage =
+"Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n"
+"            [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n"
+"            [--usage] [--version] pfifile\n";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+       { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' },
+       { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+typedef struct io {
+       FILE* fp_pdd;   /* a FilePointer to the PDD data */
+       FILE* fp_pfi;   /* a FilePointer to the PFI input stream */
+       FILE* fp_out;   /* a FilePointer to the output stream */
+} *io_t;
+
+typedef struct myargs {
+       /* common settings */
+       action_t action;
+       int verbose;
+       const char *f_in_pfi;
+       const char *f_in_pdd;
+       const char *f_out;
+
+       /* special stuff needed to get additional arguments */
+       char *arg1;
+       char **options;                 /* [STRING...] */
+} myargs;
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       /* common settings */
+                       case 'v': /* --verbose=<level> */
+                               args->verbose = 1;
+                               break;
+
+                       case 'c': /* --copyright */
+                               fprintf(stderr, "%s\n", copyright);
+                               exit(0);
+                               break;
+
+                       case 'j': /* --platform */
+                               args->f_in_pdd = optarg;
+                               break;
+
+                       case 'o': /* --output */
+                               args->f_out = optarg;
+                               break;
+
+                       case '?': /* help */
+                               printf("pfi2bin [OPTION...] pfifile\n");
+                               printf("%s", doc);
+                               printf("%s", optionsstr);
+                               printf("\nReport bugs to %s\n",
+                                      PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+
+                       case 'V':
+                               printf("%s\n", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+
+                       default:
+                               printf("%s", usage);
+                               exit(-1);
+               }
+       }
+
+       if (optind < argc)
+               args->f_in_pfi = argv[optind++];
+
+       return 0;
+}
+
+
+static size_t
+byte_to_blk(size_t byte, size_t blk_size)
+{
+       return  (byte % blk_size) == 0
+               ? byte / blk_size
+               : byte / blk_size + 1;
+}
+
+
+
+
+/**
+ * @precondition  IO: File stream points to first byte of RAW data.
+ * @postcondition IO: File stream points to first byte of next
+ *                   or EOF.
+ */
+static int
+memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs,
+               io_t io)
+{
+       int rc = 0;
+       uint32_t i;
+
+       size_t read, to_read, eb_num;
+       size_t bytes_left;
+       list_t pebs = *raw_pebs;
+       peb_t   peb  = NULL;
+
+       long old_file_pos = ftell(io->fp_pfi);
+       for (i = 0; i < pfi_raw->starts_size; i++) {
+               bytes_left = pfi_raw->data_size;
+               rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+               if (rc != 0)
+                       goto err;
+
+               eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size);
+               while (bytes_left) {
+                       to_read = MIN(bytes_left, pdd->eb_size);
+                       rc = peb_new(eb_num++, pdd->eb_size, &peb);
+                       if (rc != 0)
+                               goto err;
+                       read = fread(peb->data, 1, to_read, io->fp_pfi);
+                       if (read != to_read) {
+                               rc = -EIO;
+                               goto err;
+                       }
+                       pebs = append_elem(peb, pebs);
+                       bytes_left -= read;
+               }
+
+       }
+       *raw_pebs = pebs;
+       return 0;
+err:
+       pebs = remove_all((free_func_t)&peb_free, pebs);
+       return rc;
+}
+
+static int
+convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs,
+               struct ubi_vtbl_record *vol_tab,
+               size_t *ebs_written, io_t io)
+{
+       int rc = 0;
+       uint32_t i, j;
+       peb_t raw_peb;
+       peb_t cmp_peb;
+       ubi_info_t u;
+       size_t leb_total = 0;
+       uint8_t vol_type;
+
+       switch (ubi->type) {
+       case pfi_ubi_static:
+               vol_type = UBI_VID_STATIC; break;
+       case pfi_ubi_dynamic:
+               vol_type = UBI_VID_DYNAMIC; break;
+       default:
+               vol_type = UBI_VID_DYNAMIC;
+       }
+
+       rc = peb_new(0, 0, &cmp_peb);
+       if (rc != 0)
+               goto err;
+
+       long old_file_pos = ftell(io->fp_pfi);
+       for (i = 0; i < ubi->ids_size; i++) {
+               rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET);
+               if (rc != 0)
+                       goto err;
+               rc = ubigen_create(&u, ubi->ids[i], vol_type,
+                                  pdd->eb_size, DEFAULT_ERASE_COUNT,
+                                  ubi->alignment, UBI_VERSION,
+                                  pdd->vid_hdr_offset, 0, ubi->data_size,
+                                  io->fp_pfi, io->fp_out);
+               if (rc != 0)
+                       goto err;
+
+               rc = ubigen_get_leb_total(u, &leb_total);
+               if (rc != 0)
+                       goto err;
+
+               j = 0;
+               while(j < leb_total) {
+                       cmp_peb->num = *ebs_written;
+                       raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+                                       raw_pebs);
+                       if (raw_peb) {
+                               rc = peb_write(io->fp_out, raw_peb);
+                       }
+                       else {
+                               rc = ubigen_write_leb(u, NO_ERROR);
+                               j++;
+                       }
+                       if (rc != 0)
+                               goto err;
+                       (*ebs_written)++;
+               }
+               /* memorize volume table entry */
+               rc = ubigen_set_lvol_rec(u, ubi->size,
+                               ubi->names[i],
+                               (void*) &vol_tab[ubi->ids[i]]);
+               if (rc != 0)
+                       goto err;
+               ubigen_destroy(&u);
+       }
+
+       peb_free(&cmp_peb);
+       return 0;
+
+err:
+       peb_free(&cmp_peb);
+       ubigen_destroy(&u);
+       return rc;
+}
+
+
+static FILE*
+my_fmemopen (void *buf, size_t size, const char *opentype)
+{
+    FILE* f;
+    size_t ret;
+
+    assert(strcmp(opentype, "r") == 0);
+
+    f = tmpfile();
+    ret = fwrite(buf, 1, size, f);
+    rewind(f);
+
+    return f;
+}
+
+/**
+ * @brief              Builds a UBI volume table from a volume entry list.
+ * @return 0           On success.
+ *        else         Error.
+ */
+static int
+write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs,
+               struct ubi_vtbl_record *vol_tab, size_t vol_tab_size,
+               size_t *ebs_written, io_t io)
+{
+       int rc = 0;
+       ubi_info_t u;
+       peb_t raw_peb;
+       peb_t cmp_peb;
+       size_t leb_size, leb_total, j = 0;
+       uint8_t *ptr = NULL;
+       FILE* fp_leb = NULL;
+       int vt_slots;
+       size_t vol_tab_size_limit;
+
+       rc = peb_new(0, 0, &cmp_peb);
+       if (rc != 0)
+               goto err;
+
+       /* @FIXME: Artem creates one volume with 2 LEBs.
+        * IMO 2 volumes would be more convenient. In order
+        * to get 2 reserved LEBs from ubigen, I have to
+        * introduce this stupid mechanism. Until no final
+        * decision of the VTAB structure is made... Good enough.
+        */
+       rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC,
+                          pdd->eb_size, DEFAULT_ERASE_COUNT,
+                          1, UBI_VERSION,
+                          pdd->vid_hdr_offset, UBI_COMPAT_REJECT,
+                          vol_tab_size, stdin, io->fp_out);
+                          /* @FIXME stdin for fp_in is a hack */
+       if (rc != 0)
+               goto err;
+       rc = ubigen_get_leb_size(u, &leb_size);
+       if (rc != 0)
+               goto err;
+       ubigen_destroy(&u);
+
+       /*
+        * The number of supported volumes is restricted by the eraseblock size
+        * and by the UBI_MAX_VOLUMES constant.
+        */
+       vt_slots = leb_size / UBI_VTBL_RECORD_SIZE;
+       if (vt_slots > UBI_MAX_VOLUMES)
+               vt_slots = UBI_MAX_VOLUMES;
+       vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE;
+
+       ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t));
+       if (ptr == NULL)
+               goto err;
+
+       memset(ptr, 0xff, leb_size);
+       memcpy(ptr, vol_tab, vol_tab_size_limit);
+       fp_leb = my_fmemopen(ptr, leb_size, "r");
+
+       rc = ubigen_create(&u, UBI_LAYOUT_VOL_ID, UBI_VID_DYNAMIC,
+                          pdd->eb_size, DEFAULT_ERASE_COUNT,
+                          1, UBI_VERSION, pdd->vid_hdr_offset,
+                          UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS,
+                          fp_leb, io->fp_out);
+       if (rc != 0)
+               goto err;
+       rc = ubigen_get_leb_total(u, &leb_total);
+       if (rc != 0)
+               goto err;
+
+       long old_file_pos = ftell(fp_leb);
+       while(j < leb_total) {
+               rc = fseek(fp_leb, old_file_pos, SEEK_SET);
+               if (rc != 0)
+                       goto err;
+
+               cmp_peb->num = *ebs_written;
+               raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb,
+                               raw_pebs);
+               if (raw_peb) {
+                       rc = peb_write(io->fp_out, raw_peb);
+               }
+               else {
+                       rc = ubigen_write_leb(u, NO_ERROR);
+                       j++;
+               }
+
+               if (rc != 0)
+                       goto err;
+               (*ebs_written)++;
+       }
+
+err:
+       free(ptr);
+       peb_free(&cmp_peb);
+       ubigen_destroy(&u);
+       fclose(fp_leb);
+       return rc;
+}
+
+static int
+write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written,
+                       FILE* fp_out)
+{
+       int rc = 0;
+       uint32_t j, delta;
+       list_t ptr;
+       peb_t empty_eb, peb;
+
+       /* create an empty 0xff EB (for padding) */
+       rc = peb_new(0, pdd->eb_size, &empty_eb);
+
+       foreach(peb, ptr, raw_blocks) {
+               if (peb->num < *ebs_written) {
+                       continue; /* omit blocks which
+                                    are already passed */
+               }
+
+               if (peb->num < *ebs_written) {
+                       err_msg("eb_num: %d\n", peb->num);
+                       err_msg("Bug: This should never happen. %d %s",
+                               __LINE__, __FILE__);
+                       goto err;
+               }
+
+               delta = peb->num - *ebs_written;
+               if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) {
+                       err_msg("RAW block outside of flash_size.");
+                       goto err;
+               }
+               for (j = 0; j < delta; j++) {
+                       rc = peb_write(fp_out, empty_eb);
+                       if (rc != 0)
+                               goto err;
+                       (*ebs_written)++;
+               }
+               rc = peb_write(fp_out, peb);
+               if (rc != 0)
+                       goto err;
+               (*ebs_written)++;
+       }
+
+err:
+       peb_free(&empty_eb);
+       return rc;
+}
+
+static int
+init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size)
+{
+       uint32_t crc;
+       size_t i;
+       struct ubi_vtbl_record* res = NULL;
+
+       *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE;
+
+       res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size);
+       if (vol_tab == NULL) {
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < UBI_MAX_VOLUMES; i++) {
+               crc = clc_crc32(crc32_table, UBI_CRC32_INIT,
+                       &(res[i]), UBI_VTBL_RECORD_SIZE_CRC);
+               res[i].crc = __cpu_to_be32(crc);
+       }
+
+       *vol_tab = res;
+       return 0;
+}
+
+static int
+create_raw(io_t io)
+{
+       int rc = 0;
+       size_t ebs_written = 0; /* eraseblocks written already... */
+       size_t vol_tab_size;
+       list_t ptr;
+
+       list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */
+       list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */
+       list_t raw_pebs  = mk_empty(); /* list of raw eraseblocks */
+
+       struct ubi_vtbl_record *vol_tab = NULL;
+       pdd_data_t pdd = NULL;
+
+       rc = init_vol_tab (&vol_tab, &vol_tab_size);
+       if (rc != 0) {
+               err_msg("Cannot initialize volume table.");
+               goto err;
+       }
+
+       rc = read_pdd_data(io->fp_pdd, &pdd,
+                       err_buf, ERR_BUF_SIZE);
+       if (rc != 0) {
+               err_msg("Cannot read necessary pdd_data: %s rc: %d",
+                               err_buf, rc);
+               goto err;
+       }
+
+       rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi,
+                       err_buf, ERR_BUF_SIZE);
+       if (rc != 0) {
+               err_msg("Cannot read pfi header: %s rc: %d",
+                               err_buf, rc);
+               goto err;
+       }
+
+       pfi_raw_t pfi_raw;
+       foreach(pfi_raw, ptr, pfi_raws) {
+               rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs,
+                       io);
+               if (rc != 0) {
+                       err_msg("Cannot create raw_block in mem. rc: %d\n",
+                               rc);
+                       goto err;
+               }
+       }
+
+       pfi_ubi_t pfi_ubi;
+       foreach(pfi_ubi, ptr, pfi_ubis) {
+               rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs,
+                                       vol_tab, &ebs_written, io);
+               if (rc != 0) {
+                       err_msg("Cannot convert UBI volume. rc: %d\n", rc);
+                       goto err;
+               }
+       }
+
+       rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size,
+                       &ebs_written, io);
+       if (rc != 0) {
+               err_msg("Cannot write UBI volume table. rc: %d\n", rc);
+               goto err;
+       }
+
+       rc  = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out);
+       if (rc != 0)
+               goto err;
+
+       if (io->fp_out != stdout)
+               info_msg("Physical eraseblocks written: %8d\n", ebs_written);
+err:
+       free(vol_tab);
+       pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws);
+       pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis);
+       raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs);
+       free_pdd_data(&pdd);
+       return rc;
+}
+
+
+/* ------------------------------------------------------------------------- */
+static void
+open_io_handle(myargs *args, io_t io)
+{
+       /* set PDD input */
+       io->fp_pdd = fopen(args->f_in_pdd, "r");
+       if (io->fp_pdd == NULL) {
+               err_sys("Cannot open: %s", args->f_in_pdd);
+       }
+
+       /* set PFI input */
+       io->fp_pfi = fopen(args->f_in_pfi, "r");
+       if (io->fp_pfi == NULL) {
+               err_sys("Cannot open PFI input file: %s", args->f_in_pfi);
+       }
+
+       /* set output prefix */
+       if (strcmp(args->f_out,"") == 0)
+               io->fp_out = stdout;
+       else {
+               io->fp_out = fopen(args->f_out, "wb");
+               if (io->fp_out == NULL) {
+                       err_sys("Cannot open output file: %s", args->f_out);
+               }
+       }
+}
+
+static void
+close_io_handle(io_t io)
+{
+       if (fclose(io->fp_pdd) != 0) {
+               err_sys("Cannot close PDD file.");
+       }
+       if (fclose(io->fp_pfi) != 0) {
+               err_sys("Cannot close PFI file.");
+       }
+       if (io->fp_out != stdout) {
+               if (fclose(io->fp_out) != 0) {
+                       err_sys("Cannot close output file.");
+               }
+       }
+
+       io->fp_pdd = NULL;
+       io->fp_pfi = NULL;
+       io->fp_out = NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+       int rc = 0;
+
+       ubigen_init();
+       init_crc32_table(crc32_table);
+
+       struct io io = {NULL, NULL, NULL};
+       myargs args = {
+               .action = ACT_RAW,
+               .verbose = 0,
+
+               .f_in_pfi = "",
+               .f_in_pdd = "",
+               .f_out = "",
+
+               /* arguments */
+               .arg1 = NULL,
+               .options = NULL,
+       };
+
+       /* parse arguments */
+       parse_opt(argc, argv, &args);
+
+       if (strcmp(args.f_in_pfi, "") == 0) {
+               err_quit("No PFI input file specified!");
+       }
+
+       if (strcmp(args.f_in_pdd, "") == 0) {
+               err_quit("No PDD input file specified!");
+       }
+
+       open_io_handle(&args, &io);
+
+       info_msg("[ Creating RAW...");
+       rc = create_raw(&io);
+       if (rc != 0) {
+               err_msg("Creating RAW failed.");
+               goto err;
+       }
+
+err:
+       close_io_handle(&io);
+       if (rc != 0) {
+               remove(args.f_out);
+       }
+
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/pfiflash.c b/ubi-utils/old-tools/src/pfiflash.c
new file mode 100644 (file)
index 0000000..754fe33
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *         Frank Haverkamp
+ *
+ * Process a PFI (partial flash image) and write the data to the
+ * specified UBI volumes. This tool is intended to be used for system
+ * update using PFI files.
+ *
+ * 1.1 fixed output to stderr and stdout in logfile mode.
+ * 1.2 updated.
+ * 1.3 removed argp parsing to be able to use uClib.
+ * 1.4 Minor cleanups.
+ * 1.5 Forgot to delete raw block before updating it.
+ * 1.6 Migrated to new libubi.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pfiflash.h>
+#undef DEBUG
+#include "error.h"
+#include "config.h"
+
+#define PROGRAM_VERSION  "1.6"
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "pfiflash - a tool for updating a controller with PFI files.\n";
+
+static const char *optionsstr =
+" Standard options:\n"
+"  -c, --copyright            Print copyright information.\n"
+"  -l, --logfile=<file>       Write a logfile to <file>.\n"
+"  -v, --verbose              Be verbose during program execution.\n"
+"\n"
+" Process options:\n"
+"  -C, --complete             Execute a complete system update. Updates both\n"
+"                             sides.\n"
+"  -p, --pdd-update=<type>    Specify the pdd-update algorithm. <type> is either\n"
+"                             'keep', 'merge' or 'overwrite'.\n"
+"  -r, --raw-flash=<dev>      Flash the raw data. Use the specified mtd device.\n"
+"  -s, --side=<seqnum>        Select the side which shall be updated.\n"
+"  -x, --compare              Only compare on-flash and pfi data, print info if\n"
+"                             an update is neccessary and return appropriate\n"
+"                             error code.\n"
+"\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"  -V, --version              Print program version\n";
+
+static const char *usage =
+"Usage: pfiflash [-cvC?V] [-l <file>] [-p <type>] [-r <dev>] [-s <seqnum>]\n"
+"            [--copyright] [--logfile=<file>] [--verbose] [--complete]\n"
+"            [--pdd-update=<type>] [--raw-flash=<dev>] [--side=<seqnum>]\n"
+"            [--compare] [--help] [--usage] [--version] [pfifile]\n";
+
+static const char copyright [] __attribute__((unused)) =
+       "Copyright IBM Corp 2006";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "logfile", .has_arg = 1, .flag = NULL, .val = 'l' },
+       { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+       { .name = "complete", .has_arg = 0, .flag = NULL, .val = 'C' },
+       { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' },
+       { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' },
+       { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+       { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+       int verbose;
+       const char *logfile;
+       const char *raw_dev;
+
+       pdd_handling_t pdd_handling;
+       int seqnum;
+       int compare;
+       int complete;
+
+       FILE* fp_in;
+
+       /* special stuff needed to get additional arguments */
+       char *arg1;
+       char **options;         /* [STRING...] */
+} myargs;
+
+static pdd_handling_t
+get_pdd_handling(const char* str)
+{
+       if (strcmp(str, "keep") == 0) {
+               return PDD_KEEP;
+       }
+       if (strcmp(str, "merge") == 0) {
+               return PDD_MERGE;
+       }
+       if (strcmp(str, "overwrite") == 0) {
+               return PDD_OVERWRITE;
+       }
+
+       return -1;
+}
+
+static int
+get_update_seqnum(const char* str)
+{
+       uint32_t i = strtoul(str, NULL, 0);
+
+       if ((i != 0) && (i != 1)) {
+               return -1;
+       }
+
+       return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "cl:vCp:r:s:x?V",
+                                 long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       /* standard options */
+                       case 'c':
+                               err_msg("%s\n", copyright);
+                               exit(0);
+                               break;
+                       case 'v':
+                               args->verbose = 1;
+                               break;
+                       case 'l':
+                               args->logfile = optarg;
+                               break;
+                               /* process options */
+                       case 'C':
+                               args->complete = 1;
+                               break;
+                       case 'p':
+                               args->pdd_handling = get_pdd_handling(optarg);
+                               if ((int)args->pdd_handling < 0) {
+                                       err_quit("Unknown PDD handling: %s.\n"
+                                                "Please use either "
+                                                "'keep', 'merge' or"
+                                                "'overwrite'.\n'");
+                               }
+                               break;
+                       case 's':
+                               args->seqnum = get_update_seqnum(optarg);
+                               if (args->seqnum < 0) {
+                                       err_quit("Unsupported side: %s.\n"
+                                                "Supported sides are '0' "
+                                                "and '1'\n", optarg);
+                               }
+                               break;
+                       case 'x':
+                               args->compare = 1;
+                               break;
+                       case 'r':
+                               args->raw_dev = optarg;
+                               break;
+                       case '?': /* help */
+                               err_msg("Usage: pfiflash [OPTION...] [pfifile]");
+                               err_msg("%s", doc);
+                               err_msg("%s", optionsstr);
+                               err_msg("\nReport bugs to %s\n",
+                                       PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+                       case 'V':
+                               err_msg("%s", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+                       default:
+                               err_msg("%s", usage);
+                               exit(-1);
+
+               }
+       }
+
+       if (optind < argc) {
+               args->fp_in = fopen(argv[optind++], "r");
+               if ((args->fp_in) == NULL) {
+                       err_sys("Cannot open PFI file %s for input",
+                               argv[optind]);
+               }
+       }
+
+       return 0;
+}
+
+int main (int argc, char** argv)
+{
+       int rc = 0;
+       char err_buf[PFIFLASH_MAX_ERR_BUF_SIZE];
+       memset(err_buf, '\0', PFIFLASH_MAX_ERR_BUF_SIZE);
+
+       myargs args = {
+               .verbose    = 0,
+               .seqnum     = -1,
+               .compare    = 0,
+               .complete   = 0,
+               .logfile    = NULL, /* "/tmp/pfiflash.log", */
+               .pdd_handling = PDD_KEEP,
+               .fp_in      = stdin,
+               .raw_dev    = NULL,
+       };
+
+       parse_opt(argc, argv, &args);
+       error_initlog(args.logfile);
+
+       if (!args.fp_in) {
+               rc = -1;
+               snprintf(err_buf, PFIFLASH_MAX_ERR_BUF_SIZE,
+                        "No PFI input file specified!\n");
+               goto err;
+       }
+
+       rc = pfiflash_with_options(args.fp_in, args.complete, args.seqnum,
+                       args.compare, args.pdd_handling, args.raw_dev, err_buf,
+                       PFIFLASH_MAX_ERR_BUF_SIZE);
+       if (rc < 0) {
+               goto err_fp;
+       }
+
+ err_fp:
+       if (args.fp_in != stdin)
+               fclose(args.fp_in);
+ err:
+       if (rc != 0)
+               err_msg("pfiflash: %s\nrc: %d\n", err_buf, rc);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/pfiflash.h b/ubi-utils/old-tools/src/pfiflash.h
new file mode 100644 (file)
index 0000000..039705d
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef __PFIFLASH_H__
+#define __PFIFLASH_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/**
+ *
+ * @file pfi.h
+ *
+ * @author Oliver Lohmann <oliloh@de.ibm.com>
+ *
+ * @brief The pfiflash library offers an interface for using the
+ * pfiflash * utility.
+ */
+
+#include <stdio.h>             /* FILE */
+
+#define PFIFLASH_MAX_ERR_BUF_SIZE 1024
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum pdd_handling_t
+{
+       PDD_KEEP = 0,
+       PDD_MERGE,
+       PDD_OVERWRITE,
+       PDD_HANDLING_NUM, /* always the last item */
+} pdd_handling_t; /**< Possible PDD handle algorithms. */
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete     [0|1] Do a complete system update.
+ * @param seqnum       Index in a redundant group.
+ * @param compare      [0|1] Compare contents.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param rawdev       Device to use for raw flashing
+ * @param err_buf      An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
+               pdd_handling_t pdd_handling, const char* rawdev,
+               char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Flashes a PFI file to UBI Device 0.
+ * @param complete     [0|1] Do a complete system update.
+ * @param seqnum       Index in a redundant group.
+ * @param pdd_handling The PDD handling algorithm.
+ * @param err_buf      An error buffer.
+ * @param err_buf_size Size of the error buffer.
+ */
+int pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
+               char *err_buf, size_t err_buf_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PFIFLASH_H__ */
diff --git a/ubi-utils/old-tools/src/pfiflash_error.h b/ubi-utils/old-tools/src/pfiflash_error.h
new file mode 100644 (file)
index 0000000..0f27f4a
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef __PFIFLASH_ERROR_H__
+#define __PFIFLASH_ERROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Drake Dowsett <dowsett@de.ibm.com>
+ * Contact: Andreas Arnez <arnez@de.ibm.com>
+ */
+
+enum pfiflash_err {
+       PFIFLASH_ERR_EOF = 1,
+       PFIFLASH_ERR_FIO,
+       PFIFLASH_ERR_UBI_OPEN,
+       PFIFLASH_ERR_UBI_CLOSE,
+       PFIFLASH_ERR_UBI_MKVOL,
+       PFIFLASH_ERR_UBI_RMVOL,
+       PFIFLASH_ERR_UBI_VOL_UPDATE,
+       PFIFLASH_ERR_UBI_VOL_FOPEN,
+       PFIFLASH_ERR_UBI_UNKNOWN,
+       PFIFLASH_ERR_UBI_VID_OOB,
+       PFIFLASH_ERR_BOOTENV_CREATE,
+       PFIFLASH_ERR_BOOTENV_READ,
+       PFIFLASH_ERR_BOOTENV_SIZE,
+       PFIFLASH_ERR_BOOTENV_WRITE,
+       PFIFLASH_ERR_PDD_UNKNOWN,
+       PFIFLASH_ERR_MTD_OPEN,
+       PFIFLASH_ERR_MTD_CLOSE,
+       PFIFLASH_ERR_CRC_CHECK,
+       PFIFLASH_ERR_MTD_ERASE,
+       PFIFLASH_ERR_COMPARE,
+       PFIFLASH_CMP_DIFF
+};
+
+const char *const PFIFLASH_ERRSTR[] = {
+       "",
+       "unexpected EOF",
+       "file I/O error",
+       "couldn't open UBI",
+       "couldn't close UBI",
+       "couldn't make UBI volume %d",
+       "couldn't remove UBI volume %d",
+       "couldn't update UBI volume %d",
+       "couldn't open UBI volume %d",
+       "unknown UBI operation",
+       "PFI data contains out of bounds UBI id %d",
+       "couldn't create bootenv%s",
+       "couldn't read bootenv",
+       "couldn't resize bootenv",
+       "couldn't write bootenv on ubi%d_%d",
+       "unknown PDD handling algorithm",
+       "couldn't open MTD device %s",
+       "couldn't close MTD device %s",
+       "CRC check failed: given=0x%08x, calculated=0x%08x",
+       "couldn't erase raw mtd region",
+       "couldn't compare volumes",
+       "on-flash data differ from pfi data, update is neccessary"
+};
+
+#endif /* __PFIFLASH_ERROR_H__ */
diff --git a/ubi-utils/old-tools/src/reader.c b/ubi-utils/old-tools/src/reader.c
new file mode 100644 (file)
index 0000000..0ea8c6d
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * Read in PFI (partial flash image) data and store it into internal
+ * data structures for further processing. Take also care about
+ * special handling if the data contains PDD (platform description
+ * data/boot-parameters).
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "bootenv.h"
+#include "reader.h"
+
+#define __unused __attribute__((unused))
+
+/* @FIXME hard coded offsets right now - get them from Artem? */
+#define NAND2048_DEFAULT_VID_HDR_OFF 1984
+#define NAND512_DEFAULT_VID_HDR_OFF  448
+#define NOR_DEFAULT_VID_HDR_OFF      64
+
+#define EBUF_PFI(fmt...)                                               \
+       do { int i = snprintf(err_buf, err_buf_size, "%s\n", label);    \
+            snprintf(err_buf + i, err_buf_size - i, fmt);              \
+       } while (0)
+
+#define EBUF(fmt...) \
+       do { snprintf(err_buf, err_buf_size, fmt); } while (0)
+
+
+int
+read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data,
+             char* err_buf, size_t err_buf_size)
+{
+       int rc = 0;
+       bootenv_t pdd = NULL;
+       pdd_data_t res = NULL;
+       const char* value;
+
+       res = (pdd_data_t) malloc(sizeof(struct pdd_data));
+       if (!res) {
+               rc = -ENOMEM;
+               goto err;
+       }
+       rc = bootenv_create(&pdd);
+       if (rc != 0) {
+               goto err;
+       }
+       rc = bootenv_read_txt(fp_pdd, pdd);
+       if (rc != 0) {
+               goto err;
+       }
+       rc = bootenv_get(pdd, "flash_type", &value);
+       if (rc != 0) {
+               goto err;
+       }
+
+       if (strcmp(value, "NAND") == 0) {
+
+               rc = bootenv_get_num(pdd, "flash_page_size",
+                            &(res->flash_page_size));
+               if (rc != 0) {
+                       EBUF("Cannot read 'flash_page_size' from pdd.");
+                       goto err;
+               }
+               res->flash_type = NAND_FLASH;
+
+               switch (res->flash_page_size) {
+               case 512:
+                       res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF;
+                       break;
+               case 2048:
+                       res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF;
+                       break;
+               default:
+                       EBUF("Unsupported  'flash_page_size' %d.",
+                            res->flash_page_size);
+                       goto err;
+               }
+       }
+       else if (strcmp(value, "NOR") == 0){
+               res->flash_type = NOR_FLASH;
+               res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF;
+       }
+       else {
+               snprintf(err_buf, err_buf_size,
+                        "Unkown flash type: %s", value);
+               goto err;
+       }
+
+       rc = bootenv_get_num(pdd, "flash_eraseblock_size",
+                            &(res->eb_size));
+       if (rc != 0) {
+               EBUF("Cannot read 'flash_eraseblock_size' from pdd.");
+               goto err;
+       }
+
+       rc = bootenv_get_num(pdd, "flash_size",
+                            &(res->flash_size));
+       if (rc != 0) {
+               EBUF("Cannot read 'flash_size' from pdd.");
+               goto err;
+       }
+
+       goto out;
+ err:
+       if (res) {
+               free(res);
+               res = NULL;
+       }
+ out:
+       bootenv_destroy(&pdd);
+       *pdd_data = res;
+       return rc;
+}
+
+int
+read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw,
+            const char* label, char* err_buf, size_t err_buf_size)
+{
+       int rc = 0;
+       char tmp_str[PFI_KEYWORD_LEN];
+       bootenv_list_t raw_start_list = NULL;
+       pfi_raw_t res;
+       size_t size;
+
+       res = (pfi_raw_t) malloc(sizeof(struct pfi_raw));
+       if (!res)
+               return -ENOMEM;
+
+       rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'size' from PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'crc' from PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getstring(pfi_hd, "raw_starts",
+                                 tmp_str, PFI_KEYWORD_LEN);
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'raw_starts' from PFI.");
+               goto err;
+       }
+
+       rc = bootenv_list_create(&raw_start_list);
+       if (rc != 0) {
+               goto err;
+       }
+
+       rc = bootenv_list_import(raw_start_list, tmp_str);
+       if (rc != 0) {
+               EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+               goto err;
+       }
+
+       rc = bootenv_list_to_num_vector(raw_start_list,
+                                       &size, &(res->starts));
+       res->starts_size = size;
+
+       if (rc != 0) {
+               EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+               goto err;
+       }
+
+       goto out;
+
+ err:
+       if (res) {
+               free(res);
+               res = NULL;
+       }
+ out:
+       bootenv_list_destroy(&raw_start_list);
+       *pfi_raw = res;
+       return rc;
+}
+
+int
+read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi,
+            const char *label, char* err_buf, size_t err_buf_size)
+{
+       int rc = 0;
+       const char** tmp_names = NULL;
+       char tmp_str[PFI_KEYWORD_LEN];
+       bootenv_list_t ubi_id_list = NULL;
+       bootenv_list_t ubi_name_list = NULL;
+       pfi_ubi_t res;
+       uint32_t i;
+       size_t size;
+
+       res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi));
+       if (!res)
+               return -ENOMEM;
+
+       rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size));
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'size' from PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc));
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'crc' from PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN);
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'ubi_ids' from PFI.");
+               goto err;
+       }
+
+       rc = bootenv_list_create(&ubi_id_list);
+       if (rc != 0) {
+               goto err;
+       }
+       rc = bootenv_list_create(&ubi_name_list);
+       if (rc != 0) {
+               goto err;
+       }
+
+       rc = bootenv_list_import(ubi_id_list, tmp_str);
+       if (rc != 0) {
+               EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+               goto err;
+       }
+
+       rc = bootenv_list_to_num_vector(ubi_id_list, &size,
+                                       &(res->ids));
+       res->ids_size = size;
+       if (rc != 0) {
+               EBUF_PFI("Cannot create numeric value array: %s", tmp_str);
+               goto err;
+       }
+
+       if (res->ids_size == 0) {
+               rc = -1;
+               EBUF_PFI("Sanity check failed: No ubi_ids specified.");
+               goto err;
+       }
+
+       rc = pfi_header_getstring(pfi_hd, "ubi_type",
+                                 tmp_str, PFI_KEYWORD_LEN);
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'ubi_type' from PFI.");
+               goto err;
+       }
+       if (strcmp(tmp_str, "static") == 0)
+               res->type = pfi_ubi_static;
+       else if (strcmp(tmp_str, "dynamic") == 0)
+               res->type = pfi_ubi_dynamic;
+       else {
+               EBUF_PFI("Unknown ubi_type in PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment));
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'ubi_alignment' from PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size));
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'ubi_size' from PFI.");
+               goto err;
+       }
+
+       rc = pfi_header_getstring(pfi_hd, "ubi_names",
+                                 tmp_str, PFI_KEYWORD_LEN);
+       if (rc != 0) {
+               EBUF_PFI("Cannot read 'ubi_names' from PFI.");
+               goto err;
+       }
+
+       rc = bootenv_list_import(ubi_name_list, tmp_str);
+       if (rc != 0) {
+               EBUF_PFI("Cannot translate PFI value: %s", tmp_str);
+               goto err;
+       }
+       rc = bootenv_list_to_vector(ubi_name_list, &size,
+                                   &(tmp_names));
+       res->names_size = size;
+       if (rc != 0) {
+               EBUF_PFI("Cannot create string array: %s", tmp_str);
+               goto err;
+       }
+
+       if (res->names_size != res->ids_size) {
+               EBUF_PFI("Sanity check failed: ubi_ids list does not match "
+                        "sizeof ubi_names list.");
+               rc = -1;
+       }
+
+       /* copy tmp_names to own structure */
+       res->names = (char**) calloc(1, res->names_size * sizeof (char*));
+       if (res->names == NULL)
+               goto err;
+
+       for (i = 0; i < res->names_size; i++) {
+               res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char));
+               if (res->names[i] == NULL)
+                       goto err;
+               strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1);
+       }
+
+       goto out;
+
+ err:
+       if (res) {
+               if (res->names) {
+                       for (i = 0; i < res->names_size; i++) {
+                               if (res->names[i]) {
+                                       free(res->names[i]);
+                               }
+                       }
+                       free(res->names);
+               }
+               if (res->ids) {
+                       free(res->ids);
+               }
+               free(res);
+               res = NULL;
+       }
+
+ out:
+       bootenv_list_destroy(&ubi_id_list);
+       bootenv_list_destroy(&ubi_name_list);
+       if (tmp_names != NULL)
+               free(tmp_names);
+       *pfi_ubi = res;
+       return rc;
+}
+
+
+int
+free_pdd_data(pdd_data_t* pdd_data)
+{
+       if (*pdd_data) {
+               free(*pdd_data);
+       }
+       *pdd_data = NULL;
+
+       return 0;
+}
+
+int
+free_pfi_raw(pfi_raw_t* pfi_raw)
+{
+       pfi_raw_t tmp = *pfi_raw;
+       if (tmp) {
+               if (tmp->starts)
+                       free(tmp->starts);
+               free(tmp);
+       }
+       *pfi_raw = NULL;
+
+       return 0;
+}
+
+int
+free_pfi_ubi(pfi_ubi_t* pfi_ubi)
+{
+       size_t i;
+       pfi_ubi_t tmp = *pfi_ubi;
+       if (tmp) {
+               if (tmp->ids)
+                       free(tmp->ids);
+               if (tmp->names) {
+                       for (i = 0; i < tmp->names_size; i++) {
+                               if (tmp->names[i]) {
+                                       free(tmp->names[i]);
+                               }
+                       }
+                       free(tmp->names);
+               }
+               free(tmp);
+       }
+       *pfi_ubi = NULL;
+
+       return 0;
+}
+
+
+int
+read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+                char* err_buf, size_t err_buf_size)
+{
+       int rc = 0;
+       char mode[PFI_KEYWORD_LEN];
+       char label[PFI_LABEL_LEN];
+
+       *pfi_raws = mk_empty(); pfi_raw_t raw = NULL;
+       *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL;
+       pfi_header pfi_header = NULL;
+
+       /* read all headers from PFI and store them in lists */
+       rc = pfi_header_init(&pfi_header);
+       if (rc != 0) {
+               EBUF("Cannot initialize pfi header.");
+               goto err;
+       }
+       while ((rc == 0) && !feof(fp_pfi)) {
+               rc = pfi_header_read(fp_pfi, pfi_header);
+               if (rc != 0) {
+                       if (rc == PFI_DATA_START) {
+                               rc = 0;
+                               break; /* data section starts,
+                                         all headers read */
+                       }
+                       else {
+                               goto err;
+                       }
+               }
+               rc = pfi_header_getstring(pfi_header, "label", label,
+                                         PFI_LABEL_LEN);
+               if (rc != 0) {
+                       EBUF("Cannot read 'label' from PFI.");
+                       goto err;
+               }
+               rc = pfi_header_getstring(pfi_header, "mode", mode,
+                                         PFI_KEYWORD_LEN);
+               if (rc != 0) {
+                       EBUF("Cannot read 'mode' from PFI.");
+                       goto err;
+               }
+               if (strcmp(mode, "ubi") == 0) {
+                       rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label,
+                                         err_buf, err_buf_size);
+                       if (rc != 0) {
+                               goto err;
+                       }
+                       *pfi_ubis = append_elem(ubi, *pfi_ubis);
+               }
+               else if (strcmp(mode, "raw") == 0) {
+                       rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label,
+                                         err_buf, err_buf_size);
+                       if (rc != 0) {
+                               goto err;
+                       }
+                       *pfi_raws = append_elem(raw, *pfi_raws);
+               }
+               else {
+                       EBUF("Recvieved unknown mode from PFI: %s", mode);
+                       goto err;
+               }
+       }
+       goto out;
+
+ err:
+       *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws);
+       *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis);
+ out:
+       pfi_header_destroy(&pfi_header);
+       return rc;
+
+}
diff --git a/ubi-utils/old-tools/src/reader.h b/ubi-utils/old-tools/src/reader.h
new file mode 100644 (file)
index 0000000..715e464
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef __READER_H__
+#define __READER_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * Read Platform Description Data (PDD).
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "pfi.h"
+#include "bootenv.h"
+#include "list.h"
+
+typedef enum flash_type_t {
+       NAND_FLASH = 0,
+       NOR_FLASH,
+} flash_type_t;
+
+typedef struct pdd_data *pdd_data_t;
+typedef struct pfi_raw *pfi_raw_t;
+typedef struct pfi_ubi *pfi_ubi_t;
+
+struct pdd_data {
+       uint32_t flash_size;
+       uint32_t flash_page_size;
+       uint32_t eb_size;
+       uint32_t vid_hdr_offset;
+       flash_type_t flash_type;
+};
+
+struct pfi_raw {
+       uint32_t data_size;
+       uint32_t *starts;
+       uint32_t starts_size;
+       uint32_t crc;
+};
+
+struct pfi_ubi {
+       uint32_t data_size;
+       uint32_t alignment;
+       uint32_t *ids;
+       uint32_t ids_size;
+       char     **names;
+       uint32_t names_size;
+       uint32_t size;
+       enum { pfi_ubi_dynamic, pfi_ubi_static } type;
+       int curr_seqnum; /* specifies the seqnum taken in an update,
+                           default: 0 (used by pfiflash, ubimirror) */
+       uint32_t crc;
+};
+
+int read_pdd_data(FILE* fp_pdd, pdd_data_t *pdd_data,
+               char *err_buf, size_t err_buf_size);
+int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi, pfi_raw_t *pfi_raw,
+               const char *label, char *err_buf, size_t err_buf_size);
+int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi, pfi_ubi_t *pfi_ubi,
+               const char *label, char *err_buf, size_t err_buf_size);
+
+/**
+ * @brief Reads all pfi headers into list structures, separated by
+ *       RAW and UBI sections.
+ */
+int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi,
+               char* err_buf, size_t err_buf_size);
+int free_pdd_data(pdd_data_t *pdd_data);
+int free_pfi_raw(pfi_raw_t *raw_pfi);
+int free_pfi_ubi(pfi_ubi_t *pfi_ubi);
+
+#endif /* __READER_H__ */
diff --git a/ubi-utils/old-tools/src/ubigen.c b/ubi-utils/old-tools/src/ubigen.c
new file mode 100644 (file)
index 0000000..9fcafab
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * Tool to add UBI headers to binary images.
+ *
+ * 1.0 Initial version
+ * 1.1 Different CRC32 start value
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <mtd/ubi-header.h>
+
+#include "ubigen.h"
+#include "config.h"
+
+#define PROGRAM_VERSION "1.3"
+
+typedef enum action_t {
+       ACT_NORMAL           = 0x00000001,
+       ACT_BROKEN_UPDATE    = 0x00000002,
+} action_t;
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "ubigen - a tool for adding UBI information to a binary input file.\n";
+
+static const char *optionsstr =
+" Common settings:\n"
+"  -c, --copyright            Print copyright information.\n"
+"  -d, --debug\n"
+"  -v, --verbose              Print more progress information.\n"
+"\n"
+" UBI Settings:\n"
+"  -A, --alignment=<num>      Set the alignment size to <num> (default 1).\n"
+"                             Values can be specified as bytes, 'ki' or 'Mi'.\n"
+"  -B, --blocksize=<num>      Set the eraseblock size to <num> (default 128\n"
+"                             KiB).\n"
+"                             Values can be specified as bytes, 'ki' or 'Mi'.\n"
+"  -E, --erasecount=<num>     Set the erase count to <num> (default 0)\n"
+"  -I, --id=<num>             The UBI volume id.\n"
+"  -O, --offset=<num>         Offset from start of an erase block to the UBI\n"
+"                             volume header.\n"
+"  -T, --type=<num>           The UBI volume type:\n"
+"                             1 = dynamic, 2 = static\n"
+"  -X, --setver=<num>         Set UBI version number to <num> (default 1)\n"
+"\n"
+" Input/Output:\n"
+"  -i, --infile=<filename>    Read input from file.\n"
+"  -o, --outfile=<filename>   Write output to file (default is stdout).\n"
+"\n"
+" Special options:\n"
+"  -U, --broken-update=<leb>  Create an ubi image which simulates a broken\n"
+"                             update.\n"
+"                             <leb> specifies the logical eraseblock number to\n"
+"                             update.\n"
+"\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"  -V, --version              Print program version\n";
+
+static const char *usage =
+"Usage: ubigen [-cdv?V] [-A <num>] [-B <num>] [-E <num>] [-I <num>]\n"
+"          [-O <num>] [-T <num>] [-X <num>] [-i <filename>] [-o <filename>]\n"
+"          [-U <leb>] [--copyright] [--debug] [--verbose] [--alignment=<num>]\n"
+"          [--blocksize=<num>] [--erasecount=<num>] [--id=<num>]\n"
+"          [--offset=<num>] [--type=<num>] [--setver=<num>]\n"
+"          [--infile=<filename>] [--outfile=<filename>]\n"
+"          [--broken-update=<leb>] [--help] [--usage] [--version]\n";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "debug", .has_arg = 0, .flag = NULL, .val = 'd' },
+       { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' },
+       { .name = "alignment", .has_arg = 1, .flag = NULL, .val = 'A' },
+       { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'B' },
+       { .name = "erasecount", .has_arg = 1, .flag = NULL, .val = 'E' },
+       { .name = "id", .has_arg = 1, .flag = NULL, .val = 'I' },
+       { .name = "offset", .has_arg = 1, .flag = NULL, .val = 'O' },
+       { .name = "type", .has_arg = 1, .flag = NULL, .val = 'T' },
+       { .name = "setver", .has_arg = 1, .flag = NULL, .val = 'X' },
+       { .name = "infile", .has_arg = 1, .flag = NULL, .val = 'i' },
+       { .name = "outfile", .has_arg = 1, .flag = NULL, .val = 'o' },
+       { .name = "broken-update", .has_arg = 1, .flag = NULL, .val = 'U' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+static const char copyright [] __attribute__((unused)) =
+       "Copyright IBM Corp 2006";
+
+#define CHECK_ENDP(option, endp) do {                  \
+       if (*endp) {                                    \
+               fprintf(stderr,                         \
+                       "Parse error option \'%s\'. "   \
+                       "No correct numeric value.\n"   \
+                       , option);                      \
+               exit(EXIT_FAILURE);                     \
+       }                                               \
+} while(0)
+
+typedef struct myargs {
+       /* common settings */
+       action_t action;
+       int verbose;
+
+       int32_t id;
+       uint8_t type;
+       uint32_t eb_size;
+       uint64_t ec;
+       uint8_t version;
+       uint32_t hdr_offset;
+       uint32_t update_block;
+       uint32_t alignment;
+
+       FILE* fp_in;
+       FILE* fp_out;
+
+       /* special stuff needed to get additional arguments */
+       char *arg1;
+       char **options;                 /* [STRING...] */
+} myargs;
+
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+       unsigned long result = strtoul(cp, endp, base);
+
+       switch (**endp) {
+       case 'G':
+               result *= 1024;
+       case 'M':
+               result *= 1024;
+       case 'k':
+       case 'K':
+               result *= 1024;
+       /* "Ki", "ki", "Mi" or "Gi" are to be used. */
+               if ((*endp)[1] == 'i')
+                       (*endp) += 2;
+       }
+       return result;
+}
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       int err = 0;
+       char* endp;
+
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "cdvA:B:E:I:O:T:X:i:o:U:?V",
+                               long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       case 'c':
+                               fprintf(stderr, "%s\n", copyright);
+                               exit(0);
+                               break;
+                       case 'o': /* output */
+                               args->fp_out = fopen(optarg, "wb");
+                               if ((args->fp_out) == NULL) {
+                                       fprintf(stderr, "Cannot open file %s "
+                                               "for output\n", optarg);
+                                       exit(1);
+                               }
+                               break;
+                       case 'i': /* input */
+                               args->fp_in = fopen(optarg, "rb");
+                               if ((args->fp_in) == NULL) {
+                                       fprintf(stderr, "Cannot open file %s "
+                                               "for input\n", optarg);
+                                       exit(1);
+                               }
+                               break;
+                       case 'v': /* verbose */
+                               args->verbose = 1;
+                               break;
+
+                       case 'B': /* eb_size */
+                               args->eb_size =
+                                       (uint32_t)ustrtoul(optarg, &endp, 0);
+                               CHECK_ENDP("B", endp);
+                               break;
+                       case 'E': /* erasecount */
+                               args->ec = (uint64_t)strtoul(optarg, &endp, 0);
+                               CHECK_ENDP("E", endp);
+                               break;
+                       case 'I': /* id */
+                               args->id = (uint16_t)strtoul(optarg, &endp, 0);
+                               CHECK_ENDP("I", endp);
+                               break;
+                       case 'T': /* type */
+                               args->type =
+                                       (uint16_t)strtoul(optarg, &endp, 0);
+                               CHECK_ENDP("T", endp);
+                               break;
+                       case 'X': /* versionnr */
+                               args->version =
+                                       (uint8_t)strtoul(optarg, &endp, 0);
+                               CHECK_ENDP("X", endp);
+                               break;
+                       case 'O': /* offset for volume hdr */
+                               args->hdr_offset =
+                                       (uint32_t) strtoul(optarg, &endp, 0);
+                               CHECK_ENDP("O", endp);
+                               break;
+
+                       case 'U': /* broken update */
+                               args->action = ACT_BROKEN_UPDATE;
+                               args->update_block =
+                                       (uint32_t) strtoul(optarg, &endp, 0);
+                               CHECK_ENDP("U", endp);
+                               break;
+
+                       case '?': /* help */
+                               fprintf(stderr, "Usage: ubigen [OPTION...]\n");
+                               fprintf(stderr, "%s", doc);
+                               fprintf(stderr, "%s", optionsstr);
+                               fprintf(stderr, "\nReport bugs to %s\n",
+                                       PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+
+                       case 'V':
+                               fprintf(stderr, "%s\n", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+
+                       default:
+                               fprintf(stderr, "%s", usage);
+                               exit(-1);
+               }
+       }
+
+       if (optind < argc) {
+               if (!args->fp_in) {
+                       args->fp_in = fopen(argv[optind++], "rb");
+                       if ((args->fp_in) == NULL) {
+                               fprintf(stderr, "Cannot open file %s for "
+                                       "input\n", argv[optind]);
+                               exit(1);
+                       }
+               }
+       }
+       if (args->id < 0) {
+               err = 1;
+               fprintf(stderr,
+                               "Please specify an UBI Volume ID.\n");
+       }
+       if (args->type == 0) {
+               err = 1;
+               fprintf(stderr,
+                               "Please specify an UBI Volume type.\n");
+       }
+       if (err) {
+               fprintf(stderr, "%s", usage);
+               exit(1);
+       }
+
+       return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+       int rc = 0;
+       ubi_info_t u;
+       struct stat file_info;
+       off_t input_len = 0; /* only used in static volumes */
+
+       myargs args = {
+               .action = ACT_NORMAL,
+               .verbose = 0,
+
+               .id = -1,
+               .type = 0,
+               .eb_size = 0,
+               .update_block = 0,
+               .ec = 0,
+               .version = 0,
+               .hdr_offset = (DEFAULT_PAGESIZE) - (UBI_VID_HDR_SIZE),
+               .alignment = 1,
+
+               .fp_in = NULL,
+               .fp_out = stdout,
+               /* arguments */
+               .arg1 = NULL,
+               .options = NULL,
+       };
+
+       ubigen_init(); /* Init CRC32 table in ubigen */
+
+       /* parse arguments */
+       parse_opt(argc, argv, &args);
+
+       if (fstat(fileno(args.fp_in), &file_info) != 0) {
+               fprintf(stderr, "Cannot fetch file size "
+                               "from input file.\n");
+       }
+       input_len = file_info.st_size;
+
+       rc = ubigen_create(&u, (uint32_t)args.id, args.type,
+                       args.eb_size, args.ec, args.alignment,
+                       args.version, args.hdr_offset, 0 ,input_len,
+                       args.fp_in, args.fp_out);
+
+       if  (rc != 0) {
+               fprintf(stderr, "Cannot create UBI info handler rc: %d\n", rc);
+               exit(EXIT_FAILURE);
+       }
+
+       if (!args.fp_in || !args.fp_out) {
+               fprintf(stderr, "Input/Output error.\n");
+               exit(EXIT_FAILURE);
+
+       }
+
+       if (args.action & ACT_NORMAL) {
+               rc = ubigen_write_complete(u);
+       }
+       else if (args.action & ACT_BROKEN_UPDATE) {
+               rc = ubigen_write_broken_update(u, args.update_block);
+       }
+       if  (rc != 0) {
+               fprintf(stderr, "Error converting input data.\n");
+               exit(EXIT_FAILURE);
+       }
+
+       rc = ubigen_destroy(&u);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/ubigen.h b/ubi-utils/old-tools/src/ubigen.h
new file mode 100644 (file)
index 0000000..0f43a46
--- /dev/null
@@ -0,0 +1,149 @@
+#ifndef __UBIGEN_H__
+#define __UBIGEN_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Frank Haverkamp
+ *
+ * An utility to update UBI volumes.
+ */
+
+#include <stdio.h> /* FILE */
+#include <stdint.h>
+#include <mtd/ubi-header.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_BLOCKSIZE      (128 * 1024)
+#define DEFAULT_PAGESIZE       (2*1024)
+
+#define EUBIGEN_INVALID_TYPE           1
+#define EUBIGEN_INVALID_HDR_OFFSET     2
+#define EUBIGEN_INVALID_ALIGNMENT      3
+#define EUBIGEN_TOO_SMALL_EB           4
+#define EUBIGEN_MAX_ERROR              5
+
+
+typedef enum action {
+       NO_ERROR         = 0x00000000,
+       BROKEN_HDR_CRC   = 0x00000001,
+       BROKEN_DATA_CRC  = 0x00000002,
+       BROKEN_DATA_SIZE = 0x00000004,
+       BROKEN_OMIT_BLK  = 0x00000008,
+       MARK_AS_UPDATE   = 0x00000010,
+} ubigen_action_t;
+
+typedef struct ubi_info *ubi_info_t;
+
+/**
+ * @brief        Initialize the internal CRC32 table.
+ * @note         Necessary because of the used crc32 function in UBI.
+ *               A usage of CRC32, from e.g. zlib will fail.
+ */
+void ubigen_init(void);
+
+/**
+ * @brief        Create an ubigen handle.
+ * @param  ...
+ * @return 0     On sucess.
+ *        else   Error.
+ * @note         This parameterlist is ugly. But we have to use
+ *               two big structs and meta information internally,
+ *               filling them would be even uglier.
+ */
+int ubigen_create(ubi_info_t *u, uint32_t vol_id, uint8_t vol_type,
+                 uint32_t eb_size, uint64_t ec, uint32_t alignment,
+                 uint8_t version, uint32_t vid_hdr_offset,
+                 uint8_t compat_flag, size_t data_size,
+                 FILE* fp_in, FILE* fp_out);
+
+/**
+ * @brief        Destroy an ubigen handle.
+ * @param  u     Handle to free.
+ * @return 0     On success.
+ *        else   Error.
+ */
+int ubigen_destroy(ubi_info_t *u);
+
+/**
+ * @brief        Get number of total logical EBs, necessary for the
+ *               complete storage of data in the handle.
+ * @param  u     The handle.
+ * @return 0     On success.
+ *        else   Error.
+ */
+int ubigen_get_leb_total(ubi_info_t u, size_t* total);
+
+/**
+ * @brief        Get the size in bytes of one logical EB in the handle.
+ * @param  u     The handle.
+ * @return 0     On success.
+ *        else   Error.
+ */
+int ubigen_get_leb_size(ubi_info_t u, size_t* size);
+
+
+/**
+ * @brief        Write a logical EB (fits exactly into 1 physical EB).
+ * @param  u     Handle which holds all necessary data.
+ * @param  action Additional operations which shall be applied on this
+ *               logical eraseblock. Mostly injecting artifical errors.
+ * @return 0     On success.
+ *        else   Error.
+ */
+int ubigen_write_leb(ubi_info_t u, ubigen_action_t action);
+
+/**
+ * @brief        Write a complete array of logical eraseblocks at once.
+ * @param  u     Handle which holds all necessary data.
+ * @return 0     On success.
+ *        else   Error.
+ */
+int ubigen_write_complete(ubi_info_t u);
+
+/**
+ * @brief        Write a single block which is extracted from the
+ *               binary input data.
+ * @param  u     Handle which holds all necessary data.
+ * @param  blk   Logical eraseblock which shall hold a inc. copy entry
+ *               and a bad data crc.
+ * @return 0     On success.
+ *        else   Error.
+ */
+int ubigen_write_broken_update(ubi_info_t u, uint32_t blk);
+
+/**
+ * @brief        Use the current ubi_info data and some additional data
+ *               to set an UBI volume table entry from it.
+ * @param  u     Handle which holds some of the necessary data.
+ * @param  res_bytes Number of reserved bytes which is stored in the volume
+ *                  table entry.
+ * @param  name           A string which shall be used as a volume label.
+ * @param  lvol_r  A pointer to a volume table entry.
+ * @return 0      On success.
+ *        else    Error.
+ */
+int ubigen_set_lvol_rec(ubi_info_t u, size_t reserved_bytes,
+               const char* name, struct ubi_vtbl_record *lvol_rec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UBIGEN_H__ */
diff --git a/ubi-utils/old-tools/src/ubimirror.c b/ubi-utils/old-tools/src/ubimirror.c
new file mode 100644 (file)
index 0000000..2cc4596
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups
+ * 1.4 Migrated to new libubi
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <mtd/ubi-header.h>
+
+#include "config.h"
+#include "error.h"
+#include "example_ubi.h"
+#include "ubimirror.h"
+
+#define PROGRAM_VERSION "1.4"
+
+typedef enum action_t {
+       ACT_NORMAL = 0,
+       ACT_ARGP_ABORT,
+       ACT_ARGP_ERR,
+} action_t;
+
+#define ABORT_ARGP do {                        \
+       args->action = ACT_ARGP_ABORT;  \
+} while (0)
+
+#define ERR_ARGP do {                  \
+       args->action = ACT_ARGP_ERR;    \
+} while (0)
+
+#define VOL_ARGS_MAX 2
+
+static char doc[] = "\nVersion: " PROGRAM_VERSION "\n"
+       "ubimirror - mirrors ubi volumes.\n";
+
+static const char *optionsstr =
+"  -c, --copyright            Print copyright information.\n"
+"  -s, --side=<seqnum>        Use the side <seqnum> as source.\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"  -V, --version              Print program version\n";
+
+static const char *usage =
+"Usage: ubimirror [-c?V] [-s <seqnum>] [--copyright] [--side=<seqnum>]\n"
+"            [--help] [--usage] [--version] <source> <destination>\n";
+
+static const char copyright [] __attribute__((unused)) =
+       "(C) IBM Coorporation 2007";
+
+struct option long_options[] = {
+       { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' },
+       { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+typedef struct myargs {
+       action_t action;
+       int side;
+       int vol_no;                     /* index of current volume */
+       /* @FIXME replace by bootenv_list, makes live easier */
+       /* @FIXME remove the constraint of two entries in the array */
+       const char* vol[VOL_ARGS_MAX];  /* comma separated list of src/dst
+                                          volumes */
+       char *arg1;
+       char **options;         /* [STRING...] */
+} myargs;
+
+static int
+get_update_side(const char* str)
+{
+       uint32_t i = strtoul(str, NULL, 0);
+
+       if ((i != 0) && (i != 1)) {
+               return -1;
+       }
+       return i;
+}
+
+
+static int
+parse_opt(int argc, char **argv, myargs *args)
+{
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "cs:?V", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+                       case 'c':
+                               err_msg("%s", copyright);
+                               ABORT_ARGP;
+                               break;
+                       case 's':
+                               args->side = get_update_side(optarg);
+                               if (args->side < 0) {
+                                       err_msg("Unsupported seqnum: %s.\n"
+                                               "Supported seqnums are '0' "
+                                               "and '1'\n", optarg);
+                                       ERR_ARGP;
+                               }
+                               break;
+                       case '?': /* help */
+                               err_msg("Usage: ubimirror [OPTION...] "
+                                       "<source> <destination>\n");
+                               err_msg("%s", doc);
+                               err_msg("%s", optionsstr);
+                               err_msg("\nReport bugs to %s\n",
+                                       PACKAGE_BUGREPORT);
+                               exit(0);
+                               break;
+                       case 'V':
+                               err_msg("%s", PROGRAM_VERSION);
+                               exit(0);
+                               break;
+                       default:
+                               err_msg("%s", usage);
+                               exit(-1);
+                       }
+       }
+
+       while (optind < argc) {
+               /* only two entries allowed */
+               if (args->vol_no >= VOL_ARGS_MAX) {
+                       err_msg("%s", usage);
+                       ERR_ARGP;
+               }
+               args->vol[(args->vol_no)++] = argv[optind++];
+       }
+
+       return 0;
+}
+
+
+int
+main(int argc, char **argv) {
+       int rc = 0;
+       unsigned int ids[VOL_ARGS_MAX];
+       char err_buf[1024];
+
+       myargs args = {
+               .action = ACT_NORMAL,
+               .side = -1,
+               .vol_no = 0,
+               .vol = {"", ""},
+               .options = NULL,
+       };
+
+       parse_opt(argc, argv, &args);
+       if (args.action == ACT_ARGP_ERR) {
+               rc = 127;
+               goto err;
+       }
+       if (args.action == ACT_ARGP_ABORT) {
+               rc = 126;
+               goto out;
+       }
+       if (args.vol_no < VOL_ARGS_MAX) {
+               fprintf(stderr, "missing volume number for %s\n",
+                       args.vol_no == 0 ? "source and target" : "target");
+               rc = 125;
+               goto out;
+       }
+       for( rc = 0; rc < args.vol_no; ++rc){
+               char *endp;
+               ids[rc] = strtoul(args.vol[rc], &endp, 0);
+               if( *endp != '\0' ){
+                       fprintf(stderr, "invalid volume number %s\n",
+                                       args.vol[rc]);
+                       rc = 125;
+                       goto out;
+               }
+       }
+       rc = ubimirror(EXAMPLE_UBI_DEVICE, args.side, ids, args.vol_no,
+                      err_buf, sizeof(err_buf));
+       if( rc ){
+               err_buf[sizeof err_buf - 1] = '\0';
+               fprintf(stderr, err_buf);
+               if( rc < 0 )
+                       rc = -rc;
+       }
+ out:
+ err:
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/ubimirror.h b/ubi-utils/old-tools/src/ubimirror.h
new file mode 100644 (file)
index 0000000..d7ae2ad
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef __UBIMIRROR_H__
+#define __UBIMIRROR_H__
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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: Oliver Lohmann
+ *
+ * An utility to mirror UBI volumes.
+ */
+
+#include <stdint.h>
+
+/**
+ * @def EUBIMIRROR_SRC_EQ_DST
+ *     @brief Given source volume is also in the set of destination volumes.
+ */
+#define EUBIMIRROR_SRC_EQ_DST  20
+
+/**
+ * @def EUBIMIRROR_NO_SRC
+ *     @brief The given source volume does not exist.
+ */
+#define EUBIMIRROR_NO_SRC      21
+
+/**
+ * @def EUBIMIRROR_NO_DST
+ *     @brief One of the given destination volumes does not exist.
+ */
+#define EUBIMIRROR_NO_DST      22
+
+/**
+ * @brief Mirrors UBI devices from a source device (specified by seqnum)
+ *       to n target devices.
+ * @param devno Device number used by the UBI operations.
+ * @param seqnum An index into ids (defines the src_id).
+ * @param ids An array of ids.
+ * @param ids_size The number of entries in the ids array.
+ * @param err_buf A buffer to store verbose error messages.
+ * @param err_buf_size The size of the error buffer.
+ *
+ * @note A seqnum of value < 0 defaults to a seqnum of 0.
+ * @note A seqnum exceeding the range of ids_size defaults to 0.
+ * @note An empty ids list results in a empty stmt.
+ * @pre        The UBI volume which shall be used as source volume exists.
+ * @pre        The UBI volumes which are defined as destination volumes exist.
+ * @post The content of the UBI volume which was defined as source volume
+ *      equals the content of the volumes which were defined as destination.
+ */
+int ubimirror(uint32_t devno, int seqnum, uint32_t* ids, ssize_t ids_size,
+             char *err_buf, size_t err_buf_size);
+
+#endif /* __UBIMIRROR_H__ */
diff --git a/ubi-utils/old-tools/src/ubiupdate.c b/ubi-utils/old-tools/src/ubiupdate.c
new file mode 100644 (file)
index 0000000..75222d4
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006
+ *
+ * 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.
+ */
+
+/*
+ * An utility to update UBI volumes.
+ *
+ * Authors: Frank Haverkamp
+ *          Joshua W. Boyer
+ *          Artem Bityutskiy
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <libubi.h>
+#include "common.h"
+
+#define PROGRAM_VERSION "1.3"
+#define PROGRAM_NAME    "ubiupdate"
+
+struct args {
+       int truncate;
+       const char *node;
+       const char *img;
+};
+
+static struct args myargs = {
+       .truncate = 0,
+       .node = NULL,
+       .img = NULL,
+};
+
+static const char *doc = PROGRAM_NAME " version " PROGRAM_VERSION
+                        " - a tool to write data to UBI volumes.";
+
+static const char *optionsstr =
+"-n, --vol_id=<volume id>   ID of UBI volume to update\n"
+"-t, --truncate             truncate volume (wipe it out)\n"
+"-h, --help                 print help message\n"
+"-V, --version              print program version";
+
+static const char *usage =
+"Usage: " PROGRAM_NAME " <UBI volume node file name> [-t] [-h] [-V] [--truncate] [--help]\n"
+"\t\t[--version] <image file>\n\n"
+"Example 1: " PROGRAM_NAME " /dev/ubi0_1 fs.img - write file \"fs.img\" to UBI volume /dev/ubi0_1\n"
+"Example 2: " PROGRAM_NAME " /dev/ubi0_1 -t - wipe out UBI volume /dev/ubi0_1";
+
+struct option long_options[] = {
+       { .name = "truncate", .has_arg = 0, .flag = NULL, .val = 't' },
+       { .name = "help",     .has_arg = 0, .flag = NULL, .val = 'h' },
+       { .name = "version",  .has_arg = 0, .flag = NULL, .val = 'V' },
+       { NULL, 0, NULL, 0}
+};
+
+static int parse_opt(int argc, char * const argv[])
+{
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "n:thV", long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+               case 't':
+                       myargs.truncate = 1;
+                       break;
+
+               case 'h':
+                       fprintf(stderr, "%s\n\n", doc);
+                       fprintf(stderr, "%s\n\n", usage);
+                       fprintf(stderr, "%s\n", optionsstr);
+                       exit(0);
+
+               case 'V':
+                       fprintf(stderr, "%s\n", PROGRAM_VERSION);
+                       exit(0);
+
+               case ':':
+                       errmsg("parameter is missing");
+                       return -1;
+
+               default:
+                       fprintf(stderr, "Use -h for help\n");
+                       exit(-1);
+               }
+       }
+
+       if (optind == argc) {
+               errmsg("UBI device name was not specified (use -h for help)");
+               return -1;
+       } else if (optind != argc - 2) {
+               errmsg("specify UBI device name and image file name as first 2 "
+                      "parameters (use -h for help)");
+               return -1;
+       }
+
+       myargs.node = argv[optind];
+       myargs.img  = argv[optind + 1];
+
+       return 0;
+}
+
+static int truncate_volume(libubi_t libubi)
+{
+       int err, fd;
+
+       fd = open(myargs.node, O_RDWR);
+       if (fd == -1) {
+               errmsg("cannot open \"%s\"", myargs.node);
+               perror("open");
+               return -1;
+       }
+
+       err = ubi_update_start(libubi, fd, 0);
+       if (err) {
+               errmsg("cannot truncate volume \"%s\"", myargs.node);
+               perror("ubi_update_start");
+               close(fd);
+               return -1;
+       }
+
+       close(fd);
+       return 0;
+}
+
+static int ubi_write(int fd, const void *buf, int len)
+{
+       int ret;
+
+       while (len) {
+               ret = write(fd, buf, len);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               warnmsg("do not interrupt me!");
+                               continue;
+                       }
+                       errmsg("cannot write %d bytes to volume \"%s\"",
+                              len, myargs.node);
+                       perror("write");
+                       return -1;
+               }
+
+               if (ret == 0) {
+                       errmsg("cannot write %d bytes to volume \"%s\"",
+                              len, myargs.node);
+                       return -1;
+               }
+
+               len -= ret;
+               buf += ret;
+       }
+
+       return 0;
+}
+
+static int update_volume(libubi_t libubi, struct ubi_vol_info *vol_info)
+{
+       int err, fd, ifd;
+       long long bytes;
+       struct stat st;
+       char *buf;
+
+       buf = malloc(vol_info->leb_size);
+       if (!buf) {
+               errmsg("cannot allocate %d bytes of memory", vol_info->leb_size);
+               return -1;
+       }
+
+       err = stat(myargs.img, &st);
+       if (err < 0) {
+               errmsg("stat failed on \"%s\"", myargs.node);
+               goto out_free;
+       }
+
+       bytes = st.st_size;
+       if (bytes > vol_info->rsvd_bytes) {
+               errmsg("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)",
+                      myargs.img, bytes, myargs.node, vol_info->rsvd_bytes);
+               goto out_free;
+       }
+
+       fd = open(myargs.node, O_RDWR);
+       if (fd == -1) {
+               errmsg("cannot open UBI volume \"%s\"", myargs.node);
+               perror("open");
+               goto out_free;
+       }
+
+       ifd = open(myargs.img, O_RDONLY);
+       if (ifd == -1) {
+               errmsg("cannot open \"%s\"", myargs.img);
+               perror("open");
+               goto out_close1;
+       }
+
+       err = ubi_update_start(libubi, fd, bytes);
+       if (err) {
+               errmsg("cannot start volume \"%s\" update", myargs.node);
+               perror("ubi_update_start");
+               goto out_close;
+       }
+
+       while (bytes) {
+               int tocopy = vol_info->leb_size;
+
+               if (tocopy > bytes)
+                       tocopy = bytes;
+
+               err = read(ifd, buf, tocopy);
+               if (err != tocopy) {
+                       if (errno == EINTR) {
+                               warnmsg("do not interrupt me!");
+                               continue;
+                       } else {
+                               errmsg("cannot read %d bytes from \"%s\"",
+                                      tocopy, myargs.img);
+                               perror("read");
+                               goto out_close;
+                       }
+               }
+
+               err = ubi_write(fd, buf, tocopy);
+               if (err)
+                       goto out_close;
+               bytes -= tocopy;
+       }
+
+       close(ifd);
+       close(fd);
+       free(buf);
+       return 0;
+
+out_close:
+       close(ifd);
+out_close1:
+       close(fd);
+out_free:
+       free(buf);
+       return -1;
+}
+
+int main(int argc, char * const argv[])
+{
+       int err;
+       libubi_t libubi;
+       struct ubi_vol_info vol_info;
+
+       err = parse_opt(argc, argv);
+       if (err)
+               return -1;
+
+       if (!myargs.img && !myargs.truncate) {
+               errmsg("incorrect arguments, use -h for help");
+               return -1;
+       }
+
+       libubi = libubi_open();
+       if (libubi == NULL) {
+               perror("Cannot open libubi");
+               goto out_libubi;
+       }
+
+       err = ubi_node_type(libubi, myargs.node);
+       if (err == 1) {
+               errmsg("\"%s\" is an UBI device node, not an UBI volume node",
+                      myargs.node);
+               goto out_libubi;
+       } else if (err < 0) {
+               errmsg("\"%s\" is not an UBI volume node", myargs.node);
+               goto out_libubi;
+       }
+
+       err = ubi_get_vol_info(libubi, myargs.node, &vol_info);
+       if (err) {
+               errmsg("cannot get information about UBI volume \"%s\"",
+                      myargs.node);
+               perror("ubi_get_dev_info");
+               goto out_libubi;
+       }
+
+       if (myargs.truncate)
+               err = truncate_volume(libubi);
+       else
+               err = update_volume(libubi, &vol_info);
+       if (err)
+               goto out_libubi;
+
+       libubi_close(libubi);
+       return 0;
+
+out_libubi:
+       libubi_close(libubi);
+       return -1;
+}
diff --git a/ubi-utils/old-tools/src/unubi.c b/ubi-utils/old-tools/src/unubi.c
new file mode 100644 (file)
index 0000000..5c1d324
--- /dev/null
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ *          Frank Haverkamp, haver@vnet.ibm.com
+ *
+ * 1.2 Removed argp because we want to use uClibc.
+ * 1.3 Minor cleanups.
+ * 1.4 Meanwhile Drake had done a lot of changes, syncing those.
+ * 1.5 Bugfixes, simplifications
+ */
+
+/*
+ * unubi  reads  an  image  file containing blocks of UBI headers and data
+ * (such as produced from nand2bin) and rebuilds the volumes within.   The
+ * default  operation  (when  no  flags are given) is to rebuild all valid
+ * volumes found in the image. unubi  can  also  read  straight  from  the
+ * onboard MTD device (ex. /dev/mtdblock/NAND).
+ */
+
+/* TODO: consideration for dynamic vs. static volumes */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <mtd/ubi-header.h>
+
+#include "crc32.h"
+#include "unubi_analyze.h"
+
+#define EXEC           "unubi"
+#define CONTACT                "haver@vnet.ibm.com"
+#define VERSION                "1.5"
+
+static char doc[] = "\nVersion: " VERSION "\n";
+static int debug = 0;
+
+static const char *optionsstr =
+"Extract volumes and/or analysis information from an UBI data file.\n"
+"When no parameters are flagged or given, the default operation is\n"
+"to rebuild all valid complete UBI volumes found within the image.\n"
+"\n"
+" OPERATIONS\n"
+"  -a, --analyze              Analyze image and create gnuplot graphs\n"
+"  -i, --info-table           Extract volume information tables\n"
+"  -r, --rebuild=<volume-id>  Extract and rebuild volume\n"
+"\n"
+" OPTIONS\n"
+"  -b, --blocksize=<block-size>   Specify size of eraseblocks in image in bytes\n"
+"                             (default 128KiB)\n"
+"  -d, --dir=<output-dir>     Specify output directory\n"
+"  -D, --debug                Enable debug output\n"
+"  -s, --headersize=<header-size>   Specify size reserved for metadata in eraseblock\n"
+       "                             in bytes (default 2048 Byte)\n"
+ /* the -s option might be insufficient when using different vid
+    offset than what we used when writing this tool ... Better would
+    probably be --vid-hdr-offset or alike */
+"\n"
+" ADVANCED\n"
+"  -e, --eb-split             Generate individual eraseblock images (all\n"
+"                             eraseblocks)\n"
+"  -v, --vol-split            Generate individual eraseblock images (valid\n"
+"                             eraseblocks only)\n"
+"  -V, --vol-split!           Raw split by eraseblock (valid eraseblocks only)\n"
+"\n"
+"  -?, --help                 Give this help list\n"
+"      --usage                Give a short usage message\n"
+"      --version              Print program version\n"
+"\n";
+
+static const char *usage =
+"Usage: unubi [-aievV?] [-r <volume-id>] [-b <block-size>] [-d <output-dir>]\n"
+"            [-s <header-size>] [--analyze] [--info-table]\n"
+"            [--rebuild=<volume-id>] [--blocksize=<block-size>]\n"
+"            [--dir=<output-dir>] [--headersize=<header-size>] [--eb-split]\n"
+"            [--vol-split] [--vol-split!] [--help] [--usage] [--version]\n"
+"            image-file\n";
+
+#define ERR_MSG(fmt...)                                                        \
+       fprintf(stderr, EXEC ": " fmt)
+
+#define SPLIT_DATA     1
+#define SPLIT_RAW      2
+
+#define DIR_FMT                "unubi_%s"
+#define KIB            1024
+#define MIB            (KIB * KIB)
+#define MAXPATH                KIB
+
+/* filenames */
+#define FN_INVAL       "%s/eb%04u%s"                   /* invalid eraseblock */
+#define FN_NSURE       "%s/eb%04u_%03u_%03u_%03x%s"    /* unsure eraseblock */
+#define FN_VALID       "%s/eb%04u_%03u_%03u_%03x%s"    /* valid eraseblock */
+#define FN_VOLSP       "%s/vol%03u_%03u_%03u_%04u"     /* split volume */
+#define FN_VOLWH       "%s/volume%03u"                 /* whole volume */
+#define FN_VITBL       "%s/vol_info_table%u"           /* vol info table */
+
+static uint32_t crc32_table[256];
+
+/* struct args:
+ *     bsize           int, blocksize of image blocks
+ *     hsize           int, eraseblock header size
+ *     analyze         flag, when non-zero produce analysis
+ *     eb_split        flag, when non-zero output eb####
+ *                     note: SPLIT_DATA vs. SPLIT_RAW
+ *     vol_split       flag, when non-zero output vol###_####
+ *                     note: SPLIT_DATA vs. SPLIT_RAW
+ *     odir_path       string, directory to place volumes in
+ *     img_path        string, file to read as ubi image
+ *     vols            int array of size UBI_MAX_VOLUMES, where a 1 can be
+ *                     written for each --rebuild flag in the index specified
+ *                     then the array can be counted and collapsed using
+ *                     count_set() and collapse()
+ */
+struct args {
+       int analyze;
+       int itable;
+       uint32_t *vols;
+
+       size_t vid_hdr_offset;
+       size_t data_offset;
+       size_t bsize;           /* FIXME replace by vid_hdr/data offs? */
+       size_t hsize;
+
+       char *odir_path;
+       int eb_split;
+       int vol_split;
+       char *img_path;
+
+       char **options;
+};
+
+struct option long_options[] = {
+       { .name = "rebuild", .has_arg = 1, .flag = NULL, .val = 'r' },
+       { .name = "dir", .has_arg = 1, .flag = NULL, .val = 'd' },
+       { .name = "analyze", .has_arg = 0, .flag = NULL, .val = 'a' },
+       { .name = "blocksize", .has_arg = 1, .flag = NULL, .val = 'b' },
+       { .name = "eb-split", .has_arg = 0, .flag = NULL, .val = 'e' },
+       { .name = "vol-split", .has_arg = 0, .flag = NULL, .val = 'v' },
+       { .name = "vol-split!", .has_arg = 0, .flag = NULL, .val = 'e' },
+       { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
+       { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
+       { .name = "version", .has_arg = 0, .flag = NULL, .val = 'J' },
+       { NULL, 0, NULL, 0}
+};
+
+/**
+ * parses out a numerical value from a string of numbers followed by:
+ *     k, K, kib, KiB for kibibyte
+ *     m, M, mib, MiB for mebibyte
+ **/
+static uint32_t
+str_to_num(char *str)
+{
+       char *s;
+       ulong num;
+
+       s = str;
+       num = strtoul(s, &s, 0);
+
+       if (*s != '\0') {
+               if ((strcmp(s, "KiB") == 0) || (strcmp(s, "K") == 0) ||
+                   (strcmp(s, "kib") == 0) || (strcmp(s, "k") == 0))
+                       num *= KIB;
+               else if ((strcmp(s, "MiB") == 0) || (strcmp(s, "M") == 0) ||
+                   (strcmp(s, "mib") == 0) || (strcmp(s, "m") == 0))
+                       num *= MIB;
+               else
+                       ERR_MSG("couldn't parse '%s', assuming %lu\n",
+                               s, num);
+       }
+       return num;
+}
+
+static int
+parse_opt(int argc, char **argv, struct args *args)
+{
+       uint32_t i;
+
+       while (1) {
+               int key;
+
+               key = getopt_long(argc, argv, "ab:s:d:Deir:vV?J",
+                                 long_options, NULL);
+               if (key == -1)
+                       break;
+
+               switch (key) {
+               case 'a': /* --analyze */
+                       args->analyze = 1;
+                       break;
+               case 'b': /* --block-size=<block-size> */
+                       args->bsize = str_to_num(optarg);
+                       break;
+               case 's': /* --header-size=<header-size> */
+                       args->hsize = str_to_num(optarg);
+                       break;
+               case 'd': /* --dir=<output-dir> */
+                       args->odir_path = optarg;
+                       break;
+               case 'D': /* --debug */
+                       /* I wanted to use -v but that was already
+                          used ... */
+                       debug = 1;
+                       break;
+               case 'e': /* --eb-split */
+                       args->eb_split = SPLIT_RAW;
+                       break;
+               case 'i': /* --info-table */
+                       args->itable = 1;
+                       break;
+               case 'r': /* --rebuild=<volume-id> */
+                       i = str_to_num(optarg);
+                       if (i < UBI_MAX_VOLUMES)
+                               args->vols[str_to_num(optarg)] = 1;
+                       else {
+                               ERR_MSG("volume-id out of bounds\n");
+                               return -1;
+                       }
+                       break;
+               case 'v': /* --vol-split */
+                       if (args->vol_split != SPLIT_RAW)
+                               args->vol_split = SPLIT_DATA;
+                       break;
+               case 'V': /* --vol-split! */
+                       args->vol_split = SPLIT_RAW;
+                       break;
+               case '?': /* help */
+                       fprintf(stderr, "Usage: unubi [OPTION...] "
+                               "image-file\n%s%s\nReport bugs to %s\n",
+                               doc, optionsstr, CONTACT);
+                       exit(0);
+                       break;
+               case 'J':
+                       fprintf(stderr, "%s\n", VERSION);
+                       exit(0);
+                       break;
+               default:
+                       fprintf(stderr, "%s", usage);
+                       exit(-1);
+               }
+       }
+
+       /* FIXME I suppose hsize should be replaced! */
+       args->vid_hdr_offset = args->hsize - UBI_VID_HDR_SIZE;
+       args->data_offset = args->hsize;
+
+       if (optind < argc)
+               args->img_path = argv[optind++];
+       return 0;
+}
+
+
+/**
+ * counts the number of indicies which are flagged in full_array;
+ * full_array is an array of flags (1/0);
+ **/
+static size_t
+count_set(uint32_t *full_array, size_t full_len)
+{
+       size_t count, i;
+
+       if (full_array == NULL)
+               return 0;
+
+       for (i = 0, count = 0; i < full_len; i++)
+               if (full_array[i] != 0)
+                       count++;
+
+       return count;
+}
+
+
+/**
+ * generates coll_array from full_array;
+ * full_array is an array of flags (1/0);
+ * coll_array is an array of the indicies in full_array which are flagged (1);
+ **/
+static size_t
+collapse(uint32_t *full_array, size_t full_len,
+        uint32_t *coll_array, size_t coll_len)
+{
+       size_t i, j;
+
+       if ((full_array == NULL) || (coll_array == NULL))
+               return 0;
+
+       for (i = 0, j = 0; (i < full_len) && (j < coll_len); i++)
+               if (full_array[i] != 0) {
+                       coll_array[j] = i;
+                       j++;
+               }
+
+       return j;
+}
+
+/**
+ * data_crc: save the FILE* position, calculate the crc over a span,
+ *     reset the position
+ * returns non-zero when EOF encountered
+ **/
+static int
+data_crc(FILE* fpin, size_t length, uint32_t *ret_crc)
+{
+       int rc;
+       size_t i;
+       char buf[length];
+       uint32_t crc;
+       fpos_t start;
+
+       rc = fgetpos(fpin, &start);
+       if (rc < 0)
+               return -1;
+
+       for (i = 0; i < length; i++) {
+               int c = fgetc(fpin);
+               if (c == EOF) {
+                       ERR_MSG("unexpected EOF\n");
+                       return -1;
+               }
+               buf[i] = (char)c;
+       }
+
+       rc = fsetpos(fpin, &start);
+       if (rc < 0)
+               return -1;
+
+       crc = clc_crc32(crc32_table, UBI_CRC32_INIT, buf, length);
+       *ret_crc = crc;
+       return 0;
+}
+
+
+/**
+ * reads data of size len from fpin and writes it to path
+ **/
+static int
+extract_data(FILE* fpin, size_t len, const char *path)
+{
+       int rc;
+       size_t i;
+       FILE* fpout;
+
+       rc = 0;
+       fpout = NULL;
+
+       fpout = fopen(path, "wb");
+       if (fpout == NULL) {
+               ERR_MSG("couldn't open file for writing: %s\n", path);
+               rc = -1;
+               goto err;
+       }
+
+       for (i = 0; i < len; i++) {
+               int c = fgetc(fpin);
+               if (c == EOF) {
+                       ERR_MSG("unexpected EOF while writing: %s\n", path);
+                       rc = -2;
+                       goto err;
+               }
+               c = fputc(c, fpout);
+               if (c == EOF) {
+                       ERR_MSG("couldn't write: %s\n", path);
+                       rc = -3;
+                       goto err;
+               }
+       }
+
+ err:
+       if (fpout != NULL)
+               fclose(fpout);
+       return rc;
+}
+
+
+/**
+ * extract volume information table from block. saves and reloads fpin
+ * position
+ * returns -1 when a fpos set or get fails, otherwise <= -2 on other
+ * failure and 0 on success
+ **/
+static int
+extract_itable(FILE *fpin, struct eb_info *cur, size_t bsize, size_t num,
+              const char *path)
+{
+       char filename[MAXPATH + 1];
+       int rc;
+       size_t i, max;
+       fpos_t temp;
+       FILE* fpout = NULL;
+       struct ubi_vtbl_record rec;
+
+       if (fpin == NULL || cur == NULL || path == NULL)
+               return -2;
+
+       /* remember position */
+       rc = fgetpos(fpin, &temp);
+       if (rc < 0)
+               return -1;
+
+       /* jump to top of eraseblock, skip to data section */
+       fsetpos(fpin, &cur->eb_top);
+       if (rc < 0)
+               return -1;
+       fseek(fpin, __be32_to_cpu(cur->ec.data_offset), SEEK_CUR);
+
+       /* prepare output file */
+       if (__be32_to_cpu(cur->vid.vol_id) != UBI_LAYOUT_VOL_ID)
+               return -2;
+       memset(filename, 0, MAXPATH + 1);
+       snprintf(filename, MAXPATH, FN_VITBL, path, num);
+       fpout = fopen(filename, "w");
+       if (fpout == NULL)
+               return -2;
+
+       /* loop through entries */
+       fprintf(fpout,
+               "index\trpebs\talign\ttype\tcrc\t\tname\n");
+       max = bsize - __be32_to_cpu(cur->ec.data_offset);
+       for (i = 0; i < (max / sizeof(rec)); i++) {
+               int blank = 1;
+               char *ptr, *base;
+               char name[UBI_VOL_NAME_MAX + 1];
+               const char *type = "unknown\0";
+               uint32_t crc;
+
+               /* read record */
+               rc = fread(&rec, 1, sizeof(rec), fpin);
+               if (rc == 0)
+                       break;
+               if (rc != sizeof(rec)) {
+                       ERR_MSG("reading volume information "
+                               "table record failed\n");
+                       rc = -3;
+                       goto exit;
+               }
+
+               /* check crc */
+               crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &rec,
+                               UBI_VTBL_RECORD_SIZE_CRC);
+               if (crc != __be32_to_cpu(rec.crc))
+                       continue;
+
+               /* check for empty */
+               base = (char *)&rec;
+               ptr = base;
+               while (blank &&
+                      ((unsigned)(ptr - base) < UBI_VTBL_RECORD_SIZE_CRC)) {
+                       if (*ptr != 0)
+                               blank = 0;
+                       ptr++;
+               }
+
+               if (blank)
+                       continue;
+
+               /* prep type string */
+               if (rec.vol_type == UBI_VID_DYNAMIC)
+                       type = "dynamic\0";
+               else if (rec.vol_type == UBI_VID_STATIC)
+                       type = "static\0";
+
+               /* prep name string */
+               rec.name[__be16_to_cpu(rec.name_len)] = '\0';
+               sprintf(name, "%s", rec.name);
+
+               /* print record line to fpout */
+               fprintf(fpout, "%u\t%u\t%u\t%s\t0x%08x\t%s\n",
+                       i,
+                       __be32_to_cpu(rec.reserved_pebs),
+                       __be32_to_cpu(rec.alignment),
+                       type,
+                       __be32_to_cpu(rec.crc),
+                       name);
+       }
+
+ exit:
+       /* reset position */
+       if (fsetpos(fpin, &temp) < 0)
+               rc = -1;
+
+       if (fpout != NULL)
+               fclose(fpout);
+
+       return rc;
+}
+
+
+/**
+ * using eb chain, tries to rebuild the data of volume at vol_id, or for all
+ * the known volumes, if vol_id is NULL;
+ **/
+static int
+rebuild_volume(FILE * fpin, uint32_t *vol_id, struct eb_info **head,
+              const char *path, size_t block_size, size_t header_size)
+{
+       char filename[MAXPATH];
+       int rc;
+       uint32_t vol, num, data_size;
+       FILE* fpout;
+       struct eb_info *cur;
+
+       rc = 0;
+
+       if ((fpin == NULL) || (head == NULL) || (*head == NULL))
+               return 0;
+
+       /* when vol_id is null, then do all  */
+       if (vol_id == NULL) {
+               cur = *head;
+               vol = __be32_to_cpu(cur->vid.vol_id);
+       } else {
+               vol = *vol_id;
+               eb_chain_position(head, vol, NULL, &cur);
+               if (cur == NULL) {
+                       if (debug)
+                               ERR_MSG("no valid volume %d was found\n", vol);
+                       return -1;
+               }
+       }
+
+       num = 0;
+       snprintf(filename, MAXPATH, FN_VOLWH, path, vol);
+       fpout = fopen(filename, "wb");
+       if (fpout == NULL) {
+               ERR_MSG("couldn't open file for writing: %s\n", filename);
+               return -1;
+       }
+
+       while (cur != NULL) {
+               size_t i;
+
+               if (__be32_to_cpu(cur->vid.vol_id) != vol) {
+                       /* close out file */
+                       fclose(fpout);
+
+                       /* only stay around if that was the only volume */
+                       if (vol_id != NULL)
+                               goto out;
+
+                       /* begin with next */
+                       vol = __be32_to_cpu(cur->vid.vol_id);
+                       num = 0;
+                       snprintf(filename, MAXPATH, FN_VOLWH, path, vol);
+                       fpout = fopen(filename, "wb");
+                       if (fpout == NULL) {
+                               ERR_MSG("couldn't open file for writing: %s\n",
+                                       filename);
+                               return -1;
+                       }
+               }
+
+               while (num < __be32_to_cpu(cur->vid.lnum)) {
+                       /* FIXME haver: I hope an empty block is
+                          written out so that the binary has no holes
+                          ... */
+                       if (debug)
+                               ERR_MSG("missing valid block %d for volume %d\n",
+                                       num, vol);
+                       num++;
+               }
+
+               rc = fsetpos(fpin, &(cur->eb_top));
+               if (rc < 0)
+                       goto out;
+               fseek(fpin, __be32_to_cpu(cur->ec.data_offset), SEEK_CUR);
+
+               if (cur->vid.vol_type == UBI_VID_DYNAMIC)
+                       /* FIXME It might be that alignment has influence */
+                       data_size = block_size - header_size;
+               else
+                       data_size = __be32_to_cpu(cur->vid.data_size);
+
+               for (i = 0; i < data_size; i++) {
+                       int c = fgetc(fpin);
+                       if (c == EOF) {
+                               ERR_MSG("unexpected EOF while writing: %s\n",
+                                       filename);
+                               rc = -2;
+                               goto out;
+                       }
+                       c = fputc(c, fpout);
+                       if (c == EOF) {
+                               ERR_MSG("couldn't write: %s\n", filename);
+                               rc = -3;
+                               goto out;
+                       }
+               }
+
+               cur = cur->next;
+               num++;
+       }
+
+ out:
+       if (vol_id == NULL)
+               fclose(fpout);
+       return rc;
+}
+
+
+/**
+ * traverses FILE* trying to load complete, valid and accurate header data
+ * into the eb chain;
+ **/
+static int
+unubi_volumes(FILE* fpin, uint32_t *vols, size_t vc, struct args *a)
+{
+       char filename[MAXPATH + 1];
+       char reason[MAXPATH + 1];
+       int rc;
+       size_t i, count, itable_num;
+       /* relations:
+        * cur ~ head
+        * next ~ first */
+       struct eb_info *head, *cur, *first, *next;
+       struct eb_info **next_ptr;
+
+       rc = 0;
+       count = 0;
+       itable_num = 0;
+       head = NULL;
+       first = NULL;
+       next = NULL;
+       cur = malloc(sizeof(*cur));
+       if (cur == NULL) {
+               ERR_MSG("out of memory\n");
+               rc = -ENOMEM;
+               goto err;
+       }
+       memset(cur, 0, sizeof(*cur));
+
+       fgetpos(fpin, &(cur->eb_top));
+       while (1) {
+               const char *raw_path;
+               uint32_t crc;
+
+               cur->phys_addr = ftell(fpin);
+               cur->phys_block = cur->phys_addr / a->bsize;
+               cur->data_crc_ok = 0;
+               cur->ec_crc_ok   = 0;
+               cur->vid_crc_ok  = 0;
+
+               memset(filename, 0, MAXPATH + 1);
+               memset(reason, 0, MAXPATH + 1);
+
+               /* in case of an incomplete ec header */
+               raw_path = FN_INVAL;
+
+               /* read erasecounter header */
+               rc = fread(&cur->ec, 1, sizeof(cur->ec), fpin);
+               if (rc == 0)
+                       goto out; /* EOF */
+               if (rc != sizeof(cur->ec)) {
+                       ERR_MSG("reading ec-hdr failed\n");
+                       rc = -1;
+                       goto err;
+               }
+
+               /* check erasecounter header magic */
+               if (__be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) {
+                       snprintf(reason, MAXPATH, ".invalid.ec_magic");
+                       goto invalid;
+               }
+
+               /* check erasecounter header crc */
+               crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->ec),
+                               UBI_EC_HDR_SIZE_CRC);
+               if (__be32_to_cpu(cur->ec.hdr_crc) != crc) {
+                       snprintf(reason, MAXPATH, ".invalid.ec_hdr_crc");
+                       goto invalid;
+               }
+
+               /* read volume id header */
+               rc = fsetpos(fpin, &(cur->eb_top));
+               if (rc != 0)
+                       goto err;
+               fseek(fpin, __be32_to_cpu(cur->ec.vid_hdr_offset), SEEK_CUR);
+               rc = fread(&cur->vid, 1, sizeof(cur->vid), fpin);
+               if (rc == 0)
+                       goto out; /* EOF */
+               if (rc != sizeof(cur->vid)) {
+                       ERR_MSG("reading vid-hdr failed\n");
+                       rc = -1;
+                       goto err;
+               }
+
+               /* if the magic number is 0xFFFFFFFF, then it's very likely
+                * that the volume is empty */
+               if (__be32_to_cpu(cur->vid.magic) == 0xffffffff) {
+                       snprintf(reason, MAXPATH, ".empty");
+                       goto invalid;
+               }
+
+               /* vol_id should be in bounds */
+               if ((__be32_to_cpu(cur->vid.vol_id) >= UBI_MAX_VOLUMES) &&
+                   (__be32_to_cpu(cur->vid.vol_id) <
+                    UBI_INTERNAL_VOL_START)) {
+                       snprintf(reason, MAXPATH, ".invalid");
+                       goto invalid;
+               } else
+                       raw_path = FN_NSURE;
+
+               /* check volume id header magic */
+               if (__be32_to_cpu(cur->vid.magic) != UBI_VID_HDR_MAGIC) {
+                       snprintf(reason, MAXPATH, ".invalid.vid_magic");
+                       goto invalid;
+               }
+               cur->ec_crc_ok = 1;
+
+               /* check volume id header crc */
+               crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(cur->vid),
+                               UBI_VID_HDR_SIZE_CRC);
+               if (__be32_to_cpu(cur->vid.hdr_crc) != crc) {
+                       snprintf(reason, MAXPATH, ".invalid.vid_hdr_crc");
+                       goto invalid;
+               }
+               cur->vid_crc_ok = 1;
+
+               /* check data crc, but only for a static volume */
+               if (cur->vid.vol_type == UBI_VID_STATIC) {
+                       rc = data_crc(fpin, __be32_to_cpu(cur->vid.data_size),
+                                     &crc);
+                       if (rc < 0)
+                               goto err;
+                       if (__be32_to_cpu(cur->vid.data_crc) != crc) {
+                               snprintf(reason, MAXPATH, ".invalid.data_crc");
+                               goto invalid;
+                       }
+                       cur->data_crc_ok = 1;
+               }
+
+               /* enlist this vol, it's valid */
+               raw_path = FN_VALID;
+               cur->linear = count;
+               rc = eb_chain_insert(&head, cur);
+               if (rc < 0) {
+                       if (rc == -ENOMEM) {
+                               ERR_MSG("out of memory\n");
+                               goto err;
+                       }
+                       ERR_MSG("unknown and unexpected error, please contact "
+                               CONTACT "\n");
+                       goto err;
+               }
+
+               /* extract info-table */
+               if (a->itable &&
+                   (__be32_to_cpu(cur->vid.vol_id) == UBI_LAYOUT_VOL_ID)) {
+                       extract_itable(fpin, cur, a->bsize,
+                                      itable_num, a->odir_path);
+                       itable_num++;
+               }
+
+               /* split volumes */
+               if (a->vol_split) {
+                       size_t size = 0;
+
+                       rc = fsetpos(fpin, &(cur->eb_top));
+                       if (rc != 0)
+                               goto err;
+
+                       /*
+                        * FIXME For dynamic UBI volumes we must write
+                        * the maximum available data. The
+                        * vid.data_size field is not used in this
+                        * case. The dynamic volume user is
+                        * responsible for the content.
+                        */
+                       if (a->vol_split == SPLIT_DATA) {
+                               /* Write only data section */
+                               if (cur->vid.vol_type == UBI_VID_DYNAMIC) {
+                                       /* FIXME Formular is not
+                                          always right ... */
+                                       size = a->bsize - a->hsize;
+                               } else
+                                       size = __be32_to_cpu(cur->vid.data_size);
+
+                               fseek(fpin,
+                                     __be32_to_cpu(cur->ec.data_offset),
+                                     SEEK_CUR);
+                       }
+                       else if (a->vol_split == SPLIT_RAW)
+                               /* write entire eraseblock */
+                               size = a->bsize;
+
+                       snprintf(filename, MAXPATH, FN_VOLSP,
+                                a->odir_path,
+                                __be32_to_cpu(cur->vid.vol_id),
+                                __be32_to_cpu(cur->vid.lnum),
+                                __be32_to_cpu(cur->vid.leb_ver), count);
+                       rc = extract_data(fpin, size, filename);
+                       if (rc < 0)
+                               goto err;
+               }
+
+ invalid:
+               /* split eraseblocks */
+               if (a->eb_split) {
+                       /* jump to top of block */
+                       rc = fsetpos(fpin, &(cur->eb_top));
+                       if (rc != 0)
+                               goto err;
+
+                       if (strcmp(raw_path, FN_INVAL) == 0)
+                               snprintf(filename, MAXPATH, raw_path,
+                                        a->odir_path, count, reason);
+                       else
+                               snprintf(filename, MAXPATH, raw_path,
+                                        a->odir_path,
+                                        count,
+                                        __be32_to_cpu(cur->vid.vol_id),
+                                        __be32_to_cpu(cur->vid.lnum),
+                                        __be32_to_cpu(cur->vid.leb_ver),
+                                        reason);
+
+                       rc = extract_data(fpin, a->bsize, filename);
+                       if (rc < 0)
+                               goto err;
+               }
+
+               /* append to simple linked list */
+               if (first == NULL)
+                       next_ptr = &first;
+               else
+                       next_ptr = &next->next;
+
+               *next_ptr = malloc(sizeof(**next_ptr));
+               if (*next_ptr == NULL) {
+                       ERR_MSG("out of memory\n");
+                       rc = -ENOMEM;
+                       goto err;
+               }
+               memset(*next_ptr, 0, sizeof(**next_ptr));
+
+               next = *next_ptr;
+               memcpy(next, cur, sizeof(*next));
+               next->next = NULL;
+
+               count++;
+               rc = fsetpos(fpin, &(cur->eb_top));
+               if (rc != 0)
+                       goto err;
+               fseek(fpin, a->bsize, SEEK_CUR);
+               memset(cur, 0, sizeof(*cur));
+
+               fgetpos(fpin, &(cur->eb_top));
+       }
+
+ out:
+       for (i = 0; i < vc; i++) {
+               rc = rebuild_volume(fpin, &vols[i], &head, a->odir_path,
+                              a->bsize, a->hsize);
+               if (rc < 0)
+                       goto err;
+       }
+
+       /* if there were no volumes specified, rebuild them all,
+        * UNLESS eb_ or vol_ split or analyze was specified */
+       if ((vc == 0) && (!a->eb_split) && (!a->vol_split) &&
+           (!a->analyze) && (!a->itable)) {
+               rc = rebuild_volume(fpin, NULL, &head, a->odir_path, a->bsize,
+                                   a->hsize);
+               if (rc < 0)
+                       goto err;
+       }
+
+ err:
+       free(cur);
+
+       if (a->analyze) {
+               char fname[PATH_MAX];
+               FILE *fp;
+
+               unubi_analyze(&head, first, a->odir_path);
+
+               /* prepare output files */
+               memset(fname, 0, PATH_MAX + 1);
+               snprintf(fname, PATH_MAX, "%s/%s", a->odir_path, FN_EH_STAT);
+               fp = fopen(fname, "w");
+               if (fp != NULL) {
+                       eb_chain_print(fp, head);
+                       fclose(fp);
+               }
+       }
+       eb_chain_destroy(&head);
+       eb_chain_destroy(&first);
+
+       return rc;
+}
+
+
+/**
+ * handles command line arguments, then calls unubi_volumes
+ **/
+int
+main(int argc, char *argv[])
+{
+       int rc, free_a_odir;
+       size_t vols_len;
+       uint32_t *vols;
+       FILE* fpin;
+       struct args a;
+
+       rc = 0;
+       free_a_odir = 0;
+       vols_len = 0;
+       vols = NULL;
+       fpin = NULL;
+       init_crc32_table(crc32_table);
+
+       /* setup struct args a */
+       memset(&a, 0, sizeof(a));
+       a.bsize = 128 * KIB;
+       a.hsize = 2 * KIB;
+       a.vols = malloc(sizeof(*a.vols) * UBI_MAX_VOLUMES);
+       if (a.vols == NULL) {
+               ERR_MSG("out of memory\n");
+               rc = ENOMEM;
+               goto err;
+       }
+       memset(a.vols, 0, sizeof(*a.vols) * UBI_MAX_VOLUMES);
+
+       /* parse args and check for validity */
+       parse_opt(argc, argv, &a);
+       if (a.img_path == NULL) {
+               ERR_MSG("no image file specified\n");
+               rc = EINVAL;
+               goto err;
+       }
+       else if (a.odir_path == NULL) {
+               char *ptr;
+               int len;
+
+               ptr = strrchr(a.img_path, '/');
+               if (ptr == NULL)
+                       ptr = a.img_path;
+               else
+                       ptr++;
+
+               len = strlen(DIR_FMT) + strlen(ptr);
+               free_a_odir = 1;
+               a.odir_path = malloc(sizeof(*a.odir_path) * len);
+               if (a.odir_path == NULL) {
+                       ERR_MSG("out of memory\n");
+                       rc = ENOMEM;
+                       goto err;
+               }
+               snprintf(a.odir_path, len, DIR_FMT, ptr);
+       }
+
+       fpin = fopen(a.img_path, "rb");
+       if (fpin == NULL) {
+               ERR_MSG("couldn't open file for reading: "
+                       "%s\n", a.img_path);
+               rc = EINVAL;
+               goto err;
+       }
+
+       rc = mkdir(a.odir_path, 0777);
+       if ((rc < 0) && (errno != EEXIST)) {
+               ERR_MSG("couldn't create ouput directory: "
+                       "%s\n", a.odir_path);
+               rc = -rc;
+               goto err;
+       }
+
+       /* fill in vols array */
+       vols_len = count_set(a.vols, UBI_MAX_VOLUMES);
+       if (vols_len > 0) {
+               vols = malloc(sizeof(*vols) * vols_len);
+               if (vols == NULL) {
+                       ERR_MSG("out of memory\n");
+                       rc = ENOMEM;
+                       goto err;
+               }
+               collapse(a.vols, UBI_MAX_VOLUMES, vols, vols_len);
+       }
+
+       /* unubi volumes */
+       rc = unubi_volumes(fpin, vols, vols_len, &a);
+       if (rc < 0) {
+               /* ERR_MSG("error encountered while working on image file: "
+                  "%s\n", a.img_path); */
+               rc = -rc;
+               goto err;
+       }
+
+ err:
+       free(a.vols);
+       if (free_a_odir != 0)
+               free(a.odir_path);
+       if (fpin != NULL)
+               fclose(fpin);
+       if (vols_len > 0)
+               free(vols);
+       return rc;
+}
diff --git a/ubi-utils/old-tools/src/unubi_analyze.c b/ubi-utils/old-tools/src/unubi_analyze.c
new file mode 100644 (file)
index 0000000..2ab3b87
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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.
+ */
+
+/*
+ * Authors: Drake Dowsett, dowsett@de.ibm.com
+ * Contact: Andreas Arnez, arnez@de.ibm.com
+ *
+ * unubi uses the following functions to generate analysis output based on
+ * the header information in a raw-UBI image
+ */
+
+/*
+ * TODO: use OOB data to check for eraseblock validity in NAND images
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "unubi_analyze.h"
+#include "crc32.h"
+
+#define EC_X_INT       50
+
+/**
+ * intcmp - function needed by qsort to order integers
+ **/
+int intcmp(const void *a, const void *b)
+{
+       int A = *(int *)a;
+       int B = *(int *)b;
+       return A - B;
+}
+
+int longcmp(const void *a, const void *b)
+{
+       long long A = *(long long *)a;
+       long long B = *(long long *)b;
+       return A - B;
+}
+
+
+/**
+ * unubi_analyze_group_index - finds the normalized index in an array
+ * item:       look for this item in the array
+ * array:      array to search through
+ * size:       length of the array
+ * array should be sorted for this algorithm to perform properly;
+ * if the item is not found returns -1, otherwise return value is the
+ * index in the array (note this contricts the array size to 2^32-1);
+ **/
+int
+norm_index(uint32_t item, uint32_t *array, size_t length)
+{
+       size_t i, index;
+
+       for (index = 0, i = 0; i < length; i++) {
+               if ((i != 0) && (array[i] != array[i - 1]))
+                       index++;
+
+               if (item == array[i])
+                       return index;
+       }
+
+       return -1;
+}
+
+
+/**
+ * unubi_analyze_ec_hdr - generate data table and plot script
+ * first:      head of simple linked list
+ * path:       folder to write into
+ * generates a data file containing the eraseblock index in the image
+ * and the erase counter found in its ec header;
+ * if the crc check fails, the line is commented out in the data file;
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_ec_hdr(struct eb_info *first, const char *path)
+{
+       char filename[PATH_MAX + 1];
+       size_t count, eraseblocks;
+       uint32_t crc, crc32_table[256];
+       uint64_t *erase_counts;
+       FILE* fpdata;
+       FILE* fpplot;
+       struct eb_info *cur;
+
+       if (first == NULL)
+               return -1;
+
+       /* crc check still needed for `first' linked list */
+       init_crc32_table(crc32_table);
+
+       /* prepare output files */
+       memset(filename, 0, PATH_MAX + 1);
+       snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA);
+       fpdata = fopen(filename, "w");
+       if (fpdata == NULL)
+               return -1;
+
+       memset(filename, 0, PATH_MAX + 1);
+       snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT);
+       fpplot = fopen(filename, "w");
+       if (fpplot == NULL) {
+               fclose(fpdata);
+               return -1;
+       }
+
+       /* make executable */
+       chmod(filename, 0755);
+
+       /* first run: count elements */
+       count = 0;
+       cur = first;
+       while (cur != NULL) {
+               cur = cur->next;
+               count++;
+       }
+       eraseblocks = count;
+
+       erase_counts = malloc(eraseblocks * sizeof(*erase_counts));
+       if (!erase_counts) {
+               perror("out of memory");
+               exit(EXIT_FAILURE);
+       }
+
+       memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts));
+
+       /* second run: populate array to sort */
+       count = 0;
+       cur = first;
+       while (cur != NULL) {
+               erase_counts[count] = __be64_to_cpu(cur->ec.ec);
+               cur = cur->next;
+               count++;
+       }
+       qsort(erase_counts, eraseblocks, sizeof(*erase_counts),
+             (void *)longcmp);
+
+       /* third run: generate data file */
+       count = 0;
+       cur = first;
+       fprintf(fpdata, "# eraseblock_no actual_erase_count "
+                       "sorted_erase_count\n");
+       while (cur != NULL) {
+               crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec,
+                               UBI_EC_HDR_SIZE_CRC);
+
+               if ((__be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) ||
+                   (crc != __be32_to_cpu(cur->ec.hdr_crc)))
+                       fprintf(fpdata, "# ");
+
+               fprintf(fpdata, "%u %llu %llu", count,
+                       __be64_to_cpu(cur->ec.ec),
+                       erase_counts[count]);
+
+               if (__be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC)
+                       fprintf(fpdata, " ## bad magic: %08x",
+                               __be32_to_cpu(cur->ec.magic));
+
+               if (crc != __be32_to_cpu(cur->ec.hdr_crc))
+                       fprintf(fpdata, " ## CRC mismatch: given=%08x, "
+                               "calc=%08x", __be32_to_cpu(cur->ec.hdr_crc),
+                               crc);
+
+               fprintf(fpdata, "\n");
+
+               cur = cur->next;
+               count++;
+       }
+       fclose(fpdata);
+
+       fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+       fprintf(fpplot, "set xlabel \"eraseblock\"\n");
+
+       /* fourth run: generate plot file xtics */
+       count = 0;
+       cur = first;
+       fprintf(fpplot, "set xtics (");
+       while (cur != NULL) {
+               if ((count % EC_X_INT) == 0) {
+                       if (count > 0)
+                               fprintf(fpplot, ", ");
+                       fprintf(fpplot, "%d", count);
+               }
+
+               cur = cur->next;
+               count++;
+       }
+       fprintf(fpplot, ")\n");
+
+       fprintf(fpplot, "set ylabel \"erase count\"\n");
+       fprintf(fpplot, "set xrange [-1:%u]\n", eraseblocks + 1);
+       fprintf(fpplot, "# set yrange [-1:%llu]\n",
+               erase_counts[eraseblocks - 1] + 1);
+       fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n",
+               FN_EH_DATA, FN_EH_DATA);
+       fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n",
+               FN_EH_DATA, FN_EH_DATA);
+       fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+       fclose(fpplot);
+
+       return 0;
+}
+
+
+/**
+ * unubi_analyze_vid_hdr - generate data table and plot script
+ * head:       head of complex linked list (eb_chain)
+ * path:       folder to write into
+ * generates a data file containing the volume id, logical number, leb version,
+ * and data size from the vid header;
+ * all eraseblocks listed in the eb_chain are valid (checked in unubi);
+ * also generates a simple gnuplot sript for quickly viewing one
+ * display of the data file;
+ **/
+int
+unubi_analyze_vid_hdr(struct eb_info **head, const char *path)
+{
+       char filename[PATH_MAX + 1];
+       int rc, y1, y2;
+       size_t count, step, breadth;
+       uint32_t *leb_versions, *data_sizes;
+       FILE* fpdata;
+       FILE* fpplot;
+       struct eb_info *cur;
+
+       if (head == NULL || *head == NULL)
+               return -1;
+
+       rc = 0;
+       fpdata = NULL;
+       fpplot = NULL;
+       data_sizes = NULL;
+       leb_versions = NULL;
+
+       /* prepare output files */
+       memset(filename, 0, PATH_MAX + 1);
+       snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA);
+       fpdata = fopen(filename, "w");
+       if (fpdata == NULL) {
+               rc = -1;
+               goto exit;
+       }
+
+       memset(filename, 0, PATH_MAX + 1);
+       snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT);
+       fpplot = fopen(filename, "w");
+       if (fpplot == NULL) {
+               rc = -1;
+               goto exit;
+       }
+
+       /* make executable */
+       chmod(filename, 0755);
+
+       /* first run: count elements */
+       count = 0;
+       cur = *head;
+       while (cur != NULL) {
+               cur = cur->next;
+               count++;
+       }
+       breadth = count;
+
+       leb_versions = malloc(breadth * sizeof(uint32_t));
+       if (leb_versions == NULL) {
+               rc = -1;
+               goto exit;
+       }
+       memset(leb_versions, 0, breadth * sizeof(uint32_t));
+
+       data_sizes = malloc(breadth * sizeof(uint32_t));
+       if (data_sizes == NULL) {
+               rc = -1;
+               goto exit;
+       }
+       memset(data_sizes, 0, breadth * sizeof(*data_sizes));
+
+       /* second run: populate arrays to sort */
+       count = 0;
+       cur = *head;
+       while (cur != NULL) {
+               leb_versions[count] = __be32_to_cpu(cur->vid.leb_ver);
+               data_sizes[count] = __be32_to_cpu(cur->vid.data_size);
+               cur = cur->next;
+               count++;
+       }
+       qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp);
+       qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp);
+
+       /* third run: generate data file */
+       count = 0;
+       cur = *head;
+       fprintf(fpdata, "# x_axis vol_id lnum   y1_axis leb_ver   "
+               "y2_axis data_size\n");
+       while (cur != NULL) {
+               y1 = norm_index(__be32_to_cpu(cur->vid.leb_ver), leb_versions,
+                               breadth);
+               y2 = norm_index(__be32_to_cpu(cur->vid.data_size), data_sizes,
+                               breadth);
+
+               if ((y1 == -1) || (y2 == -1)) {
+                       rc = -1;
+                       goto exit;
+               }
+
+               fprintf(fpdata, "%u %u %u   %u %u   %u %u\n",
+                       count,
+                       __be32_to_cpu(cur->vid.vol_id),
+                       __be32_to_cpu(cur->vid.lnum),
+                       y1,
+                       __be32_to_cpu(cur->vid.leb_ver),
+                       y2,
+                       __be32_to_cpu(cur->vid.data_size));
+               cur = cur->next;
+               count++;
+       }
+
+       fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
+       fprintf(fpplot, "set xlabel \"volume\"\n");
+
+       /* fourth run: generate plot file xtics */
+       count = 0;
+       step = 0;
+       cur = *head;
+       fprintf(fpplot, "set xtics (");
+       while (cur != NULL) {
+               if (count > 0)
+                       fprintf(fpplot, ", ");
+               if (step != __be32_to_cpu(cur->vid.vol_id)) {
+                       step = __be32_to_cpu(cur->vid.vol_id);
+                       fprintf(fpplot, "\"%d\" %d 0", step, count);
+               }
+               else
+                       fprintf(fpplot, "\"%d\" %d 1",
+                               __be32_to_cpu(cur->vid.lnum), count);
+               cur = cur->next;
+               count++;
+       }
+       fprintf(fpplot, ")\n");
+       fprintf(fpplot, "set nox2tics\n");
+
+       /* fifth run: generate plot file ytics */
+       count = 0;
+       cur = *head;
+       fprintf(fpplot, "set ylabel \"leb version\"\n");
+       fprintf(fpplot, "set ytics (");
+       while (cur->next != NULL) {
+               y1 = norm_index(__be32_to_cpu(cur->vid.leb_ver), leb_versions,
+                               breadth);
+
+               if (y1 == -1) {
+                       rc = -1;
+                       goto exit;
+               }
+
+               if (count > 0)
+                       fprintf(fpplot, ", ");
+
+               fprintf(fpplot, "\"%u\" %u", __be32_to_cpu(cur->vid.leb_ver),
+                       y1);
+
+               cur = cur->next;
+               count++;
+       }
+       fprintf(fpplot, ")\n");
+
+       /* sixth run: generate plot file y2tics */
+       count = 0;
+       cur = *head;
+       fprintf(fpplot, "set y2label \"data size\"\n");
+       fprintf(fpplot, "set y2tics (");
+       while (cur != NULL) {
+               y2 = norm_index(__be32_to_cpu(cur->vid.data_size),
+                               data_sizes, breadth);
+
+               if (y2 == -1) {
+                       rc = -1;
+                       goto exit;
+               }
+
+               if (count > 0)
+                       fprintf(fpplot, ", ");
+
+               fprintf(fpplot, "\"%u\" %u", __be32_to_cpu(cur->vid.data_size),
+                       y2);
+
+               cur = cur->next;
+               count++;
+       }
+       fprintf(fpplot, ")\n");
+
+       y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth);
+       y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth);
+       fprintf(fpplot, "set xrange [-1:%u]\n", count + 1);
+       fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1);
+       fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1);
+       fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" "
+                       "axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA);
+       fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" "
+                       "axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA);
+       fprintf(fpplot, "pause -1 \"press ENTER\"\n");
+
+ exit:
+       if (fpdata != NULL)
+               fclose(fpdata);
+       if (fpplot != NULL)
+               fclose(fpplot);
+       if (data_sizes != NULL)
+               free(data_sizes);
+       if (leb_versions != NULL)
+               free(leb_versions);
+
+       return rc;
+}
+
+
+/**
+ * unubi_analyze - run all analyses
+ * head:       eb_chain head
+ * first:      simple linked list of eraseblock headers (use .next)
+ * path:       directory (without trailing slash) to output to
+ * returns 0 upon successful completion, or -1 otherwise
+ **/
+int
+unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path)
+{
+       int ec_rc, vid_rc;
+
+       if (path == NULL)
+               return -1;
+
+       ec_rc = unubi_analyze_ec_hdr(first, path);
+       vid_rc = unubi_analyze_vid_hdr(head, path);
+       if (ec_rc < 0 || vid_rc < 0)
+               return -1;
+
+       return 0;
+}
diff --git a/ubi-utils/old-tools/src/unubi_analyze.h b/ubi-utils/old-tools/src/unubi_analyze.h
new file mode 100644 (file)
index 0000000..8588219
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2006, 2007
+ *
+ * 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.
+ */
+
+#ifndef __UNUBI_ANALYZE_H__
+#define __UNUBI_ANALYZE_H__
+
+/*
+ * Author:  Drake Dowsett
+ * Contact: Andreas Arnez (arnez@de.ibm.com)
+ *
+ * Eraseblock Chain
+ *
+ * A linked list structure to order eraseblocks by volume and logical number
+ * and to update by version number. Doesn't contain actual eraseblock data
+ * but rather the erasecounter and volume id headers as well as a position
+ * indicator.
+ *
+ * Diagram Example:
+ *
+ * [V1.0v0]->[V1.1v2]->[V1.2v1]->[V2.0v2]->[V2.1v0]->[V2.2v1]->NULL
+ *     |         |         |         |         |         |
+ *   NULL    [V1.1v1]  [V1.2v0]  [V2.0v1]    NULL    [V2.2v0]
+ *               |         |         |                   |
+ *           [V1.1v0]    NULL    [V2.0v0]              NULL
+ *               |                   |
+ *             NULL                NULL
+ *
+ * [VA.BvC] represents the eb_info for the eraseblock with the vol_id A,
+ * lnum B and leb_ver C
+ * -> represents the `next' pointer
+ * | represents the `older' pointer
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mtd/ubi-header.h>
+
+#define FN_EH_STAT      "analysis_blocks.txt"
+#define FN_EH_DATA     "analysis_ec_hdr.data"
+#define FN_EH_PLOT     "analysis_ec_hdr.plot"
+#define FN_VH_DATA     "analysis_vid_hdr.data"
+#define FN_VH_PLOT     "analysis_vid_hdr.plot"
+
+struct eb_info {
+       struct ubi_ec_hdr ec;
+       struct ubi_vid_hdr vid;
+
+       fpos_t eb_top;
+       uint32_t linear;
+       int ec_crc_ok;
+       int vid_crc_ok;
+       int data_crc_ok;
+       uint32_t phys_addr;
+       int phys_block;
+
+       struct eb_info *next;
+       struct eb_info *older;
+};
+
+int eb_chain_insert(struct eb_info **head, struct eb_info *item);
+
+int eb_chain_position(struct eb_info **head, uint32_t vol_id, uint32_t *lnum,
+                     struct eb_info **pos);
+
+int eb_chain_print(FILE *stream, struct eb_info *head);
+
+int eb_chain_destroy(struct eb_info **head);
+
+int unubi_analyze(struct eb_info **head, struct eb_info *first,
+                 const char *path);
+
+#endif /* __UNUBI_ANALYZE_H__ */