cleanup mtd, implement jffs2write - one step closer to config preserving system upgrades
authorFelix Fietkau <nbd@openwrt.org>
Mon, 20 Aug 2007 16:12:24 +0000 (16:12 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Mon, 20 Aug 2007 16:12:24 +0000 (16:12 +0000)
SVN-Revision: 8444

package/mtd/Makefile
package/mtd/src/Makefile
package/mtd/src/crc32.c [new file with mode: 0644]
package/mtd/src/crc32.h [new file with mode: 0644]
package/mtd/src/jffs2.c [new file with mode: 0644]
package/mtd/src/jffs2.h [new file with mode: 0644]
package/mtd/src/mtd-api.h [new file with mode: 0644]
package/mtd/src/mtd.c
package/mtd/src/mtd.h [deleted file]

index cf23fbe..b755b6f 100644 (file)
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=mtd
-PKG_RELEASE:=5
+PKG_RELEASE:=6
 
 PKG_BUILD_DIR := $(KERNEL_BUILD_DIR)/$(PKG_NAME)
 
index 99e3ad7..5ab7d7b 100644 (file)
@@ -1,12 +1,6 @@
-# $Id$
-
-all: mtd
-
-%.o: %.c
-       $(CC) -I. $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $^
-
-mtd: mtd.o
-       $(CC) -o $@ $^
+CC = gcc
+CFLAGS += -Wall
 
+mtd: mtd.o jffs2.o crc32.o
 clean:
-       rm -f *.o mtd
+       rm -f *.o jffs2 
diff --git a/package/mtd/src/crc32.c b/package/mtd/src/crc32.c
new file mode 100644 (file)
index 0000000..6b1e50c
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+       0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+       0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+       0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+       0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+       0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+       0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+       0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+       0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+       0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+       0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+       0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+       0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+       0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+       0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+       0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+       0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+       0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+       0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+       0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+       0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+       0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+       0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+       0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+       0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+       0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+       0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+       0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+       0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+       0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+       0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+       0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+       0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+       0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+       0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+       0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+       0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+       0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+       0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+       0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+       0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+       0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+       0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+       0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+       0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+       0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+       0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+       0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+       0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+       0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+       0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+       0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+       0x2d02ef8dL
+};
diff --git a/package/mtd/src/crc32.h b/package/mtd/src/crc32.h
new file mode 100644 (file)
index 0000000..ee3145b
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef CRC32_H
+#define CRC32_H
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+       static inline uint32_t
+crc32(uint32_t val, const void *ss, int len)
+{
+       const unsigned char *s = ss;
+       while (--len >= 0)
+               val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+       return val;
+}
+
+#endif
diff --git a/package/mtd/src/jffs2.c b/package/mtd/src/jffs2.c
new file mode 100644 (file)
index 0000000..7b68ae5
--- /dev/null
@@ -0,0 +1,303 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include "jffs2.h"
+#include "crc32.h"
+#include "mtd.h"
+
+#define PAD(x) (((x)+3)&~3)
+
+#define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4"
+#define JFFS2_EOF "\xde\xad\xc0\xde"
+
+static int last_ino = 0;
+static int last_version = 0;
+static char *buf = NULL;
+static int ofs = 0;
+static int outfd = 0;
+static int mtdofs = 0;
+
+static void prep_eraseblock(void);
+
+static void pad(int size)
+{
+       if ((ofs % size == 0) && (ofs < erasesize))
+               return;
+
+       if (ofs < erasesize) {
+               memset(buf + ofs, 0xff, (size - (ofs % size)));
+               ofs += (size - (ofs % size));
+       }
+       ofs = ofs % erasesize;
+       if (ofs == 0) {
+               mtd_erase_block(outfd, mtdofs);
+               write(outfd, buf, erasesize);
+               mtdofs += erasesize;
+       }
+}
+
+static inline int rbytes(void)
+{
+       return erasesize - (ofs % erasesize);
+}
+
+static inline void add_data(char *ptr, int len)
+{
+       if (ofs + len > erasesize) {
+               pad(erasesize);
+               prep_eraseblock();
+       }
+       memcpy(buf + ofs, ptr, len);
+       ofs += len;
+}
+
+static void prep_eraseblock(void)
+{
+       if (ofs > 0)
+               return;
+
+       add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
+}
+
+static int add_dirent(char *name, char type, int parent)
+{
+       struct jffs2_raw_dirent *de;
+
+       if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
+               pad(erasesize);
+
+       prep_eraseblock();
+       last_ino++;
+       memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
+       de = (struct jffs2_raw_dirent *) (buf + ofs);
+
+       de->magic = JFFS2_MAGIC_BITMASK;
+       de->nodetype = JFFS2_NODETYPE_DIRENT;
+       de->type = type;
+       de->name_crc = crc32(0, name, strlen(name));
+       de->ino = last_ino++;
+       de->pino = parent;
+       de->totlen = sizeof(*de) + strlen(name);
+       de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
+       de->version = last_version++;
+       de->mctime = 0;
+       de->nsize = strlen(name);
+       de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
+       memcpy(de->name, name, strlen(name));
+
+       ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
+       pad(4);
+
+       return de->ino;
+}
+
+static int add_dir(char *name, int parent)
+{
+       struct jffs2_raw_inode ri;
+       int inode;
+
+       inode = add_dirent(name, IFTODT(S_IFDIR), parent);
+
+       if (rbytes() < sizeof(ri))
+               pad(erasesize);
+       prep_eraseblock();
+
+       memset(&ri, 0, sizeof(ri));
+       ri.magic = JFFS2_MAGIC_BITMASK;
+       ri.nodetype = JFFS2_NODETYPE_INODE;
+       ri.totlen = sizeof(ri);
+       ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
+
+       ri.ino = inode;
+       ri.mode = S_IFDIR | 0755;
+       ri.uid = ri.gid = 0;
+       ri.atime = ri.ctime = ri.mtime = 0;
+       ri.isize = ri.csize = ri.dsize = 0;
+       ri.version = 1;
+       ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
+       ri.data_crc = 0;
+
+       add_data((char *) &ri, sizeof(ri));
+       pad(4);
+       return inode;
+}
+
+static void add_file(char *name, int parent)
+{
+       int inode, f_offset = 0, fd;
+       struct jffs2_raw_inode ri;
+       struct stat st;
+       char wbuf[4096], *fname;
+       FILE *f;
+
+       if (stat(name, &st)) {
+               fprintf(stderr, "File %s does not exist\n", name);
+               return;
+       }
+
+       fname = strrchr(name, '/');
+       if (fname)
+               fname++;
+       else
+               fname = name;
+
+       inode = add_dirent(name, IFTODT(S_IFREG), parent);
+       memset(&ri, 0, sizeof(ri));
+       ri.magic = JFFS2_MAGIC_BITMASK;
+       ri.nodetype = JFFS2_NODETYPE_INODE;
+
+       ri.ino = inode;
+       ri.mode = st.st_mode;
+       ri.uid = ri.gid = 0;
+       ri.atime = st.st_atime;
+       ri.ctime = st.st_ctime;
+       ri.mtime = st.st_mtime;
+       ri.isize = st.st_size;
+       ri.compr = 0;
+       ri.usercompr = 0;
+
+       fd = open(name, 0);
+       if (fd <= 0) {
+               fprintf(stderr, "File %s does not exist\n", name);
+               return;
+       }
+
+       for (;;) {
+               int len = 0;
+
+               for (;;) {
+                       len = rbytes() - sizeof(ri);
+                       if (len > 128)
+                               break;
+
+                       pad(erasesize);
+                       prep_eraseblock();
+               }
+
+               if (len > sizeof(wbuf))
+                       len = sizeof(wbuf);
+
+               len = read(fd, wbuf, len);
+               if (len <= 0)
+                       break;
+
+               ri.totlen = sizeof(ri) + len;
+               ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
+               ri.version = ++last_version;
+               ri.offset = f_offset;
+               ri.csize = ri.dsize = len;
+               ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
+               ri.data_crc = crc32(0, wbuf, len);
+               f_offset += len;
+               add_data((char *) &ri, sizeof(ri));
+               add_data(wbuf, len);
+               pad(4);
+               prep_eraseblock();
+       }
+
+       close(fd);
+}
+
+int mtd_write_jffs2(char *mtd, char *filename, char *dir)
+{
+       int target_ino = 0;
+       int err = -1, fdeof = 0;
+       off_t offset;
+
+       outfd = mtd_check_open(mtd);
+       if (!outfd)
+               return -1;
+
+       if (quiet < 2)
+               fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
+       
+       buf = malloc(erasesize);
+       if (!buf) {
+               fprintf(stderr, "Out of memory!\n");
+               goto done;
+       }
+
+       /* parse the structure of the jffs2 first
+        * locate the directory that the file is going to be placed in */
+       for(;;) {
+               struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
+               unsigned int ofs = 0;
+
+               if (read(outfd, buf, erasesize) != erasesize) {
+                       fdeof = 1;
+                       break;
+               }
+               mtdofs += erasesize;
+
+               if (node->magic == 0x8519) {
+                       fprintf(stderr, "Error: wrong endianness filesystem\n");
+                       goto done;
+               }
+
+               /* assume  no magic == end of filesystem
+                * the filesystem will probably end with be32(0xdeadc0de) */
+               if (node->magic != 0x1985)
+                       break;
+
+               while (ofs < erasesize) {
+                       node = (struct jffs2_unknown_node *) (buf + ofs);
+                       if (node->magic == 0x1985) {
+                               ofs += PAD(node->totlen);
+                               if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
+                                       struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
+                                       
+                                       /* is this the right directory name and is it a subdirectory of / */
+                                       if ((de->pino == 1) && !strncmp(de->name, dir, de->nsize))
+                                               target_ino = de->ino;
+
+                                       /* store the last inode and version numbers for adding extra files */
+                                       if (last_ino < de->ino)
+                                               last_ino = de->ino;
+                                       if (last_version < de->version)
+                                               last_version = de->version;
+                               }
+                       } else {
+                               ofs = ~0;
+                       }
+               }
+       }
+
+       if (fdeof) {
+               fprintf(stderr, "Error: No room for additional data\n");
+               goto done;
+       }
+
+       /* jump back one eraseblock */
+       mtdofs -= erasesize;
+       lseek(outfd, mtdofs, SEEK_SET);
+
+       ofs = 0;
+
+       if (!last_ino)
+               last_ino = 1;
+
+       if (!target_ino)
+               target_ino = add_dir(dir, 1);
+
+       add_file(filename, target_ino);
+       pad(erasesize);
+
+       /* add eof marker, pad to eraseblock size and write the data */
+       add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
+       pad(erasesize);
+
+       err = 0;
+
+done:
+       close(outfd);
+       if (buf)
+               free(buf);
+
+       return err;
+}
diff --git a/package/mtd/src/jffs2.h b/package/mtd/src/jffs2.h
new file mode 100644 (file)
index 0000000..70d4619
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id: jffs2.h,v 1.38 2005/09/26 11:37:23 havasi Exp $
+ *
+ */
+
+#ifndef __LINUX_JFFS2_H__
+#define __LINUX_JFFS2_H__
+
+#define JFFS2_SUPER_MAGIC   0x72b6
+
+/* You must include something which defines the C99 uintXX_t types. 
+   We don't do it from here because this file is used in too many
+   different environments. */
+
+/* Values we may expect to find in the 'magic' field */
+#define JFFS2_OLD_MAGIC_BITMASK 0x1984
+#define JFFS2_MAGIC_BITMASK 0x1985
+#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+#define JFFS2_EMPTY_BITMASK 0xffff
+#define JFFS2_DIRTY_BITMASK 0x0000
+
+/* Summary node MAGIC marker */
+#define JFFS2_SUM_MAGIC        0x02851885
+
+/* We only allow a single char for length, and 0xFF is empty flash so
+   we don't want it confused with a real length. Hence max 254.
+*/
+#define JFFS2_MAX_NAME_LEN 254
+
+/* How small can we sensibly write nodes? */
+#define JFFS2_MIN_DATA_LEN 128
+
+#define JFFS2_COMPR_NONE       0x00
+#define JFFS2_COMPR_ZERO       0x01
+#define JFFS2_COMPR_RTIME      0x02
+#define JFFS2_COMPR_RUBINMIPS  0x03
+#define JFFS2_COMPR_COPY       0x04
+#define JFFS2_COMPR_DYNRUBIN   0x05
+#define JFFS2_COMPR_ZLIB       0x06
+/* Compatibility flags. */
+#define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
+#define JFFS2_NODE_ACCURATE 0x2000
+/* INCOMPAT: Fail to mount the filesystem */
+#define JFFS2_FEATURE_INCOMPAT 0xc000
+/* ROCOMPAT: Mount read-only */
+#define JFFS2_FEATURE_ROCOMPAT 0x8000
+/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
+/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
+
+#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
+
+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER             1       /* for "user." */
+#define JFFS2_XPREFIX_SECURITY         2       /* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS       3       /* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT      4       /* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED          5       /* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION              0x0001
+
+// Maybe later...
+//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+
+#define JFFS2_INO_FLAG_PREREAD   1     /* Do read_inode() for this one at
+                                          mount time, don't wait for it to
+                                          happen later */
+#define JFFS2_INO_FLAG_USERCOMPR  2    /* User has requested a specific
+                                          compression type */
+
+
+/* These can go once we've made sure we've caught all uses without
+   byteswapping */
+
+typedef        uint32_t jint32_t;
+
+typedef uint32_t jmode_t;
+
+typedef uint16_t jint16_t;
+
+struct jffs2_unknown_node
+{
+       /* All start like this */
+       jint16_t magic;
+       jint16_t nodetype;
+       jint32_t totlen; /* So we can skip over nodes we don't grok */
+       jint32_t hdr_crc;
+};
+
+struct jffs2_raw_dirent
+{
+       jint16_t magic;
+       jint16_t nodetype;      /* == JFFS2_NODETYPE_DIRENT */
+       jint32_t totlen;
+       jint32_t hdr_crc;
+       jint32_t pino;
+       jint32_t version;
+       jint32_t ino; /* == zero for unlink */
+       jint32_t mctime;
+       uint8_t nsize;
+       uint8_t type;
+       uint8_t unused[2];
+       jint32_t node_crc;
+       jint32_t name_crc;
+       uint8_t name[0];
+};
+
+/* The JFFS2 raw inode structure: Used for storage on physical media.  */
+/* The uid, gid, atime, mtime and ctime members could be longer, but
+   are left like this for space efficiency. If and when people decide
+   they really need them extended, it's simple enough to add support for
+   a new type of raw node.
+*/
+struct jffs2_raw_inode
+{
+       jint16_t magic;      /* A constant magic number.  */
+       jint16_t nodetype;   /* == JFFS2_NODETYPE_INODE */
+       jint32_t totlen;     /* Total length of this node (inc data, etc.) */
+       jint32_t hdr_crc;
+       jint32_t ino;        /* Inode number.  */
+       jint32_t version;    /* Version number.  */
+       jmode_t mode;       /* The file's type or mode.  */
+       jint16_t uid;        /* The file's owner.  */
+       jint16_t gid;        /* The file's group.  */
+       jint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
+       jint32_t atime;      /* Last access time.  */
+       jint32_t mtime;      /* Last modification time.  */
+       jint32_t ctime;      /* Change time.  */
+       jint32_t offset;     /* Where to begin to write.  */
+       jint32_t csize;      /* (Compressed) data size */
+       jint32_t dsize;      /* Size of the node's data. (after decompression) */
+       uint8_t compr;       /* Compression algorithm used */
+       uint8_t usercompr;   /* Compression algorithm requested by the user */
+       jint16_t flags;      /* See JFFS2_INO_FLAG_* */
+       jint32_t data_crc;   /* CRC for the (compressed) data.  */
+       jint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
+       uint8_t data[0];
+};
+
+struct jffs2_raw_xattr {
+       jint16_t magic;
+       jint16_t nodetype;      /* = JFFS2_NODETYPE_XATTR */
+       jint32_t totlen;
+       jint32_t hdr_crc;
+       jint32_t xid;           /* XATTR identifier number */
+       jint32_t version;
+       uint8_t xprefix;
+       uint8_t name_len;
+       jint16_t value_len;
+       jint32_t data_crc;
+       jint32_t node_crc;
+       uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+       jint16_t magic;
+       jint16_t nodetype;      /* = JFFS2_NODETYPE_XREF */
+       jint32_t totlen;
+       jint32_t hdr_crc;
+       jint32_t ino;           /* inode number */
+       jint32_t xid;           /* XATTR identifier number */
+       jint32_t xseqno;        /* xref sequencial number */
+       jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_summary
+{
+       jint16_t magic;
+       jint16_t nodetype;      /* = JFFS2_NODETYPE_SUMMARY */
+       jint32_t totlen;
+       jint32_t hdr_crc;
+       jint32_t sum_num;       /* number of sum entries*/
+       jint32_t cln_mkr;       /* clean marker size, 0 = no cleanmarker */
+       jint32_t padded;        /* sum of the size of padding nodes */
+       jint32_t sum_crc;       /* summary information crc */
+       jint32_t node_crc;      /* node crc */
+       jint32_t sum[0];        /* inode summary info */
+};
+
+union jffs2_node_union
+{
+       struct jffs2_raw_inode i;
+       struct jffs2_raw_dirent d;
+       struct jffs2_raw_xattr x;
+       struct jffs2_raw_xref r;
+       struct jffs2_raw_summary s;
+       struct jffs2_unknown_node u;
+};
+
+/* Data payload for device nodes. */
+union jffs2_device_node {
+       jint16_t old;
+       jint32_t new;
+};
+
+#endif /* __LINUX_JFFS2_H__ */
diff --git a/package/mtd/src/mtd-api.h b/package/mtd/src/mtd-api.h
new file mode 100644 (file)
index 0000000..6ce6261
--- /dev/null
@@ -0,0 +1,305 @@
+
+/* $Id: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */
+
+#ifndef __MTD_MTD_H__
+#define __MTD_MTD_H__
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/notifier.h>
+#include <linux/module.h>
+#include <linux/uio.h>
+
+#endif /* __KERNEL__ */
+
+struct erase_info_user {
+       u_int32_t start;
+       u_int32_t length;
+};
+
+struct mtd_oob_buf {
+       u_int32_t start;
+       u_int32_t length;
+       unsigned char *ptr;
+};
+
+
+#define MTD_CHAR_MAJOR 90
+#define MTD_BLOCK_MAJOR 31
+#define MAX_MTD_DEVICES 16
+
+
+
+#define MTD_ABSENT             0
+#define MTD_RAM                        1
+#define MTD_ROM                        2
+#define MTD_NORFLASH           3
+#define MTD_NANDFLASH          4
+#define MTD_PEROM              5
+#define MTD_OTHER              14
+#define MTD_UNKNOWN            15
+
+
+
+#define MTD_CLEAR_BITS         1       // Bits can be cleared (flash)
+#define MTD_SET_BITS           2       // Bits can be set
+#define MTD_ERASEABLE          4       // Has an erase function
+#define MTD_WRITEB_WRITEABLE   8       // Direct IO is possible
+#define MTD_VOLATILE           16      // Set for RAMs
+#define MTD_XIP                        32      // eXecute-In-Place possible
+#define MTD_OOB                        64      // Out-of-band data (NAND flash)
+#define MTD_ECC                        128     // Device capable of automatic ECC
+
+// Some common devices / combinations of capabilities
+#define MTD_CAP_ROM            0
+#define MTD_CAP_RAM            (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
+#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
+#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
+#define MTD_WRITEABLE          (MTD_CLEAR_BITS|MTD_SET_BITS)
+
+
+// Types of automatic ECC/Checksum available
+#define MTD_ECC_NONE           0       // No automatic ECC available
+#define MTD_ECC_RS_DiskOnChip  1       // Automatic ECC on DiskOnChip
+#define MTD_ECC_SW             2       // SW ECC for Toshiba & Samsung devices
+
+struct mtd_info_user {
+       u_char type;
+       u_int32_t flags;
+       u_int32_t size;  // Total size of the MTD
+       u_int32_t erasesize;
+       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
+       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+       u_int32_t ecctype;
+       u_int32_t eccsize;
+};
+
+struct region_info_user {
+       u_int32_t offset;               /* At which this region starts, 
+                                        * from the beginning of the MTD */
+       u_int32_t erasesize;            /* For this region */
+       u_int32_t numblocks;            /* Number of blocks in this region */
+       u_int32_t regionindex;
+};
+
+#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
+#define MEMERASE                _IOW('M', 2, struct erase_info_user)
+#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
+#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
+#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
+#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
+#define MEMGETREGIONCOUNT      _IOR('M', 7, int)
+#define MEMGETREGIONINFO       _IOWR('M', 8, struct region_info_user)
+#define        MEMREADDATA             _IOWR('M', 9, struct mtd_oob_buf)
+#define        MEMWRITEDATA            _IOWR('M', 10, struct mtd_oob_buf)
+#define MTDREFRESH                             _IO('M', 23)
+
+#ifndef __KERNEL__
+
+typedef struct mtd_info_user mtd_info_t;
+typedef struct erase_info_user erase_info_t;
+typedef struct region_info_user region_info_t;
+
+       /* User-space ioctl definitions */
+
+
+#else /* __KERNEL__ */
+
+
+#define MTD_ERASE_PENDING              0x01
+#define MTD_ERASING            0x02
+#define MTD_ERASE_SUSPEND      0x04
+#define MTD_ERASE_DONE          0x08
+#define MTD_ERASE_FAILED        0x10
+
+struct erase_info {
+       struct mtd_info *mtd;
+       u_int32_t addr;
+       u_int32_t len;
+       u_long time;
+       u_long retries;
+       u_int dev;
+       u_int cell;
+       void (*callback) (struct erase_info *self);
+       u_long priv;
+       u_char state;
+       struct erase_info *next;
+};
+
+struct mtd_erase_region_info {
+       u_int32_t offset;                       /* At which this region starts, from the beginning of the MTD */
+       u_int32_t erasesize;            /* For this region */
+       u_int32_t numblocks;            /* Number of blocks of erasesize in this region */
+};
+
+struct mtd_info {
+       u_char type;
+       u_int32_t flags;
+       u_int32_t size;  // Total size of the MTD
+
+       /* "Major" erase size for the device. Naïve users may take this
+        * to be the only erase size available, or may use the more detailed
+        * information below if they desire
+        */
+       u_int32_t erasesize;
+
+       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
+       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+       u_int32_t ecctype;
+       u_int32_t eccsize;
+
+       // Kernel-only stuff starts here.
+       char *name;
+       int index;
+
+       /* Data for variable erase regions. If numeraseregions is zero,
+        * it means that the whole device has erasesize as given above. 
+        */
+       int numeraseregions;
+       struct mtd_erase_region_info *eraseregions; 
+
+       /* This really shouldn't be here. It can go away in 2.5 */
+       u_int32_t bank_size;
+
+       struct module *module;
+       int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
+
+       /* This stuff for eXecute-In-Place */
+       int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
+
+       /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
+       void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
+
+
+       int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+       int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
+       int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+       int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
+
+       int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+       int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
+
+       /* 
+        * Methods to access the protection register area, present in some 
+        * flash devices. The user data is one time programmable but the
+        * factory data is read only. 
+        */
+       int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+
+       int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+
+       /* This function is not yet implemented */
+       int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
+
+       /* iovec-based read/write methods. We need these especially for NAND flash,
+          with its limited number of write cycles per erase.
+          NB: The 'count' parameter is the number of _vectors_, each of 
+          which contains an (ofs, len) tuple.
+       */
+       int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
+       int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, 
+               size_t *retlen, u_char *eccbuf, int oobsel);
+       int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
+       int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, 
+               size_t *retlen, u_char *eccbuf, int oobsel);
+
+       /* Sync */
+       void (*sync) (struct mtd_info *mtd);
+
+       /* Chip-supported device locking */
+       int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+       int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
+
+       /* Power Management functions */
+       int (*suspend) (struct mtd_info *mtd);
+       void (*resume) (struct mtd_info *mtd);
+
+       struct notifier_block reboot_notifier;
+
+       void *priv;
+};
+
+
+       /* Kernel-side ioctl definitions */
+
+extern int add_mtd_device(struct mtd_info *mtd);
+extern int del_mtd_device (struct mtd_info *mtd);
+
+extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num);
+
+static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
+{
+       struct mtd_info *ret;
+       
+       ret = __get_mtd_device(mtd, num);
+
+       if (ret && ret->module && !try_inc_mod_count(ret->module))
+               return NULL;
+
+       return ret;
+}
+
+static inline void put_mtd_device(struct mtd_info *mtd)
+{
+       if (mtd->module)
+              __MOD_DEC_USE_COUNT(mtd->module);
+}
+
+
+struct mtd_notifier {
+       void (*add)(struct mtd_info *mtd);
+       void (*remove)(struct mtd_info *mtd);
+       struct mtd_notifier *next;
+};
+
+
+extern void register_mtd_user (struct mtd_notifier *new);
+extern int unregister_mtd_user (struct mtd_notifier *old);
+
+int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
+                      unsigned long count, loff_t to, size_t *retlen);
+
+int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+                     unsigned long count, loff_t from, size_t *retlen);
+
+#ifndef MTDC
+#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
+#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
+#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
+#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args)
+#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args)
+#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args)
+#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args)
+#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args)
+#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args)
+#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
+#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
+#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd);  } while (0) 
+#endif /* MTDC */
+
+/*
+ * Debugging macro and defines
+ */
+#define MTD_DEBUG_LEVEL0       (0)     /* Quiet   */
+#define MTD_DEBUG_LEVEL1       (1)     /* Audible */
+#define MTD_DEBUG_LEVEL2       (2)     /* Loud    */
+#define MTD_DEBUG_LEVEL3       (3)     /* Noisy   */
+
+#ifdef CONFIG_MTD_DEBUG
+#define DEBUG(n, args...)                              \
+       do {                                            \
+               if (n <= CONFIG_MTD_DEBUG_VERBOSE)      \
+                       printk(KERN_INFO args);         \
+       } while(0)
+#else /* CONFIG_MTD_DEBUG */
+#define DEBUG(n, args...)
+#endif /* CONFIG_MTD_DEBUG */
+
+#endif /* __KERNEL__ */
+
+#endif /* __MTD_MTD_H__ */
index 85b069f..92018c2 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/syscall.h>
 #include <fcntl.h>
@@ -43,7 +44,7 @@
 #include <sys/reboot.h>
 #include <linux/reboot.h>
 
-#include "mtd.h"
+#include "mtd-api.h"
 
 #define TRX_MAGIC       0x30524448      /* "HDR0" */
 #define BUFSIZE (16 * 1024)
@@ -51,6 +52,8 @@
 
 #define DEBUG
 
+#define JFFS2_DEFAULT_DIR      "tmp"
+
 #define SYSTYPE_UNKNOWN     0
 #define SYSTYPE_BROADCOM    1
 /* to be continued */
@@ -63,16 +66,86 @@ struct trx_header {
        uint32_t offsets[3];    /* Offsets of partitions from start of header */
 };
 
-char buf[BUFSIZE];
-int buflen;
+static char buf[BUFSIZE];
+static char *imagefile;
+static int buflen;
 int quiet;
+int mtdsize = 0;
+int erasesize = 0;
+
+int mtd_open(const char *mtd)
+{
+       FILE *fp;
+       char dev[PATH_MAX];
+       int i;
+       int ret;
+       int flags = O_RDWR | O_SYNC;
+
+       if ((fp = fopen("/proc/mtd", "r"))) {
+               while (fgets(dev, sizeof(dev), fp)) {
+                       if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
+                               snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
+                               if ((ret=open(dev, flags))<0) {
+                                       snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
+                                       ret=open(dev, flags);
+                               }
+                               fclose(fp);
+                               return ret;
+                       }
+               }
+               fclose(fp);
+       }
+
+       return open(mtd, flags);
+}
+
+int mtd_check_open(const char *mtd)
+{
+       struct mtd_info_user mtdInfo;
+       int fd;
+
+       fd = mtd_open(mtd);
+       if(fd < 0) {
+               fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+               return 0;
+       }
+
+       if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
+               fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
+               close(fd);
+               return 0;
+       }
+       mtdsize = mtdInfo.size;
+       erasesize = mtdInfo.erasesize;
+
+       return fd;
+}
+
+int mtd_erase_block(int fd, int offset)
+{
+       struct erase_info_user mtdEraseInfo;
+
+       mtdEraseInfo.start = offset;
+       mtdEraseInfo.length = erasesize;
+       ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
+       if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) {
+               fprintf(stderr, "Erasing mtd failed.\n");
+               exit(1);
+       }
+}
+
+int mtd_write_buffer(int fd, char *buf, int offset, int length)
+{
+       lseek(fd, offset, SEEK_SET);
+       write(fd, buf, length);
+}
+
 
 #ifdef target_brcm
-int
+static int
 image_check_brcm(int imagefd, const char *mtd)
 {
        struct trx_header *trx = (struct trx_header *) buf;
-       struct mtd_info_user mtdInfo;
        int fd;
 
        if (strcmp(mtd, "linux") != 0)
@@ -94,18 +167,13 @@ image_check_brcm(int imagefd, const char *mtd)
        }
 
        /* check if image fits to mtd device */
-       fd = mtd_open(mtd, O_RDWR | O_SYNC);
+       fd = mtd_check_open(mtd);
        if(fd < 0) {
                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
                exit(1);
        }
 
-       if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-               fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-               exit(1);
-       }
-               
-       if(mtdInfo.size < trx->len) {
+       if(mtdsize < trx->len) {
                fprintf(stderr, "Image too big for partition: %s\n", mtd);
                close(fd);
                return 0;
@@ -116,7 +184,7 @@ image_check_brcm(int imagefd, const char *mtd)
 }
 #endif /* target_brcm */
 
-int
+static int
 image_check(int imagefd, const char *mtd)
 {
        int fd, systype;
@@ -129,48 +197,35 @@ image_check(int imagefd, const char *mtd)
 #endif
 }
 
-int mtd_check(char *mtd)
+static int mtd_check(const char *mtd)
 {
-       struct mtd_info_user mtdInfo;
        int fd;
 
-       fd = mtd_open(mtd, O_RDWR | O_SYNC);
-       if(fd < 0) {
-               fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+       fd = mtd_check_open(mtd);
+       if (!fd)
                return 0;
-       }
-
-       if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-               fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-               close(fd);
-               return 0;
-       }
 
        close(fd);
        return 1;
 }
 
-int
+static int
 mtd_unlock(const char *mtd)
 {
        int fd;
-       struct mtd_info_user mtdInfo;
        struct erase_info_user mtdLockInfo;
 
-       fd = mtd_open(mtd, O_RDWR | O_SYNC);
-       if(fd < 0) {
+       fd = mtd_check_open(mtd);
+       if(fd <= 0) {
                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
                exit(1);
        }
 
-       if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-               fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-               close(fd);
-               exit(1);
-       }
+       if (quiet < 2) 
+               fprintf(stderr, "Unlocking %s ...\n", mtd);
 
        mtdLockInfo.start = 0;
-       mtdLockInfo.length = mtdInfo.size;
+       mtdLockInfo.length = mtdsize;
        if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
                close(fd);
                return 0;
@@ -180,56 +235,26 @@ mtd_unlock(const char *mtd)
        return 0;
 }
 
-int
-mtd_open(const char *mtd, int flags)
-{
-       FILE *fp;
-       char dev[PATH_MAX];
-       int i;
-       int ret;
-
-       if ((fp = fopen("/proc/mtd", "r"))) {
-               while (fgets(dev, sizeof(dev), fp)) {
-                       if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
-                               snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
-                               if ((ret=open(dev, flags))<0) {
-                                       snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
-                                       ret=open(dev, flags);
-                               }
-                               fclose(fp);
-                               return ret;
-                       }
-               }
-               fclose(fp);
-       }
-
-       return open(mtd, flags);
-}
-
-int
+static int
 mtd_erase(const char *mtd)
 {
        int fd;
-       struct mtd_info_user mtdInfo;
        struct erase_info_user mtdEraseInfo;
 
-       fd = mtd_open(mtd, O_RDWR | O_SYNC);
-       if(fd < 0) {
-               fprintf(stderr, "Could not open mtd device: %s\n", mtd);
-               exit(1);
-       }
+       if (quiet < 2)
+               fprintf(stderr, "Erasing %s ...\n", mtd);
 
-       if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-               fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-               close(fd);
+       fd = mtd_check_open(mtd);
+       if(fd <= 0) {
+               fprintf(stderr, "Could not open mtd device: %s\n", mtd);
                exit(1);
        }
 
-       mtdEraseInfo.length = mtdInfo.erasesize;
+       mtdEraseInfo.length = erasesize;
 
        for (mtdEraseInfo.start = 0;
-                mtdEraseInfo.start < mtdInfo.size;
-                mtdEraseInfo.start += mtdInfo.erasesize) {
+                mtdEraseInfo.start < mtdsize;
+                mtdEraseInfo.start += erasesize) {
                
                ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
                if(ioctl(fd, MEMERASE, &mtdEraseInfo))
@@ -241,46 +266,50 @@ mtd_erase(const char *mtd)
 
 }
 
-int
+static int
 mtd_refresh(const char *mtd)
 {
        int fd;
 
-       fd = mtd_open(mtd, O_RDWR | O_SYNC);
-       if(fd < 0) {
+       if (quiet < 2)
+               fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);
+
+       fd = mtd_check_open(mtd);
+       if(fd <= 0) {
                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
                exit(1);
        }
+
        if (ioctl(fd, MTDREFRESH, NULL)) {
                fprintf(stderr, "Failed to refresh the MTD device\n");
                close(fd);
                exit(1);
        }
        close(fd);
+
+       if (quiet < 2)
+               fprintf(stderr, "\n");
+
        return 0;
 }
 
-int
+static int
 mtd_write(int imagefd, const char *mtd)
 {
        int fd, i, result;
        size_t r, w, e;
-       struct mtd_info_user mtdInfo;
        struct erase_info_user mtdEraseInfo;
        int ret = 0;
 
-       fd = mtd_open(mtd, O_RDWR | O_SYNC);
+       fd = mtd_check_open(mtd);
        if(fd < 0) {
                fprintf(stderr, "Could not open mtd device: %s\n", mtd);
                exit(1);
        }
-
-       if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
-               fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
-               close(fd);
-               exit(1);
-       }
                
+       if (quiet < 2)
+               fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);
+
        r = w = e = 0;
        if (!quiet)
                fprintf(stderr, " [ ]");
@@ -296,17 +325,13 @@ mtd_write(int imagefd, const char *mtd)
 
                /* need to erase the next block before writing data to it */
                while (w > e) {
-                       mtdEraseInfo.start = e;
-                       mtdEraseInfo.length = mtdInfo.erasesize;
-
                        if (!quiet)
                                fprintf(stderr, "\b\b\b[e]");
+
+                       mtd_erase_block(fd, e);
+
                        /* erase the chunk */
-                       if (ioctl (fd,MEMERASE,&mtdEraseInfo) < 0) {
-                               fprintf(stderr, "Erasing mtd failed: %s\n", mtd);
-                               exit(1);
-                       }
-                       e += mtdInfo.erasesize;
+                       e += erasesize;
                }
                
                if (!quiet)
@@ -327,11 +352,14 @@ mtd_write(int imagefd, const char *mtd)
        if (!quiet)
                fprintf(stderr, "\b\b\b\b");
 
+       if (quiet < 2)
+               fprintf(stderr, "\n");
+
        close(fd);
        return 0;
 }
 
-void usage(void)
+static void usage(void)
 {
        fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
        "The device is in the format of mtdX (eg: mtd4) or its label.\n"
@@ -340,26 +368,44 @@ void usage(void)
        "        refresh                 refresh mtd partition\n"
        "        erase                   erase all data on device\n"
        "        write <imagefile>|-     write <imagefile> (use - for stdin) to device\n"
+       "        jffs2write <file>       append <file> to the jffs2 partition on the device\n"
        "Following options are available:\n"
        "        -q                      quiet mode (once: no [w] on writing,\n"
        "                                           twice: no status messages)\n"
        "        -r                      reboot after successful command\n"
        "        -f                      force write without trx checks\n"
-       "        -e <device>             erase <device> before executing the command\n\n"
+       "        -e <device>             erase <device> before executing the command\n"
+       "        -d <name>               directory for jffs2write, defaults to \"tmp\"\n"
+       "\n"
        "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
        "         mtd -r write linux.trx linux\n\n");
        exit(1);
 }
 
+static void do_reboot(void)
+{
+       fprintf(stderr, "Rebooting ...\n");
+       fflush(stderr);
+
+       /* try regular reboot method first */
+       system("/sbin/reboot");
+       sleep(2);
+
+       /* if we're still alive at this point, force the kernel to reboot */
+       syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
+}
+
 int main (int argc, char **argv)
 {
        int ch, i, boot, unlock, imagefd, force, unlocked;
-       char *erase[MAX_ARGS], *device, *imagefile;
+       char *erase[MAX_ARGS], *device;
+       char *jffs2dir = JFFS2_DEFAULT_DIR;
        enum {
                CMD_ERASE,
                CMD_WRITE,
                CMD_UNLOCK,
-               CMD_REFRESH
+               CMD_REFRESH,
+               CMD_JFFS2WRITE
        } cmd;
        
        erase[0] = NULL;
@@ -368,7 +414,7 @@ int main (int argc, char **argv)
        buflen = 0;
        quiet = 0;
 
-       while ((ch = getopt(argc, argv, "frqe:")) != -1)
+       while ((ch = getopt(argc, argv, "frqe:d:")) != -1)
                switch (ch) {
                        case 'f':
                                force = 1;
@@ -387,7 +433,9 @@ int main (int argc, char **argv)
                                erase[i++] = optarg;
                                erase[i] = NULL;
                                break;
-                       
+                       case 'd':
+                               jffs2dir = optarg;
+                               break;
                        case '?':
                        default:
                                usage();
@@ -434,6 +482,15 @@ int main (int argc, char **argv)
                                exit(1);
                        }
                }
+       } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
+               cmd = CMD_JFFS2WRITE;
+               device = argv[2];
+       
+               imagefile = argv[1];
+               if (!mtd_check(device)) {
+                       fprintf(stderr, "Can't open device for writing!\n");
+                       exit(1);
+               }
        } else {
                usage();
        }
@@ -443,53 +500,43 @@ int main (int argc, char **argv)
        i = 0;
        unlocked = 0;
        while (erase[i] != NULL) {
-               if (quiet < 2)
-                       fprintf(stderr, "Unlocking %s ...\n", erase[i]);
                mtd_unlock(erase[i]);
-               if (quiet < 2)
-                       fprintf(stderr, "Erasing %s ...\n", erase[i]);
                mtd_erase(erase[i]);
                if (strcmp(erase[i], device) == 0)
                        unlocked = 1;
                i++;
        }
        
-       if (!unlocked) {
-               if (quiet < 2) 
-                       fprintf(stderr, "Unlocking %s ...\n", device);
-               mtd_unlock(device);
-       }
                
        switch (cmd) {
                case CMD_UNLOCK:
+                       if (!unlocked)
+                               mtd_unlock(device);
                        break;
                case CMD_ERASE:
-                       if (quiet < 2)
-                               fprintf(stderr, "Erasing %s ...\n", device);
+                       if (!unlocked)
+                               mtd_unlock(device);
                        mtd_erase(device);
                        break;
                case CMD_WRITE:
-                       if (quiet < 2)
-                               fprintf(stderr, "Writing from %s to %s ... ", imagefile, device);
+                       if (!unlocked)
+                               mtd_unlock(device);
                        mtd_write(imagefd, device);
-                       if (quiet < 2)
-                               fprintf(stderr, "\n");
+                       break;
+               case CMD_JFFS2WRITE:
+                       if (!unlocked)
+                               mtd_unlock(device);
+                       mtd_write_jffs2(device, imagefile, jffs2dir);
                        break;
                case CMD_REFRESH:
-                       if (quiet < 2)
-                               fprintf(stderr, "Refreshing mtd partition %s ... ");
                        mtd_refresh(device);
-                       if (quiet < 2)
-                               fprintf(stderr, "\n");
                        break;
        }
 
        sync();
        
-       if (boot) {
-               fprintf(stderr, "Rebooting ...\n");
-               fflush(stderr);
-               syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
-       }
+       if (boot)
+               do_reboot();
+
        return 0;
 }
diff --git a/package/mtd/src/mtd.h b/package/mtd/src/mtd.h
deleted file mode 100644 (file)
index 6ce6261..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-
-/* $Id: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */
-
-#ifndef __MTD_MTD_H__
-#define __MTD_MTD_H__
-
-#ifdef __KERNEL__
-
-#include <linux/config.h>
-#include <linux/version.h>
-#include <linux/types.h>
-#include <linux/mtd/compatmac.h>
-#include <linux/notifier.h>
-#include <linux/module.h>
-#include <linux/uio.h>
-
-#endif /* __KERNEL__ */
-
-struct erase_info_user {
-       u_int32_t start;
-       u_int32_t length;
-};
-
-struct mtd_oob_buf {
-       u_int32_t start;
-       u_int32_t length;
-       unsigned char *ptr;
-};
-
-
-#define MTD_CHAR_MAJOR 90
-#define MTD_BLOCK_MAJOR 31
-#define MAX_MTD_DEVICES 16
-
-
-
-#define MTD_ABSENT             0
-#define MTD_RAM                        1
-#define MTD_ROM                        2
-#define MTD_NORFLASH           3
-#define MTD_NANDFLASH          4
-#define MTD_PEROM              5
-#define MTD_OTHER              14
-#define MTD_UNKNOWN            15
-
-
-
-#define MTD_CLEAR_BITS         1       // Bits can be cleared (flash)
-#define MTD_SET_BITS           2       // Bits can be set
-#define MTD_ERASEABLE          4       // Has an erase function
-#define MTD_WRITEB_WRITEABLE   8       // Direct IO is possible
-#define MTD_VOLATILE           16      // Set for RAMs
-#define MTD_XIP                        32      // eXecute-In-Place possible
-#define MTD_OOB                        64      // Out-of-band data (NAND flash)
-#define MTD_ECC                        128     // Device capable of automatic ECC
-
-// Some common devices / combinations of capabilities
-#define MTD_CAP_ROM            0
-#define MTD_CAP_RAM            (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
-#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
-#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
-#define MTD_WRITEABLE          (MTD_CLEAR_BITS|MTD_SET_BITS)
-
-
-// Types of automatic ECC/Checksum available
-#define MTD_ECC_NONE           0       // No automatic ECC available
-#define MTD_ECC_RS_DiskOnChip  1       // Automatic ECC on DiskOnChip
-#define MTD_ECC_SW             2       // SW ECC for Toshiba & Samsung devices
-
-struct mtd_info_user {
-       u_char type;
-       u_int32_t flags;
-       u_int32_t size;  // Total size of the MTD
-       u_int32_t erasesize;
-       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
-       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-       u_int32_t ecctype;
-       u_int32_t eccsize;
-};
-
-struct region_info_user {
-       u_int32_t offset;               /* At which this region starts, 
-                                        * from the beginning of the MTD */
-       u_int32_t erasesize;            /* For this region */
-       u_int32_t numblocks;            /* Number of blocks in this region */
-       u_int32_t regionindex;
-};
-
-#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
-#define MEMERASE                _IOW('M', 2, struct erase_info_user)
-#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
-#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
-#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
-#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
-#define MEMGETREGIONCOUNT      _IOR('M', 7, int)
-#define MEMGETREGIONINFO       _IOWR('M', 8, struct region_info_user)
-#define        MEMREADDATA             _IOWR('M', 9, struct mtd_oob_buf)
-#define        MEMWRITEDATA            _IOWR('M', 10, struct mtd_oob_buf)
-#define MTDREFRESH                             _IO('M', 23)
-
-#ifndef __KERNEL__
-
-typedef struct mtd_info_user mtd_info_t;
-typedef struct erase_info_user erase_info_t;
-typedef struct region_info_user region_info_t;
-
-       /* User-space ioctl definitions */
-
-
-#else /* __KERNEL__ */
-
-
-#define MTD_ERASE_PENDING              0x01
-#define MTD_ERASING            0x02
-#define MTD_ERASE_SUSPEND      0x04
-#define MTD_ERASE_DONE          0x08
-#define MTD_ERASE_FAILED        0x10
-
-struct erase_info {
-       struct mtd_info *mtd;
-       u_int32_t addr;
-       u_int32_t len;
-       u_long time;
-       u_long retries;
-       u_int dev;
-       u_int cell;
-       void (*callback) (struct erase_info *self);
-       u_long priv;
-       u_char state;
-       struct erase_info *next;
-};
-
-struct mtd_erase_region_info {
-       u_int32_t offset;                       /* At which this region starts, from the beginning of the MTD */
-       u_int32_t erasesize;            /* For this region */
-       u_int32_t numblocks;            /* Number of blocks of erasesize in this region */
-};
-
-struct mtd_info {
-       u_char type;
-       u_int32_t flags;
-       u_int32_t size;  // Total size of the MTD
-
-       /* "Major" erase size for the device. Naïve users may take this
-        * to be the only erase size available, or may use the more detailed
-        * information below if they desire
-        */
-       u_int32_t erasesize;
-
-       u_int32_t oobblock;  // Size of OOB blocks (e.g. 512)
-       u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
-       u_int32_t ecctype;
-       u_int32_t eccsize;
-
-       // Kernel-only stuff starts here.
-       char *name;
-       int index;
-
-       /* Data for variable erase regions. If numeraseregions is zero,
-        * it means that the whole device has erasesize as given above. 
-        */
-       int numeraseregions;
-       struct mtd_erase_region_info *eraseregions; 
-
-       /* This really shouldn't be here. It can go away in 2.5 */
-       u_int32_t bank_size;
-
-       struct module *module;
-       int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
-
-       /* This stuff for eXecute-In-Place */
-       int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
-
-       /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
-       void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
-
-
-       int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-       int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
-
-       int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
-       int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
-
-       int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-       int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
-
-       /* 
-        * Methods to access the protection register area, present in some 
-        * flash devices. The user data is one time programmable but the
-        * factory data is read only. 
-        */
-       int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-
-       int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-
-       /* This function is not yet implemented */
-       int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-
-       /* iovec-based read/write methods. We need these especially for NAND flash,
-          with its limited number of write cycles per erase.
-          NB: The 'count' parameter is the number of _vectors_, each of 
-          which contains an (ofs, len) tuple.
-       */
-       int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
-       int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, 
-               size_t *retlen, u_char *eccbuf, int oobsel);
-       int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen);
-       int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, 
-               size_t *retlen, u_char *eccbuf, int oobsel);
-
-       /* Sync */
-       void (*sync) (struct mtd_info *mtd);
-
-       /* Chip-supported device locking */
-       int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
-       int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
-
-       /* Power Management functions */
-       int (*suspend) (struct mtd_info *mtd);
-       void (*resume) (struct mtd_info *mtd);
-
-       struct notifier_block reboot_notifier;
-
-       void *priv;
-};
-
-
-       /* Kernel-side ioctl definitions */
-
-extern int add_mtd_device(struct mtd_info *mtd);
-extern int del_mtd_device (struct mtd_info *mtd);
-
-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num);
-
-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
-{
-       struct mtd_info *ret;
-       
-       ret = __get_mtd_device(mtd, num);
-
-       if (ret && ret->module && !try_inc_mod_count(ret->module))
-               return NULL;
-
-       return ret;
-}
-
-static inline void put_mtd_device(struct mtd_info *mtd)
-{
-       if (mtd->module)
-              __MOD_DEC_USE_COUNT(mtd->module);
-}
-
-
-struct mtd_notifier {
-       void (*add)(struct mtd_info *mtd);
-       void (*remove)(struct mtd_info *mtd);
-       struct mtd_notifier *next;
-};
-
-
-extern void register_mtd_user (struct mtd_notifier *new);
-extern int unregister_mtd_user (struct mtd_notifier *old);
-
-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
-                      unsigned long count, loff_t to, size_t *retlen);
-
-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
-                     unsigned long count, loff_t from, size_t *retlen);
-
-#ifndef MTDC
-#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
-#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
-#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
-#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args)
-#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args)
-#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args)
-#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args)
-#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args)
-#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args)
-#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
-#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
-#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd);  } while (0) 
-#endif /* MTDC */
-
-/*
- * Debugging macro and defines
- */
-#define MTD_DEBUG_LEVEL0       (0)     /* Quiet   */
-#define MTD_DEBUG_LEVEL1       (1)     /* Audible */
-#define MTD_DEBUG_LEVEL2       (2)     /* Loud    */
-#define MTD_DEBUG_LEVEL3       (3)     /* Noisy   */
-
-#ifdef CONFIG_MTD_DEBUG
-#define DEBUG(n, args...)                              \
-       do {                                            \
-               if (n <= CONFIG_MTD_DEBUG_VERBOSE)      \
-                       printk(KERN_INFO args);         \
-       } while(0)
-#else /* CONFIG_MTD_DEBUG */
-#define DEBUG(n, args...)
-#endif /* CONFIG_MTD_DEBUG */
-
-#endif /* __KERNEL__ */
-
-#endif /* __MTD_MTD_H__ */